RED picoCTF 2025 Solution

Published: April 2, 2025

Description

A bright red PNG hides an obvious poem plus a suspicious Base64 blob in the LSB plane. Run an LSB scanner, grab the repeating string, and decode it to recover the flag.

Confirm the file is a real PNG with file and pngcheck. pngcheck -v should report OK for every chunk; if it ever says extra bytes after IEND, the payload is appended data and you'd switch to binwalk instead of zsteg.

Install zsteg (Ruby + gem install zsteg) so you can scan the RGBA bit planes quickly. zsteg is the right tool here because the carrier is a PNG with LSB Stepic encoding; steghide handles JPEG, binwalk handles appended files, and stegsolve is a slower GUI bit-plane viewer.

Run zsteg on the PNG and look for an entry like b1,rgba,lsb,xy ... text: "<base64>". The longest decodable text line is the payload.

bash
file red.png
bash
pngcheck -v red.png
bash
binwalk red.png  # quick sanity check for appended files
bash
sudo apt install ruby ruby-dev
bash
sudo gem install zsteg
bash
zsteg red.png

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
Two posts cover this exact path. The Steganography Tools intro spells out when to reach for zsteg, steghide, or binwalk, and the CTF Steganography guide walks the LSB plane theory in depth.
  1. Step 1
    Extract the Base64 payload
    Observation
    I noticed the carrier is a lossless PNG with suspicious LSB content flagged in the setup scan, which suggested zsteg's automated bit-plane sweep would surface the hidden payload in the b1,rgba,lsb,xy channel without needing manual guesswork.
    zsteg prints the flag repeated several times in the LSB plane (the encoder pads the carrier, which is what produces the duplication). Just take the first complete picoCTF{...} substring from the b1,rgba,lsb,xy line; the duplicates can be ignored.
    Learn more

    LSB (Least Significant Bit) steganography hides data in the lowest-order bit of each color channel in an image. A pixel's RGB values each contribute one bit of hidden data, so a 1-megapixel image can hide approximately 375 kilobytes of payload while remaining visually indistinguishable from the original - the color difference caused by flipping a single bit is below human perception threshold.

    zsteg is a Ruby tool that systematically tests all combinations of bit planes (bits 1 through 8 of each channel), channel combinations (R, G, B, A and their combinations), and byte orderings (LSB-first vs. MSB-first, row-by-row vs. column-by-column). The b1,rgba,lsb,xy notation reads as: bit plane 1 (the least-significant bit) of the RGBA channels, in least-significant-bit order, scanning x then y (left-to-right then top-to-bottom). That is exactly what the Stepic library used to encode this PNG, so zsteg's default scan finds the payload on the first try without any flags.

    Other steganography detection tools serve different carriers: steghide handles password-protected JPEG/BMP embedding, stegsolve is a GUI bit-plane viewer that is slower than zsteg's automated scan, and binwalk detects appended files or embedded archives. The choice of tool depends on the file format and embedding technique suspected.

  2. Step 2
    Decode to text
    Observation
    I noticed zsteg's output contained a long alphanumeric string ending with '==' padding on the b1,rgba,lsb,xy line, which are classic Base64 indicators and suggested running base64 -d to recover the plaintext flag.
    Use CyberChef's From Base64 recipe or Linux's base64 -d tool to decode the string into ASCII. The plaintext is already formatted as a picoCTF flag.
    bash
    base64 -d <<< 'cGljb0NURntyM2RfMXNfdGgzX3VsdDFtNHQzX2N1cjNfZjByXzU0ZG4zNTVffQ=='
    bash
    # Or pull from zsteg directly:
    bash
    zsteg -E b1,rgba,lsb,xy red.png | base64 -d 2>/dev/null | head

    Expected output

    picoCTF{r3d_1s_th3_ult1m4t3_cur3_f0r_54dn355_}
    What didn't work first

    Tried: Piping the full zsteg output directly into base64 -d without isolating the encoded string

    zsteg outputs multiple lines of plain text and metadata before and after the Base64 payload, so piping the whole output into base64 -d produces garbage or a decode error. You need to extract only the Base64 token - the long alphanumeric string ending with == - before decoding. The second command in this step shows the correct extraction flag (-E b1,rgba,lsb,xy) to isolate just the raw bytes from that specific plane.

    Tried: Using base64 -d with the encoded string pasted in via echo instead of a here-string

    echo adds a trailing newline to its output, and while base64 -d is usually tolerant of this, some shell encodings or copy-paste artifacts introduce spaces or line breaks inside the token that cause an 'invalid input' error mid-decode. The <<< here-string operator passes the value without an appended newline and avoids that fragility. If you see 'invalid character' errors, inspect the token with cat -A to reveal hidden whitespace.

    Learn more

    Finding the same Base64-encoded flag repeated multiple times in the LSB data reveals that the challenge author embedded enough copies to ensure retrieval even if some LSB bits are corrupted by image processing (like JPEG re-encoding). PNG is lossless, so corruption is not an issue here, but the repetition also makes the payload easy to spot - any LSB scanner that finds a long run of the same pattern flags it immediately.

    The base64 -d command (or base64 --decode) reads Base64 input and writes raw bytes. The echo approach works for single-line strings, but for multi-line or padded input, piping from a file is more reliable: base64 -d < encoded.txt. CyberChef's "Magic" operation can auto-detect encoding schemes and chain decoders, which is useful when the exact encoding isn't immediately obvious.

    From a defensive perspective, LSB steganography is used in the real world for both legitimate purposes (digital watermarking of media to prove ownership or detect leaks) and malicious purposes (hiding malware configuration, command-and-control instructions, or exfiltrated data in innocuous-looking images). Network DLP solutions struggle to detect LSB steganography because the images appear valid and the hidden data has no file signature to match.

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{r3d_1s_th3_ult1m4t3_cur3_f0r_54dn...}

Any LSB tool works; zsteg just makes it a one-command extraction.

Key takeaway

LSB steganography exploits the fact that the least significant bit of each color channel contributes almost nothing to perceived image quality, so swapping those bits to carry a payload produces no visible change. Lossless formats like PNG are ideal carriers because every bit is preserved exactly, while JPEG compression destroys LSB data by design. The same technique appears in real-world digital watermarking to prove media ownership, and in malware that hides command-and-control instructions inside images posted to social media.

Related reading

Want more picoCTF 2025 writeups?

Useful tools for Forensics

Do these first

What to try next