# CBC Padding Oracle via Distinct Error Messages

Language: Python
Severity: High
CWE: CWE-203

## Source
13

## Flow
6-7-8-9-10-11-13-14

## Sink
14-16

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

def retrieve_iot_sensor_config(encrypted_payload):
    master_key = b'IoTDevice2024Key'
    try:
        cipher_data = base64.b64decode(encrypted_payload)
        iv, ciphertext = cipher_data[:16], cipher_data[16:]
        cipher = AES.new(master_key, AES.MODE_CBC, iv)
        plaintext = unpad(cipher.decrypt(ciphertext), AES.block_size)
        return {"status": "success", "config": plaintext.decode()}
    except ValueError:
        return {"status": "error", "message": "Invalid padding detected in sensor configuration"}
    except Exception as e:
        return {"status": "error", "message": "Decryption failed - corrupted data"}
```

## Explanation

The function returns distinct error messages for padding failures (ValueError) versus general decryption failures (generic Exception), creating a padding oracle vulnerability. An attacker can submit modified ciphertexts and use the different error responses to decrypt data byte-by-byte without knowing the encryption key. The observable discrepancy between 'Invalid padding detected in sensor configuration' and 'Decryption failed - corrupted data' leaks information about internal decryption state, enabling systematic ciphertext manipulation to recover plaintext.

## Remediation

The fix unifies all error handling into a single generic exception handler that returns an identical error message regardless of whether the failure was due to invalid padding, base64 decoding issues, or any other decryption error. This eliminates the padding oracle by ensuring an attacker cannot distinguish between padding failures and other types of errors. Additionally, input length validation is performed before decryption to reject obviously malformed inputs early. Defining the generic error response once also ensures future code changes cannot accidentally introduce response divergence.

## Secure Code
```python
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64
import hmac
import hashlib
import os

def retrieve_iot_sensor_config(encrypted_payload):
    master_key = b'IoTDevice2024Key'
    generic_error = {"status": "error", "message": "Failed to retrieve sensor configuration"}
    try:
        cipher_data = base64.b64decode(encrypted_payload)
        if len(cipher_data) < 32 or len(cipher_data) % 16 != 0:
            return generic_error
        iv, ciphertext = cipher_data[:16], cipher_data[16:]
        cipher = AES.new(master_key, AES.MODE_CBC, iv)
        plaintext = unpad(cipher.decrypt(ciphertext), AES.block_size)
        return {"status": "success", "config": plaintext.decode()}
    except Exception:
        return generic_error
```
