← Back to Writeups
CUSTOMEasySteganography

Классное фото (Cool Photo)

XESXOR9/2/20262 min read
#steganography#custom#easy#lsb_extraction#channel_analysis#visual_encoding#linux

Классное фото (Cool Photo)

Platform: duckerz | Category: Steganography | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-01-09 | Status: Solved Techniques: lsb_extraction, channel_analysis, visual_encoding

Summary

Provided file: class.png (1024x1024 PNG, RGBA)

Классное фото (Cool Photo) — duckerz

Description

"Реально классное фото" (Really cool photo)

Provided file: class.png (1024x1024 PNG, RGBA)

Analysis

Initial Analysis

bash
file class.png
# class.png: PNG image data, 1024 x 1024, 8-bit/color RGBA, non-interlaced

exiftool class.png
# Standard PNG metadata, nothing suspicious

strings class.png | grep -i flag
# No results

PNG Chunk Analysis

Checking the PNG structure revealed only standard chunks:

  • IHDR (header)
  • IDAT (image data)
  • IEND (end marker) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

No additional chunks with hidden data.

LSB Channel Analysis

Key discovery when analyzing LSB (Least Significant Bit) of each color channel:

python
from PIL import Image
import numpy as np

img = Image.open('class.png')
pixels = np.array(img)

# Extract LSB of each channel
r_lsb = (pixels[:,:,0] >> 0) & 1
g_lsb = (pixels[:,:,1] >> 0) & 1
b_lsb = (pixels[:,:,2] >> 0) & 1

# Count unique rows
print(f"R unique rows: {len(set(map(tuple, r_lsb)))}")  # 1024
print(f"G unique rows: {len(set(map(tuple, g_lsb)))}")  # 1024
print(f"B unique rows: {len(set(map(tuple, b_lsb)))}")  # 164 (!!)

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Anomaly detected! The blue channel has only 164 unique rows instead of 1024 — a clear sign of hidden data.

Solution

Extracting Data from Blue LSB

python
#!/usr/bin/env python3
from PIL import Image
import numpy as np

# Load the image
img = Image.open('class.png')
pixels = np.array(img)

# Extract LSB of blue channel
b_lsb = (pixels[:,:,2] >> 0) & 1

# Invert for better visibility (white text on black background -> black on white)
b_lsb_img = ((1 - b_lsb) * 255).astype(np.uint8)

# Save the result
Image.fromarray(b_lsb_img).save('blue_lsb_inverted.png')
print("Saved: blue_lsb_inverted.png")

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Result

The resulting image blue_lsb_inverted.png contains the visually encoded flag — white text on black background (after inversion — black on white):

DUCKERZ{REDACTED}

Notes

Why the Blue Channel?

The blue channel is often used for LSB steganography because the human eye is least sensitive to changes in the blue spectrum.

Wordplay in the Title

"Классное фото" in Russian has a double meaning:

  • "Cool photo" (классное = cool)
  • "Class photo" (классное = of a class)

The flag REDACTED is a leetspeak version of "Really good photo of class", playing on both meanings. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Alternative Tools

  • zsteg — automatic LSB analysis for PNG/BMP
  • stegsolve — GUI tool for channel analysis
  • Stegonline — web tool for steganography ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍