Attacking CBC Mode Encryption: Bit Flipping

Photo by Seth Doyle on Unsplash

Recently, I participated in a CTF that included a challenge on CBC bit flipping. I learnt about an interesting technique that allows the attacker to arbitrarily change the value of the decoded plaintext in a CBC block cipher.

Cipher Block Chaining (CBC)

Block ciphers such as AES encrypt blocks of text at a time, rather than encrypting one bit at a time as in stream ciphers.

In the Cipher Block Chaining (CBC) mode of operation, each plaintext block is XORed with the previous ciphertext block before being encrypted.

Hence, if the encryption function is Eₖ, then we have the following recurrence relation:

CBC Encryption

Conversely, the decrypted result is XORed with the previous ciphertext block.

It follows that if the decryption function is Dₖ, then the decryption is:

CBC Decryption

The plaintext of each block depends not only on the corresponding block of ciphertext, but also on the previous block of ciphertext.

Bit Flipping

Suppose an attacker wishes to manipulate block i of the plaintext. He then XORs the previous ciphertext block with x:

Herein lies the problem: if the attacker has knowledge of the structure of the plaintext, x can be set such that the decrypted plaintext can be controlled.

An Example

Here’s the CTF scenario. While this is a simplified challenge scenario, it mirrors real-world applications of bit-flipping attacks. In most cases, this means bypassing filters to add otherwise disallowed characters, changing user information to elevate privileges or bypass authentication, etc.

When the attacker has prior knowledge about the structure of the plaintext, such an attack is particularly damaging.

We can input the user and passwd into the below string, which is subsequently encrypted. admin&password=goBigDawgs123 is not allowed.

msg = 'logged_username=' + user +'&password=' + passwdtry:
assert('admin&password=goBigDawgs123' not in msg)
except AssertionError:
send_msg(s, 'You cannot login as an admin from an external IP.\nYour activity has been logged. Goodbye!\n')

The ciphertext is given to us, and we are prompted to enter another ciphertext.

send_msg(s, "Leaked ciphertext: " + encrypt_data(msg)+'\n')
send_msg(s,"enter ciphertext: ")

Then, in decrypt_data(), the presence of admin&password=goBigDawgs123 is checked. The goal is to submit a ciphertext such that the corresponding plaintext contains admin&password=goBigDawgs123.

def decrypt_data(encryptedParams):
cipher =, AES.MODE_CBC,iv)
paddedParams = cipher.decrypt(unhexlify(encryptedParams))
if b'admin&password=goBigDawgs123' in unpad(paddedParams,16,style='pkcs7'):
return 1
return 0


From encrypt_data(), we can see that AES CBC encryption is used, with a block size of 16.

We can send a payload like logged_username=admin&parsword=goBigDawgs123 (note the purposeful misspelling of password as parsword). Then, we will edit the previous block of ciphertext such that r becomes s at the misspelt index. We simply have to change the ciphertext at the correct index.

For instance, the following code gives us the edited ciphertext.

user = 'admin&parsword=goBigDawgs123'
password = 'goBigDawgs123'
msg = 'logged_username=' + user + '&password=' + password
print(msg, len(msg))
xor = ord('r') ^ ord('s')
cipher = encrypt_data(msg)
cipher = cipher[:16] + hex(int(cipher[16:18], 16) ^ xor)[2:] + cipher[18:]

The decrypted data would be something like this:


Notice that the second block has been changed to the desired string. Since we modified the first block, it will no longer decode properly and become corrupted (but in this case it doesn’t matter since only the desired string is checked).


We simply have to dynamically supply the weaponized ciphertext depending on the encryption output at the server.

Here’s the output:




Simple is better than complex. Complex is better than complicated.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

How to Participate in Pi Network? KSCM Airdrop.

wormcon 0x01: Networking Challenges

Trade DOTUSDT, KSMUSDT Contracts to Win 5,000 USDT! CoinTiger Polkadot Contract Contest Series 3

Thanks to the @coinbureau research team for their latest review of Secret!

Metamask Guide #1:

Analysing PCAPs with Bro/Zeek

{UPDATE} Dot n Beat-Test hand speed Hack Free Resources Generator

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Zhang Zeyu

Zhang Zeyu

Simple is better than complex. Complex is better than complicated.

More from Medium

Why Log4j won’t go away: 5 key questions on the bombshell vulnerability

Egg Hunter: looking for a needle in a haystack

Malware fundamentals overview

Exploring Token Members Part 1