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
,key
andiv
and performs encryption on thedata
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 ifadmin&password=sUp3rPaSs1
is present in the decrypted data. - The function returns
1
if the condition is met, and0
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:
- It constructs the data to be encrypted using the function
encrypt_data()
in the string variablemessage
by combining the user-providedusername
andpassword
. The resulting string follows the formataccess_username=<username>&password=<password>
- It encrypts the content of the string variable
message
and 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
message
and checks ifadmin&password=sUp3rPaSs1
is 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.
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=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:
- 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