Bbbbloat picoCTF 2022 Solution

Published: July 20, 2023

Description

A heavily bloated binary makes static analysis tedious - there are many irrelevant functions. But it boils down to one integer comparison: find the magic value and enter it as input.

Ghidra quickly identifies the magic constant in main() even through all the bloat.

Download the binary and make it executable.

Open in Ghidra (or run in the picoCTF web shell), find main(), and read the integer comparison.

bash
wget https://artifacts.picoctf.net/c/172/bbbbloat && chmod +x bbbbloat

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1
    Identify the magic constant in Ghidra
    Observation
    I noticed the binary was called 'bbbbloat' and the description warned about heavy bloat, which suggested that static analysis with Ghidra would be more reliable than manual inspection, and that tracing the scanf input forward to the single integer comparison in main() was the fastest path to the magic constant.
    Open the binary in Ghidra, navigate to the main function, and look for where your scanned input is compared to a constant. The comparison is if (input == 0x86187). In decimal that is 549255.
    bash
    wget https://artifacts.picoctf.net/c/172/bbbbloat && chmod +x bbbbloat
    bash
    # Open in Ghidra, run auto-analysis, find main(), read the if-check value

    Expected output

    picoCTF{cu7_7h3_bl047_695...}
    What didn't work first

    Tried: Run 'strings bbbbloat | grep picoCTF' hoping to find the flag or the magic number without opening Ghidra.

    strings only extracts printable ASCII sequences and won't decode the integer constant 0x86187 into a meaningful hint - it appears as raw bytes, not a labeled comparison. Ghidra's decompiler is needed to trace the data flow from scanf to the cmp instruction and read the constant in context.

    Tried: Grep every function in Ghidra for 'if' statements rather than tracing input flow forward from the scanf call.

    The bloat functions contain dozens of if statements and branches that do not involve user input at all. Randomly auditing them wastes time. The correct approach is to start at the scanf call, find the variable it writes to, and follow its use - Ghidra's 'Find References to Address' on that variable immediately shows the single cmp that matters.

    Learn more

    The binary uses scanf to read an integer from the user and then compares it against a hardcoded constant. All the surrounding function calls are irrelevant bloat that does not affect the outcome. Ghidra's decompiler folds through constant propagation and shows the comparison plainly once you find main().

    Hex to decimal: 0x86187 = 8*65536 + 6*4096 + 1*256 + 8*16 + 7 = 549255. In Ghidra, right-click the hex constant in the decompiler and pick "Convert to Decimal" to avoid doing the arithmetic by hand.

    Heuristic for spotting bloat in the decompiler: long chains of nested function calls whose return value is discarded; loops with no observable side effect; functions that rebuild data they already have. Skip past those and look at where the input flows: scanf to a variable to a cmp. Trace forward from the input read; ignore everything else.

  2. Step 2
    Enter the magic value to get the flag
    Observation
    I noticed Ghidra's decompiler revealed a hardcoded constant 0x86187 compared directly against user input, which meant converting that hex value to decimal (549255) and piping it into the binary would satisfy the comparison and unlock the flag output.
    Run the binary normally and enter 549255 (0x86187) when prompted. It prints the flag.
    bash
    echo '549255' | ./bbbbloat
    What didn't work first

    Tried: Enter the hex value 0x86187 directly at the prompt instead of the decimal equivalent 549255.

    The binary reads input with scanf using a decimal integer format specifier (%d), so it interprets '0x86187' as the decimal number 0, not 549255. The comparison then fails and the binary prints the wrong-answer path. Always convert the Ghidra hex constant to decimal before providing it as input.

    Tried: Run the binary without piping input and type the value interactively, then copy the flag from the terminal - but accidentally enter a space or newline before the digits.

    scanf with %d skips leading whitespace, but if you accidentally enter a non-digit character first, scanf leaves the buffer in a bad state and the read returns 0 or the last parsed value. Using 'echo '549255' | ./bbbbloat' eliminates interactive input errors by feeding exactly the right bytes with a single trailing newline.

    Learn more

    Piping the value directly via echo 'VALUE' | ./binary is a quick way to automate providing input without interactive prompts. The binary reads from stdin, and the pipe connects stdout of echo to stdin of the binary.

    This challenge teaches that code complexity (bloat) is not the same as security. True security requires cryptographic key material or secrets that cannot be recovered from static analysis - not just confusing code structure. A hardcoded comparison value is always recoverable with Ghidra.

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.

Flag

Reveal flag

picoCTF{cu7_7h3_bl047_695...}

Ghidra reveals the magic comparison value (549255 / 0x86187). Enter it when prompted and the binary prints the flag.

Key takeaway

Obfuscation through code bloat is security theater: adding irrelevant functions and dead code slows a human reader but does nothing to prevent static analysis tools like Ghidra from decompiling the binary and revealing every constant and branch condition. Any secret hardcoded into a binary, whether a password, a magic number, or an encryption key, can be recovered by anyone with the binary and a decompiler. Real software security never relies on keeping code structure secret; it relies on cryptographic secrets that are not present in the binary at all.

Related reading

Want more picoCTF 2022 writeups?

Tools used in this challenge

What to try next