← Back to Writeups
CUSTOMEasySteganography

Огрызок (Stub)

XESXOR9/2/20262 min read
#steganography#custom#easy#alternative_barcode_library_de#finder_pattern_count_identific

Огрызок (Stub)

Platform: bug-makers | Category: Steganography | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-05-29 | Status: Solved Techniques: alternative_barcode_library_decoding, finder_pattern_count_identification

Summary

Task: PNG of a QR-like code on a torn piece of paper, flag encoded in it. Solution: image has only one finder pattern => Micro QR Code (not a damaged standard QR); decoded with zxing-cpp which supports Micro QR where pyzbar/OpenCV fail.

Огрызок (Stub) — bug-makers

Description

На обрывке бумажного листа виднеется qr-код, в нем закодирован флаг.

English: On a torn piece of paper a QR code is visible; the flag is encoded in it.

Provided file: stub.png (505x505 RGBA) — an image of a QR-like code printed on a torn piece of paper, with a drop shadow and warped/ragged paper edges. Only the top-left finder pattern is present; the other three corners are plain data modules with torn edges.

Analysis

The visual framing — "torn paper" (matching the task name Огрызок = stub/remnant) — strongly suggests the QR was physically damaged and that missing modules must be reconstructed. This is a deliberate red herring. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key structural observations:

  • Only ONE finder pattern exists (top-left). Standard QR codes have THREE (top-left, top-right, bottom-left).
  • Finder pattern bounding box: x=31..224, y=34..227 = 193px = 7 modules => module size ~27.57px.
  • Black content spanned x=13..491, y=21..487 (~17–18 modules wide).
  • Manual fixed-step grid sampling produced a clean top-left finder, but the timing pattern (row 6 / col 6) did NOT alternate the way a standard QR requires — an early clue the standard-QR assumption was wrong. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Failed path (documented as a pitfall)

All standard QR decoders failed because they require three finder patterns:

  • pyzbar — no result
  • zbarimg — no result
  • cv2.QRCodeDetector().detectAndDecode (raw, thresholded, upscaled) — no result

The tempting-but-wrong hypothesis was a damaged Version 1 (21x21) standard QR with the bottom ~4 rows and right ~4 columns torn off, to be recovered via perspective de-warp + Reed-Solomon erasure decoding. This wasted time.

Solution

Key insight: A QR symbol with only ONE finder pattern (top-left) is almost certainly a Micro QR Code. By design Micro QR has a single finder pattern — the "missing" corners are not torn off, they simply do not exist in the format. The non-alternating timing pattern and the single finder were the structural tells. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

zxing-cpp supports Micro QR Code (and rMQR, Aztec, DataMatrix, etc.) where pyzbar and OpenCV do not.

python
#!/usr/bin/env python3
# pip install zxing-cpp opencv-python
import zxingcpp, cv2

img = cv2.imread('stub.png')
for r in zxingcpp.read_barcodes(img):
    print('format:', r.format, 'text:', repr(r.text))
# -> format: Micro QR Code  text: 'BugCTF{REDACTED}'

The decoded payload micr0qr is itself a meta-hint confirming the format. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍