How CBC works
Encryption
Decryption
C[i-1] predictably flips bytes in plaintext block P[i].
Why CBC is malleable
The decryption formula reveals a direct relationship between the ciphertext and the plaintext of the next block:j in C[i-1] flips bit j in P[i]. An attacker who knows P[i] can XOR the desired change into C[i-1] to get a chosen P[i]'.
Bit-flip exploit
Scenario: a cookie is formatted asrole=user&admin=false, encrypted with CBC, and the server checks the decrypted value.
The block immediately before the target (block
target_block - 1) will decrypt to garbage, because its decryption now uses the modified ciphertext as IV. Plan for this side-effect in the exploit.PKCS#7 padding
AES operates on 16-byte blocks. Plaintext is padded to a multiple of 16 bytes using PKCS#7:CBC-MAC
CBC-MAC computes an authentication tag as the final output block of CBC encryption with IV=0:Variable-length forgery
GivenT1 = CBC_MAC(key, M1) and T2 = CBC_MAC(key, M2):
T1 into the first block of M2 cancels the CBC state, making the computation continue as if it started fresh for M2.
Decrypting with a padding oracle
A padding oracle exists when a system reveals (through error message, HTTP status code, or response time) whether a decrypted ciphertext has valid PKCS#7 padding. This leaks one bit of information per query and is enough to:- Decrypt any ciphertext without the key (16 queries per byte, in the worst case 256 each)
- Encrypt any chosen plaintext (forge ciphertext)
Common CBC vulnerabilities in the wild
| Flaw | Risk |
|---|---|
| Reused IV | Known-plaintext reveals keystream XOR relationship between messages |
| Padding oracle | Full decryption and encryption without the key |
| CBC-MAC with variable-length input | Tag forgery |
| Missing authentication | Bit-flip attacks modify plaintext undetected |
| CBC for password storage | Offline dictionary attacks possible |
Mitigations
- Use Authenticated Encryption with Associated Data (AEAD): AES-GCM, AES-GCM-SIV, or ChaCha20-Poly1305. These provide confidentiality and integrity in a single primitive.
- If CBC must be used, apply encrypt-then-MAC (never MAC-then-encrypt) and use a fresh random IV per encryption.
- Use constant-time padding validation to eliminate timing oracles.