Description
Milk... Slap... Do the words make sense to you? Download milkslap.png.
Setup
Open the challenge URL in a browser to find the embedded image.
# Open the challenge URL and view source / Network tabSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Discover the embedded image from the page
ObservationThe description says to download milkslap.png but links to a web page rather than the file. So the PNG is a sub-resource inside the page's markup, and the source or the Network tab holds its real URL.Open the page and either view source or open DevTools Network tab. The image URL appears as an <img src="..."> reference (or as a background-image in CSS). Pull it down with wget so you can run tools on the file directly.bashcurl -s <CHALLENGE_URL> | grep -Eo 'src="[^"]+\.png"' | sort -ubashwget <DISCOVERED_PNG_URL> -O milkslap.pngbashfile milkslap.pngExpected output
milkslap.png: PNG image data, 1280 x 720, 8-bit/color RGB, non-interlaced
What didn't work first
Tried: Navigate directly to the challenge URL and assume the page itself is the downloadable PNG, saving the HTML response as milkslap.png.
The challenge URL returns an HTML page, not the image binary: file reports an HTML document and zsteg refuses to parse it. The PNG loads as a sub-resource from the page's markup, so grep the HTML for the img src attribute to find the real URL before downloading.
Tried: Skip the page inspection and guess the PNG URL by appending /milkslap.png to the challenge base URL.
Without the page source or the DevTools Network tab, the sub-resource path is unknown. Guessing common suffixes usually gives a 404, and even a matching filename fails if the subdirectory differs. Grepping the markup extracts the exact src value the server actually serves.
Learn more
Why scan the page first. picoCTF stego challenges often hide the actual image one or two levels into the HTML rather than in a download link.
view-source:in the browser address bar, orcurl <url>piped throughgrep, reveals every resource the page loads. CSS background-images and lazy-loadeddata-srcattributes are common tricks - check both.Step 2Run zsteg to detect LSB steganography
ObservationThe image is a plain PNG with no password prompt anywhere. That reads as LSB steganography in the pixel data, and zsteg scans every bit plane and channel ordering automatically.Use zsteg on the image to automatically detect and extract data hidden in the least significant bits of the image pixels. The flag is hidden using LSB steganography.bashzsteg milkslap.pngbashzsteg -a milkslap.png # exhaustiveExpected output
b1,r,lsb,xy .. text: "picoCTF{imag3_m4n1pul4t10n_sl4p5}"What didn't work first
Tried: Run steghide extract -sf milkslap.png on the image expecting to recover the flag that way.
steghide supports JPEG, BMP, WAV, and AU carriers only, and rejects a PNG outright as an unsupported format. LSB encoding in a PNG is a different technique anyway: steghide picks its embedding positions with a graph-matching algorithm and, on JPEG, tweaks DCT coefficients rather than walking bit planes in order. zsteg is built for PNG and BMP LSB analysis across every channel and bit-plane combination.
Tried: Run strings milkslap.png hoping the flag appears as a plain ASCII run inside the file.
strings looks for contiguous printable ASCII in the raw bytes, but LSB steganography spreads each bit of the message across the least significant bit of successive pixel bytes. The flag characters are never contiguous, since seven bits of surrounding pixel data sit between each one, so strings finds nothing. zsteg reassembles those scattered bits in order.
Learn more
LSB steganography (Least Significant Bit) hides data by replacing the lowest bit of each pixel color channel with a bit of the secret message. Because the change is at most 1 step across the 256 possible values of each 8-bit channel, it is virtually invisible to the human eye but detectable with the right tools.
zsteg is a Ruby-based tool specifically designed to detect various steganographic methods in PNG and BMP files. It tries multiple combinations: different bit planes (LSB, bit 1, bit 2...), different color channels (R, G, B, A), different byte orderings, and both row and column scans. Its output shows detected data along with the method used to extract it.
If zsteg is not available, you can use stegsolve (a Java application) to manually examine different bit planes visually. Alternatively,
steghidehandles JPEG, BMP, WAV, and AU carriers. For a purely manual approach, a Python script using PIL can extract LSB values:pixel[0] & 1gives the red channel LSB of each pixel.
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{imag3_m4n1pul4t10n_sl4p5}
The flag was hidden in the least significant bits of the image's pixel data - a classic LSB steganography technique that is imperceptible to the naked eye.