# CBC Bit-Flipping via Unauthenticated AES-CBC Encryption

Language: Python
Severity: Critical
CWE: CWE-353

## Source
9

## Flow
9-10-16-17-18

## Sink
17

## Vulnerable Code
```python
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64

def generate_iot_device_token(device_id, access_level):
    key = b'IoTMaster2024Key'
    iv = b'StaticIV12345678'
    payload = f"device={device_id}&role={access_level}"
    cipher = AES.new(key, AES.MODE_CBC, iv)
    encrypted = cipher.encrypt(pad(payload.encode(), AES.block_size))
    return base64.b64encode(encrypted).decode()

def validate_iot_token(token):
    key = b'IoTMaster2024Key'
    iv = b'StaticIV12345678'
    cipher = AES.new(key, AES.MODE_CBC, iv)
    decrypted = unpad(cipher.decrypt(base64.b64decode(token)), AES.block_size)
    params = dict(x.split('=') for x in decrypted.decode().split('&'))
    return params['role'] == 'admin'
```

## Explanation

The code uses AES-CBC encryption without message authentication (HMAC/MAC), allowing attackers to perform bit-flipping attacks on the ciphertext. By flipping specific bits in the encrypted token, an attacker can modify the decrypted 'role' parameter from 'guest' to 'admin' without knowing the encryption key, bypassing access controls. The use of a static IV further worsens the attack surface by making the encryption deterministic and allowing precise targeting of the first plaintext block via direct IV manipulation.

## Remediation

The fix adds HMAC-SHA256 authentication over the ciphertext (Encrypt-then-MAC) to prevent bit-flipping attacks. Before decryption, the token's HMAC is verified using a constant-time comparison via hmac.compare_digest, ensuring any tampered ciphertext is rejected immediately. Additionally, a random IV is generated per token instead of using a static IV, preventing deterministic encryption patterns. Encryption and HMAC keys are separated to follow proper cryptographic key separation principles.

## Secure Code
```python
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes
import base64
import hmac
import hashlib

ENCRYPTION_KEY = b'IoTMaster2024Key'
HMAC_KEY = b'IoTHmacSecret_32'

def generate_iot_device_token(device_id, access_level):
    iv = get_random_bytes(AES.block_size)
    payload = f"device={device_id}&role={access_level}"
    cipher = AES.new(ENCRYPTION_KEY, AES.MODE_CBC, iv)
    encrypted = cipher.encrypt(pad(payload.encode(), AES.block_size))
    token_data = iv + encrypted
    mac = hmac.new(HMAC_KEY, token_data, hashlib.sha256).digest()
    return base64.b64encode(mac + token_data).decode()

def validate_iot_token(token):
    raw = base64.b64decode(token)
    mac_received = raw[:32]
    token_data = raw[32:]
    mac_computed = hmac.new(HMAC_KEY, token_data, hashlib.sha256).digest()
    if not hmac.compare_digest(mac_received, mac_computed):
        raise ValueError("Token integrity check failed: invalid HMAC")
    iv = token_data[:AES.block_size]
    encrypted = token_data[AES.block_size:]
    cipher = AES.new(ENCRYPTION_KEY, AES.MODE_CBC, iv)
    decrypted = unpad(cipher.decrypt(encrypted), AES.block_size)
    params = dict(x.split('=') for x in decrypted.decode().split('&'))
    return params['role'] == 'admin'
```
