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 1
Understand the lookup tableObservationI noticed that every string inbloat.flag.pywas assembled from indexed lookups into a single character arrayarather than readable literals, which suggested the entire obfuscation was just array indexing and could be reversed by definingain a Python REPL and evaluating the index expressions directly.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, 5, 1, 6, 4, 7]])) "Expected output
picoCTF{d30bfu5c4710n_f7w_b80...}What didn't work first
Tried: Just run
python3 bloat.flag.pyimmediately to see what the script does without reading it first.The script immediately asks for a password interactively. Without knowing the password first you cannot get past the prompt, and guessing or hitting Enter produces a decryption failure or garbled output. The correct approach is to read the source, evaluate the index expressions statically to recover the password, and only then run the script with the known credential.
Tried: Use
strings bloat.flag.pyto search for the password as a plain text literal.stringsextracts contiguous printable character sequences, but the passwordhappychanceis never stored as a single literal - it is assembled at runtime by joining individual characters from the arrayavia an index list.stringsshows the raw character array elements and miscellaneous Python keywords but never the assembled word. You must evaluate the index expression yourself in a REPL to reconstruct the credential.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 2
Recover the passwordObservationI noticed the script contained a variable assigned by joining individual characters from the arrayavia an index list and passed it directly to the decryption routine, which suggested that walking those indices against the known array would spell out the plaintext password without any cryptanalysis.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 3
Decrypt the flagObservationI noticed thatflag.txt.encwas the encrypted output and the script contained the exact decryption routine keyed to the recovered passwordhappychance, which meant runningbloat.flag.pyand supplying that credential was the only path to producing a readablepicoCTF{...}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 custom XOR or symmetric cipher logic, not a standard OpenSSL cipher suite or GPG encryption format. Running
openssl enc -d -aes-256-cbc -in flag.txt.encproduces an error about a bad magic number or unrecognized header because the file format does not match any standard envelope. You must run the Python script, which contains the exact decryption routine used to produceflag.txt.enc.Tried: Press Enter (empty password) or type a guess like
passwordwhen the script prompts, hoping to bypass the check.The script compares your input against the hard-coded obfuscated credential before calling the decryption function. A wrong password either produces an explicit failure message or decrypts to binary garbage - neither gives a valid flag. The only path forward is recovering the credential
happychanceby 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.