Description
Download the Python script crackme.py and find how to get the flag from the bezos_cc_secret variable.
Setup
Download crackme.py.
wget <url>/crackme.pySolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Read the source and identify the decode function
Observationcrackme.py holds both the encoded flag, in bezos_cc_secret, and a decode_secret function at the top of the file. The password check is only a gate, and the decryption logic can be called directly without satisfying it.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.bashless crackme.pyWhat didn't work first
Tried: Run the script normally and try to guess or brute-force the password to get the flag.
The script prompts for a password and exits on a wrong answer, so brute-forcing through the prompt is slow and pointless. The decode function and the ciphertext both sit above the password logic, so call the decryption directly and never satisfy the check at all.
Tried: Search the file with strings or grep for a readable flag pattern like picoCTF.
The flag is stored ROT47-encoded in bezos_cc_secret, so grep and strings output the scrambled ciphertext, not the plaintext. ROT47 rotates every printable ASCII character, making the output look like printable garbage with no recognizable picoCTF prefix until it is decoded.
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. SoA(65) becomesp(65 + 47 = 112),pbecomesA, 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. Thedecode_secretfunction implements this shift on the encoded string stored inbezos_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.
Step 2Call decode_secret directly on the hardcoded value
Observationbezos_cc_secret and decode_secret are both defined at module top level, ahead of any password logic. So exec() the script into a Python one-liner's namespace and call decode_secret(bezos_cc_secret) directly, skipping the gate.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.pythonpython3 -c "exec(open('crackme.py').read()); print(decode_secret(bezos_cc_secret))"What didn't work first
Tried: Import crackme as a module with 'import crackme' to access decode_secret and bezos_cc_secret.
A bare import runs the module at top level, so the password prompt fires before you can touch any names. The exec() one-liner runs top-level code too, but piping /dev/null to stdin, or stubbing out input, suppresses the prompt. A plain import gives you no easier way round it short of editing the file.
Tried: Implement ROT47 manually in a separate script rather than reusing the decode_secret function from the file.
A hand-rolled ROT47 works in principle but invites an off-by-one in the character range, 33 to 126 inclusive for 94 characters, or the wrong modulo wrap. Reusing decode_secret removes that risk entirely, because it is the same function the program uses itself, so every implementation detail is correct by construction.
Learn more
exec(open('crackme.py').read())evaluates the entire script as Python code in the current namespace, definingdecode_secretandbezos_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 behindif __name__ == '__main__'. If the prompt fires anyway, swap toimportlib.util.spec_from_file_locationwithspec.loader.exec_module(mod)after stripping the prompt block - or simpler, redirect stdin to/dev/nulland 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.
Interactive tools
- Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.
- Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.
Flag
Reveal flag
picoCTF{1|/|_4_p34||ut_...}
Per-instance flag with consistent prefix picoCTF{1|/|_4_p34||ut_} and varying 8-hex-char suffix per team (e.g. 4593da8a, dd2c4616, 8c551048 seen across instances).