CBC Bit Flipping Attack: Tampering Without the Key

CBC Bit Flipping Attack: Tampering Without the Key

Written by

in

A cbc bit flipping attack turns missing integrity protection into a tampering tool. The attacker cannot read your plaintext and does not know the key, yet by flipping chosen bits in one ciphertext block they flip the exact same bits in the next decrypted block. If that block holds a field like a role or a flag, they can rewrite it to their advantage. This post shows why CBC hands that control to an attacker and how authenticated encryption shuts it down.

How CBC decryption actually works

CBC stands for Cipher Block Chaining. When encrypting, each plaintext block is XORed with the previous ciphertext block before the block cipher runs, which chains the blocks together. The very first block is XORed with an initialization vector, the IV, instead of a previous block. Decryption runs the same chain in reverse:

P[i] = D(key, C[i]) XOR C[i-1]
P[0] = D(key, C[0]) XOR IV

Look closely at those two lines. The decrypted plaintext of block i is the raw block cipher output XORed with the previous ciphertext block. That previous block is data the attacker can see, and in a cookie or a token on the wire, often data the attacker can change. That is the whole weakness.

Why a cbc bit flipping attack works

XOR has one property that matters here. Flip a single bit of an input and you flip the matching bit of the output, nothing else moves. In CBC, C[i-1] feeds straight into the XOR that produces P[i]. So flipping bit 6 of ciphertext block i-1 flips bit 6 of plaintext block i after decryption. The attacker steers that bit without ever touching the key.

There is a price. The block you tamper with, C[i-1], still runs through the block cipher on its way to P[i-1], so that earlier block decrypts to random garbage. The attack pays off when the garbled block sits somewhere the application ignores or parses loosely, while the block you care about lands on a field that matters. Targeting block 0 avoids the cost entirely, because there you flip the IV, and the IV is not a decrypted block.

A worked example

Imagine a note sharing app called Acme Notes. After login it gives the browser an encrypted session cookie. The plaintext of its first 16 byte block is exactly:

admin=0&user=bob

The app encrypts this with CBC and trusts whatever it decrypts. The attacker wants the admin value to read 1 instead of 0. The character 0 is byte index 6 of block 0. Since block 0 is XORed with the IV, the attacker flips the matching byte of the IV and nothing else changes:

delta  = '0' XOR '1' = 0x30 XOR 0x31 = 0x01
IV'[6] = IV[6] XOR 0x01

With that one byte of the IV flipped, block 0 now decrypts to admin=1&user=bob. The username, the structure, every other byte stays intact because the IV only touches the first block. The attacker just granted themselves admin without the key, without reading a single plaintext byte, and without any error from the cipher.

If the target field sits in block 2 instead, the same trick works on ciphertext block 1, at the cost of block 1 decrypting to garbage. Attackers pick layouts where that collateral block is discarded or tolerated, and plenty of real formats oblige.

The missing idea: integrity

CBC on its own gives you confidentiality and nothing else. It was never designed to tell the receiver whether the ciphertext arrived unchanged. Decryption happily turns any bytes into some plaintext, garbled or not, and returns it. The application then parses that output and acts on it. Nothing in the pipeline ever asked the question that matters.

Encryption that is not authenticated answers “can I read this” but never “did anyone change this”. A cbc bit flipping attack lives entirely in that second, unasked question.

This is the same family of failure behind a padding oracle attack, where an error message about malformed padding leaks plaintext one byte at a time. Both start from the same root cause: a CBC ciphertext that the receiver decrypts before it ever checks whether the bytes are genuine. It also rhymes with a predictable iv attack, where a weak or reused IV undermines CBC from the other end. You can read more of these building blocks under vulnerability basics, and the wider pattern of chosen ciphertext tampering sits in our cryptographic oracle attacks hub.

How to fix it

The fix is to authenticate the ciphertext, so any tampered bit is caught and the whole message is rejected before decryption ever runs. Concrete steps:

  • Use an authenticated encryption mode. AES GCM or ChaCha20 Poly1305 produce a tag alongside the ciphertext. A flipped bit changes the tag check, the receiver sees the mismatch, and it refuses the message. Bit flipping stops being useful.
  • If you are stuck with CBC, use encrypt then MAC. Compute a MAC such as HMAC SHA256 over the IV and the full ciphertext, and send it alongside. On receipt, verify the MAC first. Only if it matches do you decrypt. Never decrypt and then check, and never trust a value you decrypted before the MAC passed.
  • Verify with a constant time comparison. Compare the received MAC to the computed one with a comparison that does not leak timing, so an attacker cannot tune forged tags byte by byte.
  • Cover everything the receiver trusts. The authenticated data must include the IV and any associated context, otherwise an attacker tampers with whatever you left outside the tag.
  • Do not invent your own construction. Reach for a vetted library that exposes an authenticated mode by default, so the correct order is the only order on offer.

The shape of this bug is worth keeping in mind. AES is not broken. CBC is doing exactly what it was specified to do. The failure is an assumption in the surrounding code: that a ciphertext which decrypts cleanly must be a ciphertext nobody altered. That assumption is false, and code review misses it because each encrypt and decrypt call looks correct on its own line.

This is the kind of flaw that shows up when you reason about how an application builds a ciphertext and whether it ever actually verifies integrity before acting on the result, which is exactly what an autonomous researcher that tests an app’s assumptions is built to check. Read how we think about it on our about page.

Frequently asked questions

What is a CBC bit flipping attack?

It is a tampering attack on CBC ciphertext that has no integrity protection. Because each plaintext block is the block cipher output XORed with the previous ciphertext block, an attacker who flips a bit in one ciphertext block flips the exact same bit in the next decrypted block. They can rewrite a field such as a role or a flag without knowing the key and without reading the plaintext. The cost is that the block they tamper with decrypts to garbage.

How can an attacker change plaintext without the key?

XOR flips the matching output bit when you flip an input bit. In CBC the previous ciphertext block is XORed into the current decrypted block, so controlling bits of that previous block controls the same bits of the next plaintext. To change the first block cleanly an attacker flips bits of the IV instead, which touches only that block and leaves every other byte intact.

Why does CBC allow this at all?

CBC provides confidentiality but no integrity. Decryption turns any bytes into some plaintext and hands it back without ever checking whether the ciphertext was changed on the way. The application then parses and trusts that output. Nothing in the flow asks whether the message is genuine, so flipped bits pass straight through.

How do you prevent a CBC bit flipping attack?

Authenticate the ciphertext so any change is detected before decryption. Use an authenticated mode like AES GCM or ChaCha20 Poly1305, which rejects a tampered message by its tag. If you must keep CBC, use encrypt then MAC: compute an HMAC over the IV and ciphertext, verify it in constant time, and only decrypt if it matches. Never decrypt first and check later.


Put an autonomous researcher on your own systems

UnboundCompute is an autonomous security researcher that reasons about how an application fits together and proves the access control and injection bugs it finds. We are opening a small number of founding design partner seats: private early access pointed at a staging target you choose, and a say in what it looks for. If your team ships software worth pressure testing, apply to the design partner program.