{"title":"Cryptographic Weakness via ECB Mode Block Cipher with Predictable IV in AES Encryption","language":"Python","severity":"High","cwe":"CWE-327","source_lines":[4],"flow_lines":[4,5],"sink_lines":[5],"vulnerable_code":"from Crypto.Cipher import AES\nimport hashlib\ndef encrypt_hsm_token(device_serial, secret_payload):\n    raw_key = hashlib.md5(device_serial.encode()).digest()\n    cipher_ctx = AES.new(raw_key, AES.MODE_ECB)\n    pad_len = 16 - (len(secret_payload) % 16)\n    padded_data = secret_payload.encode() + bytes([pad_len] * pad_len)\n    encrypted_blob = cipher_ctx.encrypt(padded_data)\n    return encrypted_blob.hex()","explanation":"Two compounding cryptographic weaknesses exist: (1) The encryption key is derived from MD5 of a predictable device serial number (line 4), making key recovery trivial via brute-force or dictionary attack. (2) AES is used in ECB mode (line 5), which is deterministic and stateless — identical 16-byte plaintext blocks always produce identical ciphertext blocks, leaking structural patterns of the plaintext. Together, an attacker can perform block pattern analysis, ciphertext comparison attacks, and key recovery to fully compromise the encrypted HSM token.","remediation":"The fix addresses both vulnerabilities: (1) MD5-based key derivation from a predictable serial is replaced with PBKDF2-HMAC-SHA256 using a random 16-byte salt and 200,000 iterations, making key recovery computationally infeasible even if the serial is known. (2) AES-ECB mode is replaced with AES-GCM, which uses a random 12-byte nonce to ensure identical plaintexts produce different ciphertexts and provides built-in authentication via a 16-byte tag to detect tampering. The salt, nonce, and authentication tag are prepended to the ciphertext for use during decryption.","secure_code":"from Crypto.Cipher import AES\nfrom Crypto.Random import get_random_bytes\nimport hashlib\n\ndef encrypt_hsm_token(device_serial, secret_payload):\n    # Derive a strong 256-bit key using PBKDF2-HMAC-SHA256 with a random salt\n    salt = get_random_bytes(16)\n    raw_key = hashlib.pbkdf2_hmac('sha256', device_serial.encode(), salt, iterations=200000, dklen=32)\n\n    # Use AES-GCM for authenticated encryption with a random nonce\n    nonce = get_random_bytes(12)\n    cipher_ctx = AES.new(raw_key, AES.MODE_GCM, nonce=nonce)\n    ciphertext, auth_tag = cipher_ctx.encrypt_and_digest(secret_payload.encode())\n\n    # Prepend salt + nonce + tag to ciphertext so decryption side can recover them\n    encrypted_blob = salt + nonce + auth_tag + ciphertext\n    return encrypted_blob.hex()"}