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.
Setup
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.
file red.pngpngcheck -v red.pngbinwalk red.png # quick sanity check for appended filessudo apt install ruby ruby-devsudo gem install zstegzsteg red.pngSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Extract the Base64 payloadObservationI 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 completepicoCTF{...}substring from theb1,rgba,lsb,xyline; 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,xynotation 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.
Step 2
Decode to textObservationI 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'sbase64 -dtool to decode the string into ASCII. The plaintext is already formatted as a picoCTF flag.bashbase64 -d <<< 'cGljb0NURntyM2RfMXNfdGgzX3VsdDFtNHQzX2N1cjNfZjByXzU0ZG4zNTVffQ=='bash# Or pull from zsteg directly:bashzsteg -E b1,rgba,lsb,xy red.png | base64 -d 2>/dev/null | headExpected 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 -dcommand (orbase64 --decode) reads Base64 input and writes raw bytes. Theechoapproach 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.