AES-CBC bit flipping Attack
n00b19CTF(Easy-Flipp, 100 pt)
Welcome to this simple crypto challenge created by me. During the CTF, no one managed to solve this challenge, so I decided to provide a writeup for beginners to understand how CBC bit flipping works.
AES-CBC Encryption and Decryption Flowchart:
The main vulnerability of CBC is that it uses the ciphertext of the previous block to encrypt the next block of plaintext. Similarly, during decryption, the second block’s ciphertext is XORed with the previous block’s ciphertext. Now, let’s think about what happens if we change some bits in the previous block’s ciphertext. Obviously, the next block’s plaintext will be altered. Easy, right? Let’s explore it practically!
Now let’s discuss the challenge!
Hosted on: nc noob.bckdr.in 10006
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
from Crypto.Random import get_random_bytes
greeting = """
___ ___ _ ___ _____ ___ _ ___
_ __ / _ \ / _ \ | |__ / __\/__ \ / __\ / | / _ \
| '_ \ | | | || | | || '_ \ / / / /\// _\_____ | || (_) |
| | | || |_| || |_| || |_) |/ /___ / / / / |_____|| | \__, |
|_| |_| \___/ \___/ |_.__/ \____/ \/ \/ |_| /_/
"""
key = get_random_bytes(16)
iv = get_random_bytes(16)
flag = open('flag','rb').read().strip()
def encrypt_data(data):
cipher = AES.new(key, AES.MODE_CBC,iv)
enc = cipher.encrypt(pad(data,16,style='pkcs7'))
return enc.encode('hex')
def decrypt_data(encryptedParams):
cipher = AES.new(key, AES.MODE_CBC,iv)
paddedParams = cipher.decrypt(encryptedParams.decode('hex'))
return unpad(paddedParams,16,style='pkcs7')
print(greeting)
print('hey n00b!! you know how CBC bit flipping works?\nIf you flip the bit correctly i will reward you fl4g!')
print(line)
msg = "admin=0"
print("Current Auth Message is : " + msg)
print("Encryption of auth Message in hex : " + iv.encode('hex') + encrypt_data(msg))
enc_msg = raw_input("Give me Encrypted msg in hex : ")
try:
final_dec_msg = decrypt_data(enc_msg)
if "admin=1" in final_dec_msg:
print('Whoa!! you got it!! Now its reward time!!')
print(flag)
else:
print('Try again you can do it!!')
exit()
except:
print('bye bye!!')
In this code, we can observe two functions: encrypt_data() and decrypt_data(). The encrypt_data() function takes a message, randomly generated key, and IV, and encrypts the message using AES-CBC. In CBC encryption and decryption, for the first block, there is no previous block, so the IV is used.
Let’s break down the encryption process step by step:
Message: admin=0
Block size: 16
After PKCS7 padding, the message becomes: admin=0\t\t\t\t\t\t\t\t\t
Here, we added \t nine times because len('admin=0') = 7and16 - 7 = 9. So, chr(9)*9 = "\t\t\t\t\t\t\t\t\t"
Our target is to change the encrypted data so that after decryption, the message becomes: admin=1
Now, let’s run the challenge!
~/ctf-2018/n00bctf18/Easy-Flipp$ nc noob.bckdr.in 10006
___ ___ _ ___ _____ ___ _ ___
_ __ / _ \ / _ \ | |__ / __\/__ \ / __\ / | / _ \
| '_ \ | | | || | | || '_ \ / / / /\// _\_____ | || (_) |
| | | || |_| || |_| || |_) |/ /___ / / / / |_____|| | \__, |
|_| |_| \___/ \___/ |_.__/ \____/ \/ \/ |_| /_/
hey n00b!! you know how CBC bit flipping works?
If you flip the bit correctly i will reward you fl4g!
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)
Current Auth Message is : admin=0
Encryption of auth Message in hex : fefbc34b8c1ec3f371f37ab5378e0e212f3ffd012a824cd16ffe8030d8bcd963
Give me Encrypted msg in hex :
The output encrypted message in hex and its length is 32 bytes.
From the above code, we can infer that the length of the encrypted message is the sum of the IV (16 bytes) and the message (16 bytes) in hex.
Let’s break down the encrypted message:
In [4]: a = "fefbc34b8c1ec3f371f37ab5378e0e212f3ffd012a824cd16ffe8030d8bcd963".decode('hex')
In [5]: len(a)
Out[5]: 32
In [6]: a[:16]
Out[6]: '\xfe\xfb\xc3K\x8c\x1e\xc3\xf3q\xf3z\xb57\x8e\x0e!'
In [7]: a[16:]
Out[7]: '/?\xfd\x01*\x82L\xd1o\xfe\x800\xd8\xbc\xd9c'
The first block is the IV: \xfe\xfb\xc3K\x8c\x1e\xc3\xf3q\xf3z\xb57\x8e\x0e!
The second block is the padded encrypted message: /?\xfd\x01*\x82L\xd1o\xfe\x800\xd8\xbc\xd9c
The byte we need to change in the message (admin=0
) is at index 6.
During the decryption process, the code decrypts the data using standard AES (/?\xfd\x01*\x82L\xd1o\xfe\x800\xd8\xbc\xd9c, key)
and XORs it with
the previous block, which is the IV in this case.
To achieve our goal, we need to change the byte at index 6 in the IV so that after XORing, it becomes ‘1’ instead of ‘0’ in the message.
Initially, in decryption: IV[6] ^ Dec_AES(message[6]) = '0'
.
We want ‘1’, so by XORing both sides with ‘1’: (IV[6]^1)^Dec_AES(message[6]) = '0^1'
.
This simplifies to: (IV[6]^1)^Dec_AES(message[6]) = '1'
.
To accomplish this, we just need to XOR the character of the IV at index 6 with ‘1’, encode it in hex, and send it. Let’s do it!
In [13]: a = "fefbc34b8c1ec3f371f37ab5378e0e212f3ffd012a824cd16ffe8030d8bcd963".decode('hex')
In [14]: a[6]
Out[14]: '\xc3'
In [15]: chr(ord('\xc3')^1)
Out[15]: '\xc2'
In [16]: a = a[:6] + '\xc2' + a[7:]
In [17]: a.encode('hex')
Out[17]: 'fefbc34b8c1ec2f371f37ab5378e0e212f3ffd012a824cd16ffe8030d8bcd963'
Let’s see what happens if we send this changed encrypted data:
~/ctf-2018/n00bctf18/Easy-Flipp$ nc noob.bckdr.in 10006
___ ___ _ ___ _____ ___ _ ___
_ __ / _ \ / _ \ | |__ / __\/__ \ / __\ / | / _ \
| '_ \ | | | || | | || '_ \ / / / /\// _\_____ | || (_) |
| | | || |_| || |_| || |_) |/ /___ / / / / |_____|| | \__, |
|_| |_| \___/ \___/ |_.__/ \____/ \/ \/ |_| /_/
hey n00b!! you know how CBC bit flipping works?
If you flip the bit correctly i will reward you fl4g!
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)(_)
Current Auth Message is : admin=0
Encryption of auth Message in hex : fefbc34b8c1ec3f371f37ab5378e0e212f3ffd012a824cd16ffe8030d8bcd963
Give me Encrypted msg in hex : fefbc34b8c1ec2f371f37ab5378e0e212f3ffd012a824cd16ffe8030d8bcd963
Whoa!! you got it!! Now its reward time!!
CTF{XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}
Congratulations! You successfully flipped the bit and obtained the flag:
CTF{flag is redacted because its hosted on backdoor server!!!}
Let me know what you think of this article on twitter @0xdr3dd or leave a comment below!