SteganographyImage Steganography
Pixel Manipulation Steganography
steganographyintermediate
Find flags hidden through pixel value manipulation, palette tricks, and color channel encoding.
steganographypixelspalettergbalphachannelpythonpil
Pixel Manipulation Steganography
Beyond LSB, flags can be hidden through direct pixel value manipulation — specific pixels storing ASCII values, color channel differences encoding data, or palette tricks hiding information in indexed color tables. These techniques require manual analysis with Python since automated tools like zsteg don't cover them.
Analysis Workflow
Reading Pixel Data
from PIL import Image
import numpy as np
img = Image.open('suspicious.png')
print(f"Mode: {img.mode}") # RGB, RGBA, P (palette), L (grayscale)
print(f"Size: {img.size}") # (width, height)
pixels = np.array(img)
print(f"Shape: {pixels.shape}") # (height, width, channels)
px = pixels[0, 0] # top-left pixel
print(f"First pixel: {px}") # [R, G, B] or [R, G, B, A]Color Channel Techniques
Some challenges store the flag directly in pixel R values as printable ASCII:
from PIL import Image
img = Image.open('suspicious.png').convert('RGB')
width, height = img.size
flag = ''
for y in range(height):
for x in range(width):
r, g, b = img.getpixel((x, y))
if 32 <= r <= 126:
flag += chr(r)
print(flag)Also try the Green and Blue channels independently.
Image Difference
Two nearly-identical images where the difference encodes the flag:
from PIL import Image
import numpy as np
img1 = np.array(Image.open('image1.png').convert('RGB'))
img2 = np.array(Image.open('image2.png').convert('RGB'))
diff = np.abs(img1.astype(int) - img2.astype(int))
# Amplify differences for visibility
amplified = np.clip(diff * 50, 0, 255).astype('uint8')
Image.fromarray(amplified).save('diff.png')
# Extract text from diff
for row in diff:
for px in row:
for v in px:
if 32 <= v <= 126:
print(chr(v), end='')Generate All Bit Planes
from PIL import Image
import numpy as np
img = Image.open('suspicious.png').convert('RGB')
pixels = np.array(img)
for channel, name in enumerate(['R', 'G', 'B']):
for bit in range(8):
plane = ((pixels[:,:,channel] >> bit) & 1) * 255
Image.fromarray(plane.astype('uint8')).save(f'{name}_bit{bit}.png')
print("Saved all 24 bit planes")Open each *_bit0.png plane — if bit 0 (LSB) planes look like random static, data is likely hidden there.
Checklist
- Check image mode: RGB, RGBA, P (palette)?
- Extract alpha channel (RGBA): non-255 values → hidden data
- Plot each bit plane: R/G/B × bits 0-7 → visual inspection
- Check palette entries for ASCII-printable values
- XOR R, G, B channels together
- If two images provided: compute difference, amplify
- Scan every pixel's RGB values for printable ASCII
- Aperisolve automates many of these checks
Last updated on