Skip to main content

Pixelated picoCTF 2021 Solution

Recover a hidden image by combining two seemingly random image shares using visual cryptography.

Published: April 2, 2026Updated: August 13, 2026

Description

Here's two images that are meant to be combined to get the flag. Can you do it?

Download s1.png and s2.png from the challenge page.

Inspect both files to confirm their pixel format before any arithmetic.

bash
wget <url>/s1.png
bash
wget <url>/s2.png
bash
file s1.png s2.png

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
For more steg techniques (LSB, channel splits, secret-sharing variants), see the CTF Steganography guide.
  1. Step 1Combine the two images
    Observation
    The challenge gives two PNGs and says they need combining. That is the hallmark of visual secret sharing: neither share reveals anything alone, and pixel-wise arithmetic across both recovers the flag.
    This is visual secret sharing - neither image alone reveals anything meaningful. The shares are typically generated as share = secret - noise (with noise being random per-pixel), so addition recovers the secret while XOR yields garbage. Use Pillow's ImageChops.add() pixel-by-pixel; if the result is still noise, try ImageChops.logical_xor instead - that signals the shares are XOR masks.
    python
    python3 << 'EOF'
    from PIL import Image, ImageChops
    
    img1 = Image.open("s1.png").convert("RGB")
    img2 = Image.open("s2.png").convert("RGB")
    result = ImageChops.add(img1, img2)
    result.save("combined.png")
    print("Saved combined.png")
    EOF

    Expected output

    Saved combined.png
    What didn't work first

    Tried: Use ImageChops.logical_xor() instead of ImageChops.add() to combine the two shares

    XOR on RGB values produces a third noise-like image rather than the flag, because these shares came from subtracting a random noise image from the secret, not from XOR-masking it. ImageChops.add() reverses that subtraction. XOR reconstruction only works when the shares were built with XOR, which is common in binary black-and-white secret sharing but not the additive scheme here.

    Tried: Open s1.png or s2.png directly in an image viewer hoping to spot the flag in one of the shares

    Each share is built to look like uniform random pixel noise, so neither image shows any text or pattern on its own. That is the security guarantee of visual secret sharing: information-theoretically, one share reveals nothing. Both are needed, and they have to be combined arithmetically before any signal appears.

    Learn more

    Visual secret sharing is a cryptographic technique invented by Moni Naor and Adi Shamir in 1994. In a (2,2) visual secret sharing scheme, a secret image is split into two "shares" - each looks like random noise - but stacking or combining them reveals the original. Neither share alone gives any information about the secret.

    There are two common combination methods depending on how the shares were generated:

    • XOR: each pixel in share 1 is XORed with the corresponding pixel in share 2. Used when shares are binary (black/white) or when you want perfect reconstruction.
    • Addition (saturating): pixel values are added and clamped at 255. ImageChops.add() does this - it's saturating addition (values above 255 clip to 255, not wrap around). This works here because the two shares were generated by subtracting a random noise image from the original.

    Pillow (PIL) is Python's standard image processing library. ImageChops provides channel-wise arithmetic operations on images. The convert("RGB") call ensures both images are in the same color mode before arithmetic - mixing modes (e.g., RGBA + RGB) would raise an error. Run file s1.png s2.png first to confirm the mode (RGB, L, RGBA) so you know what to convert to.

    NumPy fallback. If you prefer raw arrays or want to clip explicitly:

    import numpy as np
    from PIL import Image
    
    a = np.array(Image.open("s1.png").convert("RGB"))
    b = np.array(Image.open("s2.png").convert("RGB"))
    combined = np.clip(a.astype(np.int16) + b.astype(np.int16), 0, 255).astype(np.uint8)
    Image.fromarray(combined).save("combined.png")

    Real-world use: Visual secret sharing is used in physical security schemes where a secret can be revealed by overlaying transparencies, requiring no computer. It's also a foundational concept in threshold cryptography, where n shares are generated and any k of them can reconstruct the secret.

  2. Step 2View the result
    Observation
    The script saved combined.png without errors, so the pixel arithmetic worked. The flag text should be readable in that output image.
    Open combined.png in any image viewer. The flag text is now visible in the reconstructed image.
    bash
    xdg-open combined.png
    Learn more

    xdg-open is a Linux command that opens a file with the default application for its type - similar to double-clicking in a file manager. For a .png file it will launch your default image viewer (GNOME Photos, Eye of GNOME, etc.). On macOS the equivalent is open combined.png, and on Windows you can use start combined.png.

    If you're working on a headless server (no GUI), you can instead use tools like eog, feh, or transfer the file to your local machine via scp and open it there. Alternatively, Python can display the image inline: from PIL import Image; Image.open('combined.png').show().

Interactive tools
  • StegallDrop any file and Stegall runs every applicable steg technique in parallel: LSB sweeps, bit planes, spectrograms, polyglot carving, metadata, whitespace decode, and a 6-layer base/ROT/XOR/zlib cascade. Recursively unpacks results and surfaces flag matches.
  • Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.
  • 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{5ticky_5icky_5ticky_...}

This is visual secret sharing - neither image alone reveals anything, but combining them (XOR / ADD) reconstructs the hidden image.

Key takeaway

Visual secret sharing splits an image into two or more noise-like shares, none of which reveals anything about the original. Recombining them with pixel-wise XOR or addition recovers the secret, an idea rooted in Shamir's threshold scheme generalized to images. The same principle runs multi-party key ceremonies at certificate authorities and in hardware security modules, where no single party holds the whole secret. In a CTF, two images that look like static are a strong signal to try combining them.

Related reading

Useful tools for Cryptography

Where to go next