Description
A BMP image with suspicious noise hides a ZIP archive in its red and green color channels. Extract the RGB pixel data, skip the blue and alpha bytes, and unzip the result to find the flag.
Setup
Download the BMP image.
Open it in CyberChef or a hex editor to inspect the file structure and color channels.
Write a Python script to extract the red and green channel bytes from the pixel data.
wget 'https://artifacts.picoctf.net/c/371/Invisible WORDs.bmp'pip3 install pillowSolution
Walk me through it- Step 1Inspect the image and identify the anomalyOpen the image in CyberChef and split into color channels. The red and green channels show obvious noise patterns at the bottom of the image, while the blue channel looks normal. This indicates data hidden in the red and green channels. The file header reveals a 32-bit bit depth, meaning pixels are stored as RGBA (red, green, blue, alpha).python
python3 -c "from PIL import Image; img = Image.open(\"Invisible WORDs.bmp\"); print(img.mode, img.size)"Learn more
BMP files with 32-bit color store pixels as four bytes per pixel: Red, Green, Blue, Alpha (RGBA). The BMP file header at offset 0x0A stores the pixel array start offset. Examining this value reveals where the raw pixel data begins, which helps when working at the byte level without a library.
In CyberChef, the Split Colour Channels operation separates an image into its component channels. Channels carrying hidden data typically appear as random noise or structured patterns that differ from the rest of the image. Clean channels look smooth and gradual.
- Step 2Extract red and green bytes with PythonThe pixel array starts at byte 0x8A in this BMP. Each pixel is 4 bytes: RGBA. To extract the hidden data, read two bytes (red and green) and skip two bytes (blue and alpha) for every pixel until the end of the file. Write the extracted bytes to a new file.python
python3 - <<'PY' with open("Invisible WORDs.bmp", "rb") as f: data = f.read() # BMP pixel data starts at offset stored at bytes 0x0A..0x0D (little-endian) import struct pixel_start = struct.unpack_from("<I", data, 0x0A)[0] # typically 0x8A = 138 out = bytearray() i = pixel_start while i + 3 < len(data): out.append(data[i]) # red out.append(data[i + 1]) # green i += 4 # skip blue and alpha with open("output.bin", "wb") as f: f.write(out) print(f"Extracted {len(out)} bytes to output.bin") PYLearn more
The BMP file format stores the pixel array offset at bytes 0x0A through 0x0D as a little-endian 32-bit integer. For this particular file the value is
0x8A(138 decimal), meaning pixel data starts 138 bytes into the file.RGBA pixel layout means each group of 4 bytes is one pixel: byte 0 is red, byte 1 is green, byte 2 is blue, byte 3 is alpha. Skipping two bytes and reading two is the pattern: read at
iandi+1, then advanceiby 4. - Step 3Identify and unzip the embedded archiveThe extracted binary file is a ZIP archive, but it may be corrupted. Run binwalk to find valid ZIP data inside it, then extract to get a text file. Search the text for the flag.bash
binwalk output.binbashbinwalk -e output.binbashgrep -r 'picoCTF' _output.bin.extracted/Learn more
binwalk scans a binary file for known magic numbers and reports embedded file formats. ZIP archives start with the bytes
PK\x03\x04. Even if the outer file appears broken,binwalk -ecan carve out valid ZIP sections. The extracted archive in this challenge contains a text file, which turns out to be the text of Frankenstein, with the flag hidden inside.After extraction, use
grep -r 'picoCTF'on the extracted directory to find the flag without reading the entire novel.
Flag
picoCTF{w0rd_d0g_y0u_f0und_s0m3th1ng_f1234567}
The flag is embedded in the Frankenstein text file inside the ZIP that was hidden in the red and green color channels of the BMP image.