Category: Reverse Engineering
Difficulty: Medium
Points: 30
Description
A trial version of “Arcane Calculator” is provided as a Python script. The full version is locked behind a license key. Reverse-engineering the key validation logic reveals the key.
Overview
- The key follows the template
picoCTF{1n_7h3_|<3y_of_XXXXXXXX}where the 8 dynamic characters are derived from the SHA256 hash of the usernameGOUGH. - The
check_keyfunction reveals the exact index sequence used to pick those characters. - The “full version” is an AES-Fernet blob embedded in the script that decrypts once the correct key is entered.
Solution
Step 1: Identify the Key Structure
- The script exposes the key template directly as a global constant:
key_part_static1_trial = "picoCTF{1n_7h3_|<3y_of_"
key_part_dynamic1_trial = "xxxxxxxx" # 8 unknown characters
key_part_static2_trial = "}"
- The task reduces to finding those 8 characters.
Step 2: Read the Validation Logic
check_keycompares each dynamic character against a specific index ofhashlib.sha256(b"GOUGH").hexdigest().- The indices used, in order, are
[4, 5, 3, 6, 2, 7, 1, 8].
if key[i] != hashlib.sha256(username_trial).hexdigest()[4]: return False
if key[i] != hashlib.sha256(username_trial).hexdigest()[5]: return False
if key[i] != hashlib.sha256(username_trial).hexdigest()[3]: return False
if key[i] != hashlib.sha256(username_trial).hexdigest()[6]: return False
if key[i] != hashlib.sha256(username_trial).hexdigest()[2]: return False
if key[i] != hashlib.sha256(username_trial).hexdigest()[7]: return False
if key[i] != hashlib.sha256(username_trial).hexdigest()[1]: return False
if key[i] != hashlib.sha256(username_trial).hexdigest()[8]: return False
Step 3: Reconstruct the Key
- Compute the SHA256 hash of the username and extract the characters at the specified indices.
import hashlib
h = hashlib.sha256(b"GOUGH").hexdigest()
indices = [4, 5, 3, 6, 2, 7, 1, 8]
dynamic = "".join(h[i] for i in indices)
key = "picoCTF{1n_7h3_|<3y_of_" + dynamic + "}"
print(key)
Step 4: Decrypt the Full Version
- The Fernet cipher is initialized with the key (base64-encoded), and the embedded blob is decrypted to produce
keygenme.py. - The full version script contains the actual flag.
Why This Works
- The developer inadvertently exposed the entire validation logic in plaintext.
- A proper keygen would use a hardware token or a server-side secret; storing the validation logic client-side always allows static analysis.
- Fernet encryption does not help here because the key derivation is visible in the same file.