Description
A seemingly harmless "Country Flags" gallery hides a covert message from the Upanzi Network. Inspect the list of flags, identify the odd entry, and extract the hidden data from its PNG.
Spin up the challenge instance and browse the provided gallery URL.
Pull the page source with curl, then grep it for flags/ references to enumerate every linked image and spot the odd one.
Download the suspicious PNG and confirm it's a real image with file before running stego decoders.
curl http://standard-pizzas.picoctf.net:56409/ -o index.htmlgrep -oE 'flags/[a-zA-Z]+\.png' index.html | sort -uwget http://standard-pizzas.picoctf.net:56409/flags/upz.pngfile upz.pngSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Spot the rogue flagObservationI noticed the gallery lists country-flag images where every filename follows a standard three-letter country code pattern, which suggested that grepping the raw page source for image references would quickly surface any outlier slug that doesn't belong to a real country.Grep the page source for everyflags/<name>.pngreference. The output is mostly country codes -usa.png,fra.png,jpn.png, etc. - and one outlierupz.png. Upanzi is a fictional CyLab Africa nation, so its image is the embedded clue. Download it and run a stego decoder.bashgrep -oE 'flags/[a-zA-Z]+\.png' index.html | sort -ubash# Output mostly country codes; upz.png stands out.bashwget http://standard-pizzas.picoctf.net:56409/flags/upz.pngbashfile upz.pngbash# Should report: PNG image data, NNNxNNN, 8-bit/color RGBA, non-interlacedExpected output
upz.png: PNG image data, 800 x 533, 8-bit/color RGBA, non-interlaced
What didn't work first
Tried: Manually scrolling through the rendered gallery page looking for a visually different flag image.
The rogue image upz.png is styled and sized identically to every other flag in the gallery, so it is not visually distinguishable. Grepping the raw HTML source for the image filenames exposes the non-country-code slug upz instantly, which visual inspection of the rendered page cannot do.
Tried: Running
strings upz.pngto look for the flag directly in the binary.LSB steganography distributes the payload across the least significant bits of individual pixel channels, not as a contiguous ASCII string in the file. The
stringscommand only finds sequences of printable characters 4+ bytes long that are stored consecutively in the binary, so it will miss the LSB-encoded payload entirely. A dedicated LSB decoder like stepic or zsteg is required to reconstruct the bit stream.Learn more
OSINT (Open Source Intelligence) and visual reconnaissance are important first steps in CTF challenges. When presented with a list of items, security researchers learn to look for anomalies - entries that don't belong, slightly misspelled names, unusual ordering, or references to fictional entities. "Upanzi" is a fictional African nation referenced in cybersecurity educational contexts, making it immediately suspicious in a list of real countries.
Viewing page source is a fundamental web security technique. HTML comments, hidden form fields, unusual script tags, metadata, and data attributes often contain information that is not visible in the rendered page. Developers sometimes leave debug information, internal API endpoints, or - as in this challenge - clues to hidden functionality directly in the source code.
The gallery structure is a common steganography delivery mechanism: embed a secret-carrying image among many innocuous images so that a casual observer sees only a normal image gallery. Finding the odd one out requires enumeration, which is why tools like
curlcombined with text search (grep) are useful for quickly scanning all entries programmatically rather than reading them visually.Step 2
Install StepicObservationI noticed the challenge title 'flags-are-stepic' is a pun on the Python LSB steganography library stepic, which suggested that this specific library was used to embed the payload and that installing it would give me the matching decoder.Stepic is the Python LSB stego library the challenge name riffs on. Create a virtualenv andpip install stepic(Pillow comes in as a dependency, so you don't need to install it separately). If install fails, fall back tozsteg upz.png.pythonpython3 -m venv venv && source venv/bin/activatebashpip install stepic # pulls in Pillow automaticallyWhat didn't work first
Tried: Running
pip install stepicglobally without a virtualenv and then callingstepicas a shell command.Stepic installs as a Python library, not a standalone shell binary. Without the virtualenv activated, the
stepiccommand may not be on PATH. The correct invocation ispython3 -m stepicorstepiconly after activating the virtualenv where it was installed.Learn more
LSB steganography (Least Significant Bit) hides data by replacing the lowest-order bit of each color channel in every pixel with bits from the secret message. The change is visually imperceptible because flipping the LSB changes a pixel's color value by only 1 out of 255. A red pixel at value 200 (11001000) becomes 201 (11001001) - completely indistinguishable to the human eye.
Stepic is a Python library that encodes and decodes messages hidden in PNG images using LSB steganography. The
-dflag (decode) reads the LSB of each channel pixel by pixel, reconstructs the binary stream, and interprets it as text. Note: Stepic encodes into the alpha channel when present, so an RGBA PNG can carry payload bits that an RGB-only decoder will miss entirely. zsteg's bit-plane brute force handles both. It is a straightforward implementation that does not use passwords or additional encoding, making it easy to use but also easy to detect with forensic tools.Other popular LSB stego tools include zsteg (Ruby, scans multiple bit planes and color channel combinations), StegSolve (Java GUI tool that visualizes individual bit planes), and steghide (supports password-protected embedding in JPEG and BMP files). When a challenge does not specify which tool was used, zsteg is often the best first choice because it automatically tries many configurations.
Step 3
Decode the PNGObservationI noticed that upz.png was confirmed as a valid 8-bit/color RGBA PNG by thefilecommand, and that the challenge name pointed directly to stepic as the encoder, which suggested running stepic in decode mode to extract the LSB-embedded payload from the RGBA pixel data.Run Stepic in decode mode against upz.png. It walks each pixel's low bits and prints the embedded ASCII flag. If the default zsteg fallback finds nothing, brute-force bit depths withzsteg -b. For paranoia,stegoVeritas upz.png --allruns statistical tests to confirm LSB embedding before you keep digging.bashstepic -d -i upz.pngbash# Fallback if Stepic isn't installed:bashzsteg upz.pngbash# zsteg tries multiple bit planes and encodings; if default misses, brute-force:bashzsteg upz.png -b 1,2,3,4What didn't work first
Tried: Using steghide to decode the PNG with
steghide extract -sf upz.png.Steghide only supports JPEG and BMP carrier formats, not PNG. Running it against upz.png produces an error about an unsupported file format. The challenge uses Stepic, which encodes LSB payloads specifically into PNG pixel channels. Use stepic or zsteg instead.
Tried: Running
stepic -e -i upz.png(encode flag) instead ofstepic -d -i upz.png(decode flag).The
-eflag encodes a new message into the image and requires a message argument, so it will prompt for input or error out. The correct flag is-dfor decode, which reads the existing LSB payload out of the image. Mixing up encode and decode is common when first learning a tool's CLI.Learn more
The PNG format is particularly well-suited for steganography because it uses lossless compression. JPEG images use lossy compression, which discards fine-grained pixel differences during encoding - destroying LSB-embedded data in the process. PNG preserves every pixel value exactly, so LSB payloads survive saving and sharing. This is why most LSB steganography challenges use PNG or BMP files.
Detecting LSB steganography statistically is possible through histogram analysis: LSB embedding creates subtle patterns in the distribution of pixel values that deviate from natural images. Tools like StegoVeritas and academic tools like StegExpose perform these statistical tests to detect the presence of hidden data without knowing the message content. In practice, simple single-bit LSB embedding is easily detected; more sophisticated algorithms like F5 and JPEG steganography with perceptual modeling are harder to detect.
This challenge is a great introduction to the broader field of digital watermarking and information hiding, which has legitimate applications in copyright protection, covert communications research, and digital forensics (detecting when images have been modified or contain hidden content).
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{fl4g_h45_fl4ga66...}
Any other LSB steganography decoder (zsteg, StegSolve, etc.) works too; the payload is short plaintext.