Challenge Description
This challenge presents us with a PNG image file called rainbow_box.png
. The challenge involves extracting hidden information from the image using bitplane analysis.
Analysis
The key insight is that this is a bitplane steganography challenge where information is typically hidden in the least significant bits of the RGB channels. In bitplane steganography:
- Each RGB channel has 8 bits per pixel (0-7, where 0 is LSB and 7 is MSB)
- Hidden data is typically stored in the lower bitplanes (bits 0-3) to minimize visual impact
- Binary data can optionally be encoded across multiple bitplanes
In this case, checking the bitplanes using any visual tool will clearly show identifiable characters. For example, the LSB for red shows the character M, the first character of the flag.

Solution
Method 1: Using StegOnline (Recommended)
The easiest approach is to use StegOnline, a powerful web-based steganography analysis tool:
- Upload the
rainbow_box.png
file to StegOnline - Navigate to the “Bit Planes” section
- Extract the individual bitplanes for each RGB channel, noting a clearly visible character in each bitplane that is not a MSB
StegOnline provides an intuitive interface for bitplane extraction and makes it easy to identify patterns that form readable text. Just browse each bitplane grabbing each character 1 by 1 to solve this way.
Method 2: Custom Python Script
Alternatively, you can write a custom script to extract the bitplanes. Here’s a Python script that extracts all bitplanes except the MSB and displays them in a single row:
#!/usr/bin/env python3
"""
Extract all bitplanes (except MSBs) from a stego image and display them in black and white.
"""
from PIL import Image
import os
def extract_bitplanes_simple(image_path, exclude_msb=True):
"""Extract all bitplanes from an image and save them as individual files."""
img = Image.open(image_path)
img_array = img.convert('RGB')
pixels = img_array.load()
width, height = img_array.size
output_dir = "extracted_bitplanes"
os.makedirs(output_dir, exist_ok=True)
channel_names = ['R', 'G', 'B']
bitplanes_extracted = 0
for channel_idx, channel_name in enumerate(channel_names):
for bit_idx in range(8):
if exclude_msb and bit_idx == 7:
continue
bitplane_img = Image.new('L', (width, height))
bitplane_pixels = bitplane_img.load()
for y in range(height):
for x in range(width):
pixel = pixels[x, y]
channel_value = pixel[channel_idx]
bit_value = (channel_value >> bit_idx) & 1
bitplane_pixels[x, y] = bit_value * 255
filename = f"{output_dir}/bitplane_{channel_name}{bit_idx}.png"
bitplane_img.save(filename)
bitplanes_extracted += 1
print(f"Saved: {filename}")
print(f"\nExtracted {bitplanes_extracted} bitplanes to '{output_dir}/' directory")
create_summary_image(output_dir, bitplanes_extracted, width, height)
def create_summary_image(output_dir, num_bitplanes, width, height):
"""Create a summary image showing all bitplanes in a single row."""
summary_width = width * num_bitplanes
summary_height = height
summary_img = Image.new('L', (summary_width, summary_height), 255)
for i in range(num_bitplanes):
x = i * width
y = 0
channel_names = ['R', 'G', 'B']
bit_idx = i % 7
channel_idx = i // 7
if channel_idx < len(channel_names):
channel_name = channel_names[channel_idx]
filename = f"{output_dir}/bitplane_{channel_name}{bit_idx}.png"
try:
bitplane_img = Image.open(filename)
summary_img.paste(bitplane_img, (x, y))
except Exception as e:
print(f"Warning: Could not load {filename}: {e}")
summary_path = f"{output_dir}/all_bitplanes_summary.png"
summary_img.save(summary_path)
print(f"Created summary image: {summary_path}")
if __name__ == "__main__":
extract_bitplanes_simple("rainbow_box.png", exclude_msb=True)
