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.
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 1Extract the Base64 payload
ObservationThe carrier is a lossless PNG and the setup scan already flagged odd LSB content. zsteg sweeps the bit planes automatically and surfaces the payload without 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 2Decode to text
ObservationOn the b1,rgba,lsb,xy line, zsteg shows a long alphanumeric string ending in == padding. That is Base64; decode it.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 prints lines of text and metadata around the payload, so piping all of it into a decoder gives garbage or an error. Pull out just the Base64 token first. The -E flag in the second command isolates the raw bytes from that one bit plane.
Tried: Using base64 -d with the encoded string pasted in via echo instead of a here-string
echo appends a newline, which the decoder usually tolerates, but copy-paste can also slip spaces or breaks inside the token and those fail mid-decode. A here-string passes the value with no trailing newline. If you hit an invalid-character error, run the token through cat -A to see the 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.