1337 Local CTF: Baby-AES — Known Plaintext → Tiny Keyspace
Baby-AES
Challenge Context
The challenge provided aes.py. The script reads the flag, uses it as an AES key padded with null bytes, encrypts a 16-byte zero block in ECB mode, and prints the ciphertext.
Reconnaissance
The important part of the source was:
with open("flag.txt", "rb") as f: flag = f.read().strip()
key = flag + b"\x00" * (16 - len(flag))cipher = AES.new(key, AES.MODE_ECB)pt = b"\x00" * 16ct = cipher.encrypt(pt)
print("len(flag): ", len(flag))print("Ciphertext (hex):", ct.hex())The output told us:
len(flag): 9Ciphertext (hex): 489b02c7245f7d5b6221ba7bd7f85c29Vulnerability / Core Idea
The flag is the AES key. Because the flag length is 9 and the format is leet{...}, only three bytes are unknown:
leet{???}This is not an AES break. The cryptographic mistake is turning a low-entropy, format-constrained secret into a symmetric key and revealing a known plaintext/ciphertext pair.
Exploitation
For each candidate leet{XYZ}:
- Pad it with seven null bytes to produce a 16-byte AES key.
- Encrypt
00 * 16under AES-ECB. - Compare the output to
489b02c7245f7d5b6221ba7bd7f85c29.
from Crypto.Cipher import AESimport itertools, string
target = bytes.fromhex("489b02c7245f7d5b6221ba7bd7f85c29")pt = b"\x00" * 16charset = string.ascii_letters + string.digits + "_!@#$%^&*()-+=[]{}|;:',.<>?/~`"
for a, b, c in itertools.product(charset, repeat=3): flag = f"leet{{{a}{b}{c}}}".encode() key = flag + b"\x00" * 7 if AES.new(key, AES.MODE_ECB).encrypt(pt) == target: print(flag.decode()) breakFlag
leet{lol}Lesson Learned
Known plaintext plus a tiny keyspace turns strong primitives into a dictionary check. The primitive was fine; the key derivation was the bug.
Cover: Wallhaven xelyx3.
Share Article
If this article helped you, please share it with others!