crackme-py picoCTF 2021 Solution

Published: April 2, 2026

Description

Download the Python script crackme.py and find how to get the flag from the bezos_cc_secret variable.

Download crackme.py.

bash
wget <url>/crackme.py
  1. Step 1Read the source and identify the decode function
    Open crackme.py. It contains two relevant names: bezos_cc_secret (the encoded flag) and decode_secret (the function that reverses the encoding). The decode function is exposed and callable without solving the password check.
    bash
    less crackme.py
    Learn more

    What ROT47 actually does. ROT47 rotates each printable ASCII character (code points 33 to 126, i.e. ! through ~) by 47 positions, wrapping inside that 94-character range. So A (65) becomes p (65 + 47 = 112), p becomes A, and so on. Because 47 is exactly half of 94, applying ROT47 twice returns the original string, which makes it self-inverse - the same function encodes and decodes. The decode_secret function implements this shift on the encoded string stored in bezos_cc_secret.

    The reverse-engineering insight: recognize when a program already contains its own decryption routine. Rather than reimplementing the algorithm, you call the existing function with the right input. Common pattern in crackme challenges and in real malware analysis.

  2. Step 2Call decode_secret directly on the hardcoded value
    Use exec() to load the script into the current Python session, which defines all its functions and variables. Then call decode_secret(bezos_cc_secret) directly to decode the flag without needing to know the password.
    python
    python3 -c "exec(open('crackme.py').read()); print(decode_secret(bezos_cc_secret))"
    Learn more

    exec(open('crackme.py').read()) evaluates the entire script as Python code in the current namespace, defining decode_secret and bezos_cc_secret. Note: exec() runs at module top level, which means any top-level code in the script executes, including the password prompt if it's not gated behind if __name__ == '__main__'. If the prompt fires anyway, swap to importlib.util.spec_from_file_location with spec.loader.exec_module(mod) after stripping the prompt block - or simpler, redirect stdin to /dev/null and read the variables from the module's namespace.

    This technique works because the encoding key is implicit in the decode function itself; the function does not require a separate secret key argument. Any encoding scheme that embeds its own decryption logic alongside the ciphertext provides no real security; the attacker just needs to locate and call that logic. For more on Python scripting idioms used across CTF challenges, see Python for CTF.

Flag

picoCTF{...}

The script verifies input but also exposes the decode function - just call it on the hardcoded encoded value to extract the flag directly.

Want more picoCTF 2021 writeups?

Useful tools for Reverse Engineering

Related reading

What to try next