Investigative Reversing 3 picoCTF 2019 Solution

Published: April 2, 2026

Description

Four files. The flag is spread across multiple images with a more complex encoding scheme.

Download all provided files.

bash
wget <url>/mystery
bash
wget <url>/mystery.png
bash
wget <url>/mystery2.png
bash
wget <url>/mystery3.png
bash
chmod +x mystery
  1. Step 1Run the binary and observe output
    Execute the mystery binary with all provided PNG files. Use strace to observe which files are read and written, giving you a map of the encoding process.
    bash
    strace -e trace=open,openat,read,write ./mystery mystery.png mystery2.png mystery3.png 2>&1 | grep -E 'open|write'
    bash
    ./mystery mystery.png mystery2.png mystery3.png
    Learn more

    strace intercepts and records all system calls made by a process. Filtering for file operations (open, write) shows exactly which files the binary reads from and writes to, without needing to fully reverse the code.

  2. Step 2Systematic Ghidra analysis
    Load the binary in Ghidra. Map out the complete data flow: how each image file is opened, which pixel values are read, what math is done to encode each flag byte, and which output file receives the result.
    bash
    ghidra mystery &
    Learn more

    For complex multi-file encoding, document each encoding step as you discover it. Create a table: flag byte index -> source image -> pixel index -> bit positions. This systematic approach prevents confusion when writing the extractor.

  3. Step 3Write the multi-image extractor
    Implement the inverse of each encoding step in Python. Extract partial flag data from each image and assemble them in the correct order.
    python
    python3 << 'EOF'
    from PIL import Image
    
    images = [Image.open(f'mystery{i}.png') for i in ['', '2', '3']]
    pixel_lists = [list(img.getdata()) for img in images]
    
    # Extract flag bytes (adapt to actual encoding from Ghidra)
    flag_bytes = []
    for i in range(50):  # try up to 50 characters
        img_idx = i % len(images)
        px = pixel_lists[img_idx][i // len(images)]
        flag_bytes.append(px[0] & 0x0F)  # example
    
    print(bytes(flag_bytes).decode('ascii', errors='replace'))
    EOF
    Learn more

    Multi-image steganography distributes hidden data across several cover images to increase capacity and reduce the per-image statistical anomaly. Each image only needs to change a few pixels, making detection harder.

Flag

picoCTF{...}

Analyze the binary in Ghidra, then extract partial flag data from each PNG and combine them in the correct order.

Want more picoCTF 2019 writeups?

Useful tools for Forensics

Related reading

What to try next