Cryptography / Sea of Secrets CTF 2026
The Forgotten Vault
We get challenge.py, output.txt (a 64-byte hex ciphertext) and wordlist.txt (5,460 words). The vault is AES-128-ECB, and the key is MD5(password) where the password is a wordlist word,…
Crypto, Easy, 100 pts
Flag: HACKITUP{md5_k3y_d3r1v4t10n_w1th_p1n_m4ngl1ng_15_f4r_t00_w34k}
We get challenge.py, output.txt (a 64-byte hex ciphertext) and wordlist.txt (5,460 words). The vault is AES-128-ECB, and the key is MD5(password) where the password is a wordlist word, first letter capitalized, followed by a 2-digit PIN:
def format_password(word, pin):
return f"{word.capitalize()}{pin:02d}"
def derive_key(password):
return hashlib.md5(password.strip().encode("utf-8")).digest()That is 5,460 * 100 = 546,000 possible passwords, and MD5 with no salt costs nothing, so the whole space can be searched. ECB doesn't matter here. We never need the block structure, only a way to tell a right key from a wrong one: after decrypting, check for valid PKCS#7 padding and the string HACKITUP in the plaintext.
for word in words:
cap = word.capitalize()
for pin in range(100):
pw = f"{cap}{pin:02d}"
key = hashlib.md5(pw.encode()).digest()
pt = AES.new(key, AES.MODE_ECB).decrypt(ct)
padlen = pt[-1]
if 1 <= padlen <= 16 and pt.endswith(bytes([padlen]) * padlen):
text = pt[:-padlen].decode(errors="ignore")
if "HACKITUP" in text:
print(pw, text)The full script is solve.py. It finishes in a couple of seconds and finds word keystone, PIN 42, password Keystone42:
HACKITUP{md5_k3y_d3r1v4t10n_w1th_p1n_m4ngl1ng_15_f4r_t00_w34k}