Description
Here's two images that are meant to be combined to get the flag. Can you do it?
Setup
Download s1.png and s2.png from the challenge page.
Inspect both files to confirm their pixel format before any arithmetic.
wget <url>/s1.pngwget <url>/s2.pngfile s1.png s2.pngSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Combine the two imagesObservationI noticed the challenge provided two separate PNG files (s1.png and s2.png) that are described as needing to be 'combined,' which is a hallmark of visual secret sharing where neither share reveals anything alone and pixel-wise arithmetic on both shares recovers the hidden flag.This is visual secret sharing - neither image alone reveals anything meaningful. The shares are typically generated asshare = secret - noise(withnoisebeing 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.pythonpython3 << '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") EOFExpected 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 pixel values produces a third noise-like image rather than the flag because these shares were generated by subtracting a random noise image from the secret, not by XOR-masking it. ImageChops.add() reconstructs the original by reversing that subtraction. XOR-based reconstruction only works when the shares were created with XOR (common in binary black-and-white secret sharing), not the additive scheme used 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 generated to look like uniformly random pixel noise, so no flag text or pattern is visible in either image alone. This is the security guarantee of visual secret sharing - information-theoretically, a single share reveals nothing about the secret. Both shares are required and must be arithmetically combined 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.
ImageChopsprovides channel-wise arithmetic operations on images. Theconvert("RGB")call ensures both images are in the same color mode before arithmetic - mixing modes (e.g., RGBA + RGB) would raise an error. Runfile s1.png s2.pngfirst 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.
Step 2
View the resultObservationI noticed the Python script saved combined.png without errors, which meant the pixel-arithmetic reconstruction succeeded and the flag text embedded in the visual secret share scheme would now be readable in that output image.Open combined.png in any image viewer. The flag text is now visible in the reconstructed image.bashxdg-open combined.pngLearn more
xdg-openis a Linux command that opens a file with the default application for its type - similar to double-clicking in a file manager. For a.pngfile it will launch your default image viewer (GNOME Photos, Eye of GNOME, etc.). On macOS the equivalent isopen combined.png, and on Windows you can usestart 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 viascpand 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.