FIGureeee1tout: Turning SHAttered into an AES Key
- ▣ Sep 2026
- ◈ Cryptography 2 · FIGureeee1tout
- ◷ 5 min read
- ⚑ Team Megnetar
Overview
verifier.py hid its internals (_BLK, _digest and _kdf) but stated the rule: provide data_a and data_b that differ, yet produce the same digest. The pair then feeds a key-derivation step that decrypts the flag.
Challenge brief
Find two different inputs that the verifier considers identical.
Analysis
The required block size of 320 bytes is the giveaway: it matches the colliding prefix of the SHAttered PDFs, the first practical SHA-1 collision published in 2017. The first 320 bytes of shattered-1.pdf and shattered-2.pdf differ but collide under SHA-1.
Solution
The verifier derives its AES key as _kdf(data_a[:_BLK] + data_b[:_BLK])[:16]; with SHA-256 as the KDF this produced a 16-byte AES-128 key.
flag.enc is a small container: the first two bytes give the ciphertext length (0x0060 = 96), the next 16 bytes are the IV and the remaining 80 bytes are AES-CBC ciphertext. Decrypting and removing PKCS#7 padding returned the flag.
1from hashlib import sha2562from Crypto.Cipher import AES3from Crypto.Util.Padding import unpad4 5a = open("shattered-1.pdf", "rb").read()[:320]6b = open("shattered-2.pdf", "rb").read()[:320]7key = sha256(a + b).digest()[:16]8 9blob = open("flag.enc", "rb").read()10iv, ct = blob[2:18], blob[18:]11print(unpad(AES.new(key, AES.MODE_CBC, iv).decrypt(ct), 16))Flag
Redacted. Format: Bsides_Mumbai{…}.
Lessons learned
SHA-1 must not be used anywhere integrity matters. Known collision prefixes make "two different files, same hash" a lookup, not a research problem.

