Published on: March 24, 2025
3 min read · Posted by poppet_t
This challenge is unbearable to solve...
looking at the challenge file, u can see that there is a simple AES challenge with a few key steps,
User Input Handling: The client sends data in hex format via a POST request. This data is converted to bytes.
Plaintext Construction: The user's data is concatenated with the secret flag (user_data + flag).
Padding: The combined plaintext is padded using PKCS#7 padding to match the AES block size.
Encryption: AES-256-CBC encrypts the padded plaintext using the pre-generated 256-bit (32-byte) key and 128-bit (16-byte) IV.
Block Size: AES operates on a fixed 128-bit block size (16 bytes), regardless of the key length (256 bits in this case). The padding is explicitly applied to align the plaintext to this block size. The use of a static IV across all requests is a security weakness but doesn't affect the block size.
Hence, the induvidual characters of the flag can be guessed by sending in a null byte, this will cause the flag bytes to be added. Then this string will be padded to be 16 bytes to find one block. This block would then be AES-ed with a 32 byte key and 16 byte IV which is static, hence the system is deterministic. Classic example of a chosen-plaintext attack.
This solve script exploits the AES-CBC encryption oracle with a static IV to recover the flag byte-by-byte using a chosen-plaintext attack. Here's how it works:
Static IV Vulnerability:
Controlling Plaintext Blocks:
user_data + flag
.user_data
, the attacker aligns the flag bytes into predictable positions in the ciphertext blocks.Byte-by-Byte Bruteforce:
user_data
of length 15 - (current_flag_length % 16)
to shift the target flag byte into the last position of the first block.
\x00
). The first block becomes 15_pad_bytes + flag[0]
.user_data
= current_pad + known_flag_bytes + guessed_byte
.Validation via Ciphertext Matching:
known_flag
.Termination:
}
(indicating completion) or after a predefined length.user_data
length forces flag bytes into predictable block positions.Leak Flag[0]:
user_data = 15 bytes
.15_bytes + flag[0] + flag[1...]
.flag[0]
by comparing ciphertext blocks.Leak Flag[1]:
user_data = 14 bytes + known_flag[0]
.14_bytes + flag[0] + flag[1] + flag[2...]
.flag[1]
by matching ciphertext blocks.Repeat until the full flag is recovered.
Please login to comment
No comments yet