CBC Attacks
cryptointermediate
Exploit AES-CBC vulnerabilities - bit flipping, IV=Key recovery, and padding oracle chaining.
cryptoaescbcbit-flipivpadding-oracledecryption
CBC Attacks
AES-CBC (Cipher Block Chaining) chains blocks by XORing each plaintext block with the previous ciphertext block before encryption. This introduces vulnerabilities that ECB doesn't have.
What is CBC?
- Cipher Block Chaining (CBC) is a mode that adds chaining between blocks: before encrypting each plaintext block the system XORs it with the previous ciphertext block (or the IV for the first block). This means each ciphertext block depends on all previous plaintext blocks, not just the block itself.
Why this matters
- Changes to one block affect the next block's decryption, enabling both protections and attack surfaces (e.g., bit-flipping and padding oracles).
- Unlike ECB, CBC hides repeated patterns across equal plaintext blocks, but incorrect use (predictable IVs, reused IVs, lack of authentication) introduces CTF-relevant vulnerabilities.


CBC Math Review
Encrypt:
Decrypt:
Notation (quick reference)
- P_i: Plaintext block number i (16 bytes / 128 bits for AES).
- C_i: Ciphertext block number i (16 bytes / 128 bits).
- K: Symmetric key used for AES encryption/decryption.
- IV: Initialization Vector - a non-secret, typically random 16-byte value used to randomize encryption of the first block.
- AES_K(...): AES block encryption under key
K(encrypts a single 16-byte block). - AES_K^-1(...): AES block decryption under key
K(the inverse operation of AES_K).
Attack 1: Bit Flipping
Goal: Change a specific byte in P[i] by modifying C[i-1].
Since , flipping a bit in flips the corresponding bit in .
# Example: Change P[1][6] from 'X' (0x58) to 'Y' (0x59)
ciphertext = bytearray(oracle_encrypt(plaintext))
target_block = 0 # modify C[0] to affect P[1]
target_byte = 6 # byte position within the block
# original_value ⊕ desired_value = bit to flip
ciphertext[target_block * 16 + target_byte] ^= ord('X') ^ ord('Y')
# (If you know the plaintext byte at that position)
# Or if you don't know the original:
# Set: C[i-1][j] ^= known_P[i][j] ^ desired_P[i][j] (XOR adjustment)CTF application: If admin=0 is in P[1] and you can modify C[0]:
ct = bytearray(encrypt(b"A" * 16 + b"admin=0;role=user"))
# C[0] is the "A" * 16 block (our controlled padding)
# P[1] starts with "admin=0"
pos_of_0 = 16 + 6 # C[0][6] affects P[1][6]
ct[pos_of_0] ^= ord('0') ^ ord('1') # change '0' to '1'
submit(bytes(ct))Attack 2: IV = Key Recovery
If the IV equals the key (a common implementation mistake):
When :
Craft . With , decrypting gives (128-bit / 16-byte blocks).
def recover_iv_equals_key(oracle_encrypt, oracle_decrypt, block_size=16):
# Encrypt any 3-block plaintext
pt = b'A' * (block_size * 3)
ct = oracle_encrypt(pt)
C0 = ct[:block_size]
C1 = ct[block_size:2*block_size]
C2 = ct[2*block_size:3*block_size]
# Craft: C0 || 0...0 || C0
crafted = C0 + b'\x00' * block_size + C0
# Decrypt and XOR P'[0] with P'[2]
pt_prime = oracle_decrypt(crafted)
P0 = pt_prime[:block_size]
P2 = pt_prime[2*block_size:3*block_size]
key = bytes(a ^ b for a, b in zip(P0, P2))
return keyAttack 3: IV Manipulation
If the IV is user-controlled or predictable, you can control P[0]:
# P[0] = AES_K^{-1}(C[0]) ⊕ IV
# To set P[0] = desired: IV' = AES_K^{-1}(C[0]) ⊕ desired = P[0]_orig ⊕ IV_orig ⊕ desired
def craft_iv(original_iv, original_p0, desired_p0):
return bytes(a ^ b ^ c for a, b, c in zip(original_iv, original_p0, desired_p0))Attack 4: Padding Oracle (recap)
See Padding Oracle for the full attack. In brief:
# If oracle reveals valid/invalid padding:
# 1. Modify C[i-1] to control P[i] via CBC relationship
# 2. Find byte values that produce valid PKCS#7 padding
# 3. Use padding values to recover intermediate state
# 4. XOR with C[i-1] to get P[i]Common CTF CBC Scenarios
| Scenario | Attack |
|---|---|
| Encrypt with IV=0, change IV | IV manipulation |
| admin=false in position you control | Bit-flip attack |
| IV sent with ciphertext to client | Modify IV → control P[0] |
| Padding error revealed in response | Padding oracle |
| IV reused with same key | Known-plaintext recovery |
Last updated on