← Back to Writeups
CUSTOMMediumSteganography

Древнейшая уцуцуга

XESXOR9/10/20263 min read
#steganography#custom#medium#inverted_qr_correction#near_black_pixel_differentiati#pixel_value_amplification#qr_extraction_from_pixel_encod#steghide_password_extraction#linux

Древнейшая уцуцуга

Platform: hackerlab | Category: Steganography | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-04-08 | Status: Solved Techniques: inverted_qr_correction, near_black_pixel_differentiation, pixel_value_amplification, qr_extraction_from_pixel_encoding, steghide_password_extraction

Summary

Task: a JPEG image with 'TRY HARDER' text hides a QR code in near-black pixel values and uses steghide for final flag storage. Solution: amplify dark pixel differences to reveal hidden QR code, invert it for decoding, use the decoded password with steghide to extract the flag.

Древнейшая уцуцуга — Hackerlab

Description

В далёком прошлом, создателей уцуцужных заданий наказывали сожжением на костре. Утешает то, что эти времена уже прошли.

English summary: A ZIP archive contains try_harder.jpg — a 512×512 JPEG showing "TRY HARDER" text with a bearded man on a dark background. The goal is to find the hidden CODEBY{...} flag.

Analysis

Initial Recon

Standard tools (file, exiftool, strings) revealed nothing unusual. No appended data after the JPEG EOI marker, no embedded archives (ZIP/RAR/PNG), no EXIF comments with hints. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Pixel Value Analysis — The Breakthrough

Analyzing the pixel value distribution of the dark background area revealed a critical anomaly:

  • 142,501 pixels had value (3, 3, 3) — the dominant dark background color
  • 4,540 pixels had value (6, 6, 6) — a suspiciously distinct cluster of near-black pixels

Both values are visually indistinguishable to the human eye (both appear as pure black), but the bimodal distribution in the dark region strongly suggested hidden data encoded as a subtle pixel value difference. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

Stage 1 — Revealing the Hidden QR Code

Enhanced the dark pixels by multiplying all pixel values by 50, making the (3,3,3) vs (6,6,6) difference visible:

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

img = Image.open("try_harder.jpg")
arr = np.array(img, dtype=np.float64)

# Amplify all pixel values ×50 to reveal hidden structure
enhanced = np.clip(arr * 50, 0, 255).astype(np.uint8)
Image.fromarray(enhanced).save("enhanced.png")

This revealed a QR code hidden in the dark background — the (6,6,6) pixels formed QR modules against the (3,3,3) background. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Stage 2 — Extracting and Decoding the QR Code

The QR code was approximately 25×25 modules with a 4-pixel module size. Extracted it by thresholding:

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

img = Image.open("try_harder.jpg")
arr = np.array(img, dtype=np.float64)

# Threshold: pixels with average brightness 4.5–8 → black (QR dark module)
avg = arr.mean(axis=2)
qr_mask = (avg > 4.5) & (avg < 8)

# Create binary QR image
qr_img = np.where(qr_mask, 0, 255).astype(np.uint8)
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Crop to QR region (find bounding box of dark pixels)
rows = np.any(qr_mask, axis=1)
cols = np.any(qr_mask, axis=0)
rmin, rmax = np.where(rows)[0][[0, -1]]
cmin, cmax = np.where(cols)[0][[0, -1]]

cropped = qr_img[rmin:rmax+1, cmin:cmax+1]
Image.fromarray(cropped).save("qr_cropped.png")

Standard QR decoders (pyzbar, cv2.QRCodeDetector) initially failed on the extracted image. The reason: the QR code was color-inverted — dark and light modules were swapped relative to the QR standard.

After inverting the colors (black↔white), the QR decoded successfully: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

python
#!/usr/bin/env python3
from PIL import Image, ImageOps
from pyzbar.pyzbar import decode

qr = Image.open("qr_cropped.png")
qr_inverted = ImageOps.invert(qr.convert("L"))
results = decode(qr_inverted)
password = results[0].data.decode()
print(f"Password: {password}")
# Output: sup3rp4ssw0rd31337

QR content: sup3rp4ssw0rd31337

Stage 3 — Steghide Extraction

Used the decoded password with steghide on the original JPEG:

bash
steghide extract -sf try_harder.jpg -p "sup3rp4ssw0rd31337"

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

This extracted the flag directly. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍