Description
The provided Python script (bloat.flag.py) hides logic behind an array of printable characters before requesting a password to decrypt flag.txt.enc. Deobfuscate the script to recover the password, then run it to reveal the flag.
Setup
Download both bloat.flag.py and flag.txt.enc into the same working directory.
Read through the script to understand how the lookup table a[...] maps back to readable characters.
After uncovering the hard-coded password (happychance), run the script to decrypt the encrypted flag file.
wget https://artifacts.picoctf.net/c/103/bloat.flag.pywget https://artifacts.picoctf.net/c/103/flag.txt.encpython3 bloat.flag.pypython3 bloat.flag.py | tee output.txtsed -n '2p' output.txtSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Understand the lookup table
ObservationEvery string in the script is assembled from indexed lookups into one character array rather than written as a literal. The whole obfuscation is array indexing, so define that array in a REPL and evaluate the index expressions yourself.Every string literal is built from indices into one character arraya. Defineain a REPL and let Python evaluate the indexing for you.pythonpython3 -c " # Paste the obfuscated array exactly as it appears in bloat.flag.py a = ['h','a','p','y','c','n','e','...'] # Then walk one of the obfuscated index expressions, e.g.: print(''.join([a[i] for i in [0, 1, 2, 2, 3, 4, 0, 1, 5, 4, 6]])) "Expected output
happychance
What didn't work first
Tried: Just run
python3 bloat.flag.pyimmediately to see what the script does without reading it first.The script asks for a password immediately, and without it you get no further; guessing, or pressing Enter, gives a decryption failure or garbled output. Read the source, evaluate the index expressions statically to recover the password, then run the script with it.
Tried: Use
strings bloat.flag.pyto search for the password as a plain text literal.strings extracts contiguous printable runs, but the password is never stored as a single literal: it is assembled at runtime by joining characters from the array through an index list. strings shows the raw array elements and stray Python keywords, never the assembled word. Evaluate the index expression in a REPL to rebuild it.
Learn more
The trick is "character-array string building": instead of
"happychance"the code writesa[i0] + a[i1] + a[i2] + .... At runtime the interpreter joins the same string; statically it's just integers. Definingain a REPL and running''.join([a[i] for i in [...]])on each obfuscated expression instantly reveals the plaintext.Don't run untrusted scripts on your host. Run inside a VM, container, or with
python3 -c "..."capturing only the print output. Real malware uses the same character-array, base64, and eval-chain patterns you see here.Step 2Recover the password
ObservationOne variable is built by joining individual characters from that array through an index list, then passed straight to the decryption routine. Walk those indices against the array and the plaintext password spells itself out, with no cryptanalysis needed.Walking the index expression for the credential variable spellshappychance. That string is whatflag.txt.encwas encrypted with.Learn more
Specifically, the script's password variable is assigned something like
password = a[i0] + a[i1] + .... Walking the index list against youraarray producesh-a-p-p-y-c-h-a-n-c-e. Because the array and the index sequence are both right there in the source, the obfuscation is purely cosmetic.Hardcoded credentials in source - even obfuscated ones - are a classic antipattern. Real systems store secrets outside source (environment variables, AWS Secrets Manager, HashiCorp Vault) or prompt the user at runtime and derive a key via PBKDF2/Argon2.
Step 3Decrypt the flag
Observationflag.txt.enc is the encrypted output, and the script holds the exact decryption routine keyed to the recovered password. Running the script and supplying that credential is the only route to a readable flag.Run the script with the recovered password, then verify the output starts withpicoCTF{before submitting.pythonpython3 bloat.flag.py | tee output.txtbash# enter: happychancebashsed -n '2p' output.txtbashgrep -oE 'picoCTF\{[^}]+\}' output.txtWhat didn't work first
Tried: Try to decrypt
flag.txt.encdirectly withopenssl enc -dorgpg --decryptwithout running the Python script.The script uses its own XOR or symmetric cipher logic, not an OpenSSL suite or a GPG envelope. Point openssl at the encrypted file and it errors on a bad magic number or unrecognized header, because the format matches no standard container. Run the Python script, which holds the exact routine that produced the file.
Tried: Press Enter (empty password) or type a guess like
passwordwhen the script prompts, hoping to bypass the check.The script checks your input against the obfuscated credential before it calls the decryption function. A wrong password gives either an explicit failure or binary garbage, neither of which is a flag. Recover the credential by evaluating the index expression from the source.
Learn more
Decryption is success when the output is well-formed: starts with
picoCTF{, ends with}, only printable ASCII inside. If the second line is binary garbage, the password is wrong or the script's decryption logic differs from what you reconstructed. Thegrep -oEregex is a robust alternative tosed -n '2p'when the output ordering shifts.teewrites stdin to both stdout and a file - useful when you want to see output live while also keeping a log. Combined with line-selection tools (sed,awk,grep), it beats copy-paste for any script that produces more than a couple of lines.
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{d30bfu5c4710n_f7w_b80...}
Never run opaque scripts blindly. Printing the decoded payload first keeps you safe and shows the password immediately.