FactCheck

Published: April 3, 2024

Description

This binary is putting together some important piece of information... Can you uncover that information? Examine this file. Do you understand its inner workings?

Static reversing

Download the binary and review it in Ghidra or objdump.

Pay special attention to the std::basic_string constructor calls and the subsequent comparisons before characters are appended.

wget https://artifacts.picoctf.net/c_titan/187/bin && \
chmod +x bin && \
objdump -D bin | less

Solution

  1. Step 1Map each DAT entry
    In Ghidra, rename the DAT_ addresses (char_0, char_a, etc.) to their literal characters. This makes the later logic much easier to read.
    Learn more

    Ghidra is a free, open-source reverse engineering framework developed by the NSA. Its decompiler translates machine code back into pseudo-C, making binary analysis accessible without reading raw assembly. Ghidra can analyze binaries for x86, ARM, MIPS, PowerPC, and dozens of other architectures.

    When Ghidra encounters global variables without debug symbols, it names them with their memory addresses: DAT_00401234. Renaming variables to meaningful names is a core reverse engineering workflow. As you identify what each address holds (a character, a string, a counter), renaming it propagates throughout the decompiled view, transforming unreadable code into something approaching the original source.

    C++ std::basic_string (the backing type for std::string) appears frequently in decompiled C++ output. The constructor calls are verbose in decompiled code because string construction involves memory allocation, length tracking, and null termination. Recognizing this pattern and focusing on what values are being appended - rather than the construction mechanics - is key to efficiently reading C++ reverse engineering output.

    This annotation workflow is the same technique used by professional malware analysts who reverse-engineer obfuscated code. Starting with the first few identifiable symbols (strings, API calls, magic constants), analysts build outward, renaming and retyping until the full picture becomes clear. Tools like IDA Pro and Binary Ninja offer similar renaming and retyping capabilities.

  2. Step 2Evaluate the conditions
    Walk through the comparisons: '5' < 'B' succeeds so 'e' is appended, '6' != 'A' adds '9', the difference check fails, and the trailing lines append d a 2 c 0 e }.
    Learn more

    The binary builds the flag string conditionally by evaluating comparisons between character literals and appending different characters based on whether each comparison succeeds or fails. This is a classic obfuscation technique: instead of storing "picoCTF{...}" as a plain string (visible with strings), the binary assembles it at runtime through a series of conditional branches.

    Character comparisons in C use ASCII values. '5' is 0x35 (53) and 'B' is 0x42 (66), so '5' < 'B' is true. This numeric comparison approach means the outcome of each branch is predictable from the ASCII table - no runtime input is needed, making this static analysis sufficient to recover the flag without running the binary.

    This technique of hiding strings by building them character-by-character at runtime is widely used in malware to evade signature-based detection. Antivirus tools that scan for known bad strings won't find them if the strings are never stored completely in the binary. More advanced obfuscators XOR encode strings and decrypt them only when needed, requiring dynamic analysis to observe the decrypted values.

  3. Step 3Concatenate
    Combine the hard-coded prefix picoCTF{...}.
    Learn more

    The flag prefix picoCTF{wELF_d0N3_mate_ contains a pun: ELF (Executable and Linkable Format) is the binary format used by Linux executables, shared libraries, and core dumps. "Well done, mate" becomes "wELF d0N3 mate" - a nod to the fact that you've successfully reversed an ELF binary.

    ELF files have a well-defined structure: an ELF header, program headers (segments for runtime loading), section headers (sections for linking), and the actual data. The readelf command-line tool and tools like pwntools' ELF() class provide programmatic access to ELF internals - essential for binary exploitation and patching.

    The suffix e9da2c0e is a hash-like value unique to your downloaded binary, as the flag note mentions. This per-instance randomization is a common CTF technique to prevent flag sharing between players - each download has a different suffix, so the answer you'd get may differ from others. It reinforces the importance of working through the analysis yourself rather than copying answers.

    Assembling the flag from static analysis demonstrates that you can recover secrets from a binary without ever running it. This is the essence of static reversing - useful when the binary is for a different architecture, requires unavailable hardware, or is suspected malware that you don't want to execute.

Flag

picoCTF{...}

Note that this flag will be different for you based on your downloaded binary.

Want more picoCTF 2024 writeups?

Useful tools for Reverse Engineering

Related reading

What to try next