intermediatecat/stego~2 min read

Deep LSB Steganography & Bitplane Extraction

Extract hidden messages and binary payloads from image color channels using custom Python PIL/OpenCV bit-shifting scripts.

// prerequisite reading

Least Significant Bit (LSB) Deep Dive

In 24-bit RGB images, each pixel consists of three 8-bit color channels: Red, Green, and Blue ($0$ to $255$).

An 8-bit binary value represents a channel:

$$\text{Pixel Red Channel} = 1101011\mathbf{0}2 \quad (214{10})$$

Changing only the least significant bit (bit 0) alters the integer value to $215_{10}$. This $1/256$-th variation in color intensity is completely imperceptible to human vision.


Bitplane Extraction Principles

An 8-bit image channel contains 8 distinct bitplanes (Bit 0 to Bit 7).

  • Bitplane 7 (MSB): Contains high-level image structure and visual features.
  • Bitplane 0 (LSB): Contains fine noise, where steganographic data is embedded.
Bitplane 7 (MSB) -> [ 1 ] 1 0 1 0 1 1 0 (Visual structural data)
...
Bitplane 0 (LSB) -> 1 1 0 1 0 1 1 [ 0 ] (Steganographic payload location)

Python LSB Extraction Script (PIL / NumPy)

When standard GUI tools like StegSolve do not extract custom bit orderings, write a Python script to iterate over pixel coordinates:

from PIL import Image

def extract_lsb(image_path, bit_index=0, channel=0):
    img = Image.open(image_path).convert('RGB')
    width, height = img.size
    
    extracted_bits = []
    
    # Iterate row by row (x, y) or column by column
    for y in range(height):
        for x in range(width):
            pixel = img.getpixel((x, y))
            channel_val = pixel[channel] # 0=Red, 1=Green, 2=Blue
            
            # Extract specific bit using bitwise AND
            bit = (channel_val >> bit_index) & 1
            extracted_bits.append(str(bit))
            
    # Convert bits array to characters
    bit_string = "".join(extracted_bits)
    bytes_list = [int(bit_string[i:i+8], 2) for i in range(0, len(bit_string), 8)]
    
    return bytes(bytes_list)

# Usage example: Extract LSB of Red Channel
output_data = extract_lsb("stego_challenge.png", bit_index=0, channel=0)
with open("extracted_flag.bin", "wb") as f:
    f.write(output_data)

print("Extraction complete! Check extracted_flag.bin")

Common Bitplane Combinations in CTFs

  1. RGB LSB Interleaved: Read Bit 0 of R, then G, then B sequentially for each pixel.
  2. Column-Major Order: Loop for x inside for y vs for y inside for x.
  3. Inverted Bit Plane: Bits embedded in MSB (Bit 7) instead of LSB.

Automated CLI Suite

# zsteg checks all 256 permutations of channels and bitplanes automatically
zsteg -a stego_challenge.png

# Extract specific channel bitplane (e.g., b1,bgr,lsb,xy)
zsteg -E "b1,bgr,lsb,xy" stego_challenge.png > payload.bin