Investigative Reversing 2 picoCTF 2019 Solution

Published: April 2, 2026

Description

Three files: mystery binary, mystery.png, and mystery2.png. The binary encodes data across both images.

Download all three files.

bash
wget <url>/mystery
bash
wget <url>/mystery.png
bash
wget <url>/mystery2.png
bash
chmod +x mystery
  1. Step 1Run the binary on both images
    Execute mystery with both PNG files as arguments. The binary modifies both images to split the encoded flag between them, or it encodes one byte per image alternately.
    bash
    ./mystery mystery.png mystery2.png
    Learn more

    When a challenge involves multiple images, the flag is often split: even-indexed bytes go to image 1 and odd-indexed bytes go to image 2, or the images are XOR'd together to reveal the flag. Analyze the binary to determine the exact scheme.

  2. Step 2Decompile to understand the two-image encoding
    In Ghidra, find how the binary processes both images. Note whether it interleaves flag bytes between the two images, XORs values from both, or uses one image for the odd bytes and one for even bytes.
    bash
    ghidra mystery &
    Learn more

    A common two-image technique: store the first half of the flag in image 1 and the second half in image 2. Another: alternate bytes - byte 0 in image 1, byte 1 in image 2, byte 2 in image 1, etc. The extraction script must match the encoding order exactly.

  3. Step 3Extract and combine the flag
    Write a Python script to extract the data from both output images and combine them in the correct order to reconstruct the complete flag.
    python
    python3 << 'EOF'
    from PIL import Image
    
    img1 = Image.open('mystery.png')
    img2 = Image.open('mystery2.png')
    
    pixels1 = list(img1.getdata())
    pixels2 = list(img2.getdata())
    
    # Extract from both (example: interleaved bytes)
    flag = ''
    for i in range(min(200, len(pixels1), len(pixels2))):
        b1 = (pixels1[i][2] & 0x0F)  # low nibble of blue channel in img1
        b2 = (pixels2[i][2] & 0x0F)  # low nibble of blue channel in img2
        char = (b1 << 4) | b2
        if char == 0:
            break
        flag += chr(char)
    
    print(flag)
    EOF
    Learn more

    When extracting nibbles (4-bit halves) instead of full bytes, each pixel encodes half a character. Two pixels together encode one character. Always read the Ghidra decompilation to confirm the exact bit layout.

Flag

picoCTF{...}

The flag is split between two images - extract the encoded halves from each PNG and combine them to reconstruct the flag.

Want more picoCTF 2019 writeups?

Useful tools for Forensics

Related reading

What to try next