Мозаика (Mosaic)
Мозаика (Mosaic)
Platform: hackerlab | Category: Misc | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-04-05 | Status: Solved Techniques: base64_to_zip, image_rotation_180, qr_reassembly, quadrant_rearrangement, zip2john_cracking
Summary
Task: a PNG image contains a QR code rendered with circular dots, split into 4 quadrants and rearranged (one quadrant rotated 180°). Solution: identify cut lines between finder patterns, rearrange quadrants to restore valid QR layout, decode to get base64-encoded password-protected ZIP, crack with john to extract flag.
Мозаика (Mosaic) — Hackerlab (CODEBY Games)
Description
Задача проста на первый взгляд, но только на первый. Мы нашли необычную картину - это не просто красочная мозаика, она была создана нашим старым другом, который обожает шифровать в них скрытые сообщения
English summary: the provided archive contains task.png (846×846 pixels) — a QR code rendered with anti-aliased circular dots instead of standard square modules. The QR code has been split into 4 quadrants and the pieces have been rearranged like a jigsaw puzzle, with one piece rotated 180°. The goal is to reassemble the QR code, decode it, and extract the flag.
Analysis
Initial observations
The 846×846 image shows a grid of circular dots of varying sizes on a white background. Three QR finder patterns (the characteristic concentric squares) are visible, but at non-standard positions:
- Two finder patterns side by side near the top-center of the image
- One finder pattern at the bottom-right corner
In a valid QR code, the three finder patterns must be at the top-left, top-right, and bottom-left corners. The unusual positioning immediately suggests the image has been cut and rearranged.
Grid analysis
The image contains a 61×61 grid of dots (QR version 11), with cell spacing of approximately 13.8 pixels. The dots are circular and anti-aliased, which causes most standard QR decoders (pyzbar, zbarimg) to fail — they expect sharp square modules.
Identifying the puzzle
The image was split into 4 quadrants by two cuts:
- Vertical cut at approximately x=401 pixels (between the two adjacent top finder patterns)
- Horizontal cut at approximately y=470 pixels (above the bottom-right finder pattern)
These cuts produce 4 unequal pieces:
- A (top-left, 470×401): contains one finder pattern
- B (top-right, 470×445): contains one finder pattern
- C (bottom-left, 376×401): no finder pattern
- D (bottom-right, 376×445): contains one finder pattern, rotated 180°
Reassembly
To restore the valid QR layout (finders at TL, TR, BL):
Original layout: Reassembled:
[A | B] [B | A]
[C | D] [rot180(D) | C]
The bottom-right quadrant D must be rotated 180° before placement at the bottom-left position to correctly orient its finder pattern.
Solution
Step 1: Reassemble the QR code
python read-only • copy allowed#!/usr/bin/env python3 """Reassemble the mosaic QR code from 4 rearranged quadrants.""" import numpy as np from PIL import Image img = np.array(Image.open('task.png').convert('L')) # Cut positions (between finder patterns) cut_x, cut_y = 401, 470 # Extract 4 quadrants A = img[0:cut_y, 0:cut_x] # top-left B = img[0:cut_y, cut_x:] # top-right C = img[cut_y:, 0:cut_x] # bottom-left D = img[cut_y:, cut_x:] # bottom-right # Rotate D by 180 degrees D_rot = np.rot90(D, 2) # Reassemble: [B, A] on top, [rot180(D), C] on bottom assembled = np.vstack([ np.hstack([B, A]), np.hstack([D_rot, C]) ]) Image.fromarray(assembled).save('assembled_qr.png')
Step 2: Decode the QR code
Standard QR decoders fail on the circular dot style. The zxingcpp Python library handles it:
python read-only • copy allowedimport zxingcpp results = zxingcpp.read_barcodes(Image.fromarray(assembled)) for r in results: print(r.text)
Alternatively, the Aspose online QR reader (aspose.app) can scan dot-based QR codes.
The QR decodes to a base64 string:
UEsDBAoACQAAAP1t/FaDz0hbJAAAABgAAAAIABwAZmxhZy50eHRV...
Step 3: Decode base64 to ZIP
bash read-only • copy allowedecho "UEsDBAoACQAAAP1t/FaD..." | base64 -d > flag.zip
The PK header (0x504B) confirms this is a ZIP archive. The archive contains flag.txt but is password-protected (PKZIP traditional encryption).
Step 4: Crack the ZIP password
bash read-only • copy allowedzip2john flag.zip > hash.txt john hash.txt # Password found: iloveme
Step 5: Extract the flag
bash read-only • copy allowedunzip -P iloveme flag.zip cat flag.txt # CODEBY{REDACTED}