bloat.py picoCTF 2022 Solution

Published: July 20, 2023

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.

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.

bash
wget https://artifacts.picoctf.net/c/103/bloat.flag.py
bash
wget https://artifacts.picoctf.net/c/103/flag.txt.enc
python
python3 bloat.flag.py
python
python3 bloat.flag.py | tee output.txt
bash
sed -n '2p' output.txt
  1. Step 1Understand the lookup table
    Every string literal is built from indices into one character array a. Define a in a REPL and let Python evaluate the indexing for you.
    python
    python3 -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]]))
    "
    Learn more

    The trick is "character-array string building": instead of "happychance" the code writes a[i0] + a[i1] + a[i2] + .... At runtime the interpreter joins the same string; statically it's just integers. Defining a in 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.

  2. Step 2Recover the password
    Walking the index expression for the credential variable spells happychance. That string is what flag.txt.enc was encrypted with.
    Learn more

    Specifically, the script's password variable is assigned something like password = a[i0] + a[i1] + ... . Walking the index list against your a array produces h-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.

  3. Step 3Decrypt the flag
    Run the script with the recovered password, then verify the output starts with picoCTF{ before submitting.
    python
    python3 bloat.flag.py | tee output.txt
    bash
    # enter: happychance
    bash
    sed -n '2p' output.txt
    bash
    grep -oE 'picoCTF\{[^}]+\}' output.txt
    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. The grep -oE regex is a robust alternative to sed -n '2p' when the output ordering shifts.

    tee writes 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.

Flag

picoCTF{d30bfu5c4710n_f7w_b80...}

Never run opaque scripts blindly-printing the decoded payload first keeps you safe and shows the password immediately.

Want more picoCTF 2022 writeups?

Useful tools for Reverse Engineering

Related reading

Do these first

What to try next