./dr3dd

AES-CBC bit flipping Attack

by on under Cryptography
8 minute read

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:

alt text

alt text

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!!!}

Crypto, n00b19CTF, Easy-Flipp, AES-CBC
comments powered by Disqus