Post

TryHackMe | Flip

Writeup of a easy-rated Crypto Challenge from TryHackMe


In this machine, a server is listening on port 1337 and requires a username and password to log in as an admin. Upon successful connection, the server returns a leaked ciphertext, encrypted using AES-CBC mode. By examining the provided source code and having knowledge of the plaintext structure being encrypted, it becomes apparent that the cipher is susceptible to a bit flipping attack.

Challenge Description


Log in as the admin and capture the flag! If you can…
The server is listening on port 1337 via TCP. You can connect to it using Netcat or any other tool you prefer.

Write-Up


Source Code

encrypt_data() function:
1
2
3
4
5
def encrypt_data(data,key,iv):
    padded = pad(data.encode(),16,style='pkcs7')
    cipher = AES.new(key, AES.MODE_CBC,iv)
    enc = cipher.encrypt(padded)
    return enc.hex()
  • This function takes 3 parameters: data, key and iv and performs encryption on the data using AES Advanced Encryption Standard cipher in Cipher Block Chaining mode.
  • The resulting ciphertext is returned in Hexadecimal representation.
decrypt_data() function:
1
2
3
4
5
6
7
def decrypt_data(encryptedParams,key,iv):
    cipher = AES.new(key, AES.MODE_CBC,iv)
    paddedParams = cipher.decrypt( unhexlify(encryptedParams))
    if b'admin&password=sUp3rPaSs1' in unpad(paddedParams,16,style='pkcs7'):
        return 1
    else:
        return 0
  • This function takes the encrypted data, key and IV as input, decrypts the data using AES-CBC mode and checks if admin&password=sUp3rPaSs1 is present in the decrypted data.
  • The function returns 1 if the condition is met, and 0 otherwise.
setup() function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def setup(server,username,password,key,iv):
        message = 'access_username=' + username +'&password=' + password
        send_message(server, "Leaked ciphertext: " + encrypt_data(message,key,iv)+'\n')
        send_message(server,"enter ciphertext: ")

        enc_message = server.recv(4096).decode().strip()

        try:
                check = decrypt_data(enc_message,key,iv)
        except Exception as e:
                send_message(server, str(e) + '\n')
                server.close()

        if check:
                send_message(server, 'No way! You got it!\nA nice flag for you: '+ flag)
                server.close()
        else:
                send_message(server, 'Flip off!')
                server.close()
  • This function handles the setup phase of the communication between the server and the client. Here’s an overview of what the function does:
    1. It constructs the data to be encrypted using the function encrypt_data() in the string variable message by combining the user-provided username and password. The resulting string follows the format access_username=<username>&password=<password>
    2. It encrypts the content of the string variable message and sends the resulting ciphertext to the user.
    3. It sends a request to the client to enter the ciphertext, and attempts to decrypt the received ciphertext by calling the decrypt_data() function. If the decrypt function returns 1, a success message is sent along with the flag. Otherwise, a failure message is displayed.
start() function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def start(server):
        key = get_random_bytes(16)
        iv = get_random_bytes(16)
        send_message(server, 'Welcome! Please login as the admin!\n')
        send_message(server, 'username: ')
        username = server.recv(4096).decode().strip()

        send_message(server, username +"'s password: ")
        password = server.recv(4096).decode().strip()

        message = 'access_username=' + username +'&password=' + password

        if "admin&password=sUp3rPaSs1" in message:
            send_message(server, 'Not that easy :)\nGoodbye!\n')
        else:
            setup(server,username,password,key,iv)
  • This function sets up the initial communication with the client/user, retrieves the username and password from the client, constructs the data to be encrypted in the string variable message and checks if admin&password=sUp3rPaSs1 is a substring of the string message. If it’s the case, an error message is returned. Otherwise, the setup() function is executed.

In summary, the user is prompted to provide a username and password. These credentials are combined into a string with the format access_username=<username>&password=<password>. If the substring admin&password=sUp3rPaSs1 is not found within the constructed message, it will be encrypted using the AES cipher in CBC mode (AES-CBC-128). The resulting ciphertext is then returned to the user. The user is then asked to input a ciphertext for decryption, and the decrypted plaintext is checked for the presence of the substring admin&password=sUp3rPaSs1. If this substring is found, a flag will be returned.


AES-CBC vulnerability:

AES-CBC mode:

AES, short for Advanced Encryption Standard, is a 128-bit symmetric bloc cipher, which means that it takes 128 bits of plaintext and encrypts it into 128 bits of ciphertext with a key that can either be 128, 192 or 256 bits.
In CBC mode, each block of plaintext is XORed with the previous ciphertext block before being encrypted. In other words, CBC mode links the output of one block to the input of the next block, which essentially randomise the encryption and hence generate distinct ciphertexts even if the same plaintext is encrypted multiple times.
For the first block, we use something called an Initialization Vector (IV), which is nothing more than a random array of bytes that has the same length of the blocks.

AES-CBC | Encryption


Decryption works by doing the process in reverse.
As you can see in the representation below, the first ciphertext block is decrypted using the decryption key (which is the same key used for encryption since we are working with symmetric encryption) and XORing the result with the Initialization Vector (IV), which gives us the first plaintext block.
Now, moving to the next block, we decrypt the second ciphertext block and XOR the result with the previous ciphertext block which gives us the second plaintext block, and so on …

AES-CBC | Decryption


Bit Flipping Attack:

Now that we understand how CBC works, we can now talk about Bit-Flipping attack.
The main vulnerable part of CBC is that, it relies on the previous ciphertext block in order to encrypt/decrypt the next plaintext/ciphertext block, and this exactly where the Bit-Flipping attack comes into play.
Let’s view the below visual representation of this attack in order to better understand how it works:

Bit Flipping Process
  • As you can see, a 1-bit error in the second ciphertext block :
    • Completely scrambles the plaintext block which has the same index as the modified ciphertext,
    • AND Generates the same 1-bit error in the next plaintext block (Third block).
  • For example, in order to flip the first bit of the third plaintext block, we need to flip the first bit of the second ciphertext block.

Exploitation:

The attack:

Since we have knowledge of the plaintext structure being encrypted with AES-CBC mode, along with the resulting ciphertext (Leaked ciphertext) and we know the condition that needs to be met in order to get the flag, we can perform the Bit Flipping attack, by manipulating a specific parts of the leaked ciphertext so that the string admin&password=sUp3rPaSs1 would be part of the decrypted data.

Weaponization:

To carry out the attack, we can construct a payload with intentionally misspelled characters, such as access_username=bdmin&password=sUp3rPaSs1&password=password (Note the purposeful misspelling of admin as bdmin). Next, we manipulate the ciphertext by modifying the previous block so that the b at the misspelled index is changed to a.
To do so, we input bdmin&password=sUp3rPaSs1 as the username and choose any desired value (not necessarily password) for the password field, so that the resulting message would be access_username=bdmin&password=sUp3rPaSs1&password=password as planned.

nc localhost 1337


Refer to the provided representation below for a better understanding of the encryption process:

Encryption process
  • As you can see, the message access_username=bdmin&password=sUp3rPaSs1&password=password is partitioned into 16-bytes blocks, and the misspelled character ‘b’ is placed in the first byte of the second plaintext block. Therefore, to replace ‘b’ with ‘a’, we must manipulate the first byte of the preceding block, which corresponds to the first block in the sequence. The following representation illustrates this manipulation:


Decryption process
  • As you can see, by performing the bit flipping process on the first byte of the previous block (Block 1), the first byte ‘b’ of the second block (Block 2) is replaced with ‘a’, as planned.
Executing the attack using Python:

The following code perform the Bit Flipping attack on the first byte of the first ciphertext block:

1
2
3
4
# Flipping the first byte of the first ciphertext block so that 'b' in the second plaintext block "bdmin&password=sUp3rPaSs1" becomes 'a' -> "admin&password=sUp3rPaSs1"
xor = ord('b') ^ ord('a')
flipped = hex(int(leaked_ciphertext[0:2], 16) ^ xor)[2:]
ciphertext = flipped.encode("utf-8") + leaked_ciphertext[2:]
  • This code flips the first byte of the first block of the leaked ciphertext, changing ‘b’ to ‘a’ in the second plaintext block. This manipulation is done using XOR operations. Next, it constructs the modified ciphertext by combining the flipped byte with the remaining leaked ciphertext.

You can find the entire script here


Exploit execution

References:


https://ir0nstone.gitbook.io/notes/other/pwntools/processes_and_communication

https://zhangzeyu2001.medium.com/attacking-cbc-mode-bit-flipping-7e0a1c185511

https://younestasra-r4z3rsw0rd.github.io/posts/MoreCookies/#back-to-basics-

https://alicegg.tech/2019/06/23/aes-cbc.html

https://crypto.stackexchange.com/questions/66085/bit-flipping-attack-on-cbc-mode/66086#66086

This post is licensed under CC BY 4.0 by the author.