TryHackme Flip Room
By: Muhammad Saad
Challenge Description : Log in as the admin and capture the flag! If you can…
- In This Room we will be focusing on AES & CBC.
- AES (Advanced Encryption Standard) is a symmetric key encryption algorithm, meaning the same key is used for both encrypting and decrypting data. Its purpose is to securely encrypt information so that only someone with the correct key can read it. It supports key sizes of 128-bit, 192-bit, or 256-bit. In simple terms, it takes plain text and a secret key, performs multiple rounds of substitution, permutation, mixing, and key addition, and produces cipher text, which is the encrypted data.
- CBC (Cipher Block Chaining) is a mode of operation for block ciphers like AES. It works by taking each block of plain text and combining it with the previous block of cipher text before encrypting. This means each block depends on the one before it, making the encryption more secure. An initialization vector (IV) is used for the first block to start the process.
From the Room we got a python file : app-1684527681671.py and we are to connect to port 1337 via TCP by netcat.
Trying to Login with the given Credentials “admin&password=sUp3rPaSs1” the server would respond
Let’s have a look on the given file app-1684527681671.py, Inside the code, I find the following function:
The decrypt_data() function returns 1 only when the decrypted data contains the exact credentials: admin&password=sUp3rPaSs1
However, we have already tried logging in using
admin / sUp3rPaSs1, and it still fails.
This means the challenge does not expect us to use the credentials directly instead, we must craft a ciphertext that decrypts to this admin string.
Next, the
setup()function shows how the server interacts with the user:
This part reveals the intended attack path:
- The server gives us a leaked ciphertext generated using our own username and password.
- Then it asks us to submit a ciphertext.
- That ciphertext is passed to
decrypt_data(). - If the decrypted value contains
admin&password=sUp3rPaSs1, we get the flag.
Therefore, the goal is not cracking AES, but flipping bits in the ciphertext (CBC bit‑flipping attack) so that after decryption, the output matches the admin string.
To understand how the vulnerability works, I tried logging in with the following credentials:
username = bdmin
password = sUp3rPaSs1
This creates the message:
access_username=bdmin&password=sUp3rPaSs1
When the server encrypts this message, it returns the following leaked ciphertext:
c0fb51e07c1fc9f46ad9981473c8f6fd137a9b54c4b5b51a98b4a7cb617a32c7a52f02862aebc5d88547eb8160910e89
Immediately afterward, the server prompts us to enter a ciphertext.
If the ciphertext we submit decrypts to the correct admin string, the server will reveal the flag.
- To proceed with the attack, I first checked the length of the plaintext message: access_username=admin&password=sUp3rPaSs1
The message is 41 bytes long.
- To align it with AES‑CBC’s block requirements, we need the plaintext length to be a multiple of 16 bytes (because AES has a fixed 128‑bit / 16‑byte block size).
If we append 7 additional characters, the total becomes: 41 + 7 = 48 bytes - This fits perfectly into three 16‑byte blocks: 16 + 16 + 16 = 48 bytes
Since AES‑CBC encrypts the data block‑by‑block, each 16‑byte plaintext block corresponds directly to each 32‑hex‑character (16‑byte) ciphertext block.
This relationship is what allows us to perform a bit‑flipping attack: by modifying specific bytes in the ciphertext, we can control the decrypted output in the next block.
The Process of CBC Decryption :
CBC decrypts each block in two steps:
1. AES decrypt the ciphertext block
First, the block Ci is decrypted with the AES key:
Get 0x7ipher’s stories in your inbox
Join Medium for free to get updates from this writer.
intermediate = AES_decrypt(Ci)
2. XOR with previous ciphertext block
Then the result is XORed with the previous ciphertext block:
Pi = intermediate XOR C(i‑1)
For the first block, we use the IV instead of a previous block:
P1 = AES_decrypt(C1) XOR IV
In our case, the plaintext is split into three 16‑byte blocks:
(Here # represents padding.)
The leaked ciphertext is 96 hex characters, which equals 3 ciphertext blocks (each block = 32 hex characters = 16 bytes):
- The server will only reveal the flag if the decrypted plaintext contains: admin&password=sUp3rPaSs1
- But our current plaintext is: bdmin&password=sUp3rPaSs1
- Only one letter differs:
b(in “bdmin”) must becomea(in “admin”).
In CBC decryption: P2 = AES_decrypt(C2) XOR C1
- This means any change we make in ciphertext block C1 will directly change the corresponding bytes in plaintext block P2 when the server decrypts it.
- So to change: bdmin → admin
- We only need to modify the first byte of Block 2, which is controlled by the first byte of C1.
CBC Bit‑Flipping to Change “b” → “a”
Our blocks are:
The first byte of B2 is controlled by the first byte of C1.
We want:
- Current plaintext byte: b
Hex:0x62 - Desired plaintext byte: a
Hex:0x61
We need to adjust the first byte of C1 to change b → a.
We already checked earlier that:
- In Block 1:
ais 1 byte → C1 =c0(2 hex characters)- In Block 2:
bis 1 byte → C2 =13(2 hex characters)- When we XOR
c0 ^ 13, it equalsb. - The hex value of
bis62, so we can write: c0 ^ 13 = 62 and 0xc0 ^ 0x62 = 13 - Performing hex on
0xc0 ^ 0x62gives0xa2.
Then further:0xc0 ^ 0xa2 = 0x62 - We know that
bin hex is62, but we wantainstead.
The hex ofais61. - To find the correct hex for C1 to produce “ a ” we would find the hex of
hex(0xa2 ^ 0x62)which is equals to 0xc3.
- So the first byte of C1 should definitely be c3: c3fb51e07c1fc9f46ad9981473c8f6fd
Let’s try using this final ciphertext to get the flag:
c3fb51e07c1fc9f46ad9981473c8f6fd137a9b54c4b5b51a98b4a7cb617a32c7a52f02862aebc5d88547eb8160910e89
The Flag : THM{FliP_DaT_B1t_oR_G3t_Fl1pP3d}








