Score! You found a treasure chest! Now if only you could figure out
how to unlock it… maybe there’s a magic word?
Initial Analysis
The challenge provides a Linux binary that prompts for a “magic
word.” If the correct word is entered, it displays a treasure chest
(ASCII art) and presumably the flag. The goal is to reverse-engineer the
“magic word” verification logic.
Using IDA Pro to decompile the main function, the
following program flow was identified:
Input Collection: The program reads up to 256
characters from stdin using fgets.
Padding & Allocation:
It calculates the input length (v8).
It determines a padding value (v7 = v8 % 8).
It allocates memory for the input plus padding and copies the string
into it.
Crucially, it appends v7 null bytes.
Encryption: It calls sub_4012A9, which
iterates through the input in 8-byte blocks and encrypts them using
sub_4011C6.
Verification: The program checks if the final
processed length v8 is 34 (0x22) and if
the resulting ciphertext matches a hardcoded byte array at
unk_404080.
The function sub_4011C6 implements a variation of the
Tiny Encryption Algorithm (TEA).
Key: The key is the hardcoded string
"tiny_encrypt_key".
Constants: It uses the standard TEA delta
0x9E3779B9.
Structure: It performs 32 rounds of Feistel-like
transformations.
Implementation Detail: Unlike standard TEA, it
updates the “sum” (v6) at the start of the loop and uses key indices
[0, 1] for the first half and [2, 3] for the
second half of the block update.
Extraction & Decoding
To solve the challenge, we extract the target ciphertext from
0x404080 and the 16-byte key. Since TEA is a symmetric
block cipher, we can implement a decryption routine.
Target Ciphertext (34 bytes):38 75 5B CB 44 D2 BE 5D 96 9C 56 43 EA 98 06 75 4A 48 13 E6 D4 E8 8E 4F 72 70 8B FF DC 99 F8 76 C5 C9
# Data from unk_404080 ciphertext = bytes.fromhex("38755BCB44D2BE5D969C5643EA9806754A4813E6D4E88E4F72708BFFDC99F876") key = b"tiny_encrypt_key"
flag = b"" for i inrange(0, len(ciphertext), 8): flag += tea_decrypt(ciphertext[i:i+8], key)
print(flag.decode().strip('\x00'))
Conclusion
Running the decryption yields the magic word/flag. The check for
length 34 and the specific padding logic suggests the original flag was
29 characters long, which when padded with 5 null bytes (29 % 8 = 5),
results in the 34-byte block compared by the binary.