TryHackMe | Flip
Writeup of a easy-rated Crypto Challenge from TryHackMe
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,keyandivand performs encryption on thedatausing 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-CBCmode and checks ifadmin&password=sUp3rPaSs1is present in the decrypted data. - The function returns
1if the condition is met, and0otherwise.
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:
- It constructs the data to be encrypted using the function
encrypt_data()in the string variablemessageby combining the user-providedusernameandpassword. The resulting string follows the formataccess_username=<username>&password=<password> - It encrypts the content of the string variable
messageand sends the resulting ciphertext to the user. - 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 returns1, a success message is sent along with the flag. Otherwise, a failure message is displayed.
- It constructs the data to be encrypted using the function
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
messageand checks ifadmin&password=sUp3rPaSs1is a substring of the stringmessage. If it’s the case, an error message is returned. Otherwise, thesetup()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.
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 …

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:

- 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 thesecond 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.
.png)
Refer to the provided representation below for a better understanding of the encryption process:

- As you can see, the message
access_username=bdmin&password=sUp3rPaSs1&password=passwordis 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:
- 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
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://crypto.stackexchange.com/questions/66085/bit-flipping-attack-on-cbc-mode/66086#66086



.png)