Проклятый старый дом (Cursed Old House)
Проклятый старый дом (Cursed Old House)
Platform: bug-makers | Category: Crypto | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-01-01 | Status: Solved Techniques: rotating_cardan_grille, alpha_channel_hole_detection, grid_rotation_transform, leetspeak_decode
Summary
Task: two PNG wooden boards — a 6x6 character grid and a mask with 6 transparent holes — encode a hidden message. Solution: recognize a rotating Cardan grille, detect holes via PNG alpha channel, read revealed cells across all 4 rotations (90deg CW transform (r,c)->(c,5-r)), then leet-decode.
Проклятый старый дом (Cursed Old House) — Bug Makers CTF
Description
Вы нашли в дремучем лесу старый ветхий дом. А когда вошли в него, то двери захлопнулись, и перед вами выпали две деревянные дощечки. Кажется, призрак что-то пытается сказать. Какое послание он передает?
English translation: You found an old dilapidated house in a dense forest. When you entered, the doors slammed shut and two wooden boards fell out in front of you. It seems the ghost is trying to say something. What message does it convey?
Given files: old-house.zip containing two PNG images:
task.png— a 6×6 grid of characters carved on a wooden board.solver.png— a wooden board (mask) with 6 circular holes, encoded as transparent regions over a green underlay.- (Plus pre-segmented
cell_R_C.pngimages of each grid cell.)
Goal: recover the message the ghost is transmitting.
Analysis
The decisive semantic clue is "две деревянные дощечки" (two wooden boards): one board carries a character grid, the other is a mask with holes. This is the textbook setup of a Cardan grille (решётка Кардано) — a transposition cipher where a perforated mask is laid over a grid to reveal only the cells through the holes.
Two boards alone could be a static grille, but a static 6-hole mask over a 36-cell grid would reveal only 6 characters. The correct interpretation is a rotating Cardan grille: the mask is rotated through all four orientations (0°, 90°, 180°, 270°), and at each orientation the revealed cells are read. With 6 holes × 4 rotations = 24 revealed cells, the four hole sets tile distinct cells of the grid (a hallmark of a properly designed rotating grille).
The 6×6 grid (from task.png)
S 0 0 3 0 C
3 0 4 1 7 D
2 P M R U S
D 3 4 Z G 8
L 0 4 W 5 N
3 Z 8 G 2 L
Hole positions (from solver.png)
The holes are transparent (alpha < 50) regions over a green underlay. Reading the PNG alpha channel and binning the transparent pixels into the 6×6 grid yields six hole cells (zero-indexed (row, col)):
(0,5), (1,2), (2,3), (3,0), (4,2), (4,5)
Solution
-
Recognize the two-boards clue as a Cardan grille.
-
Extract the 6×6 grid characters from
task.png(verified against the segmentedcell_R_C.pngimages). -
Detect hole cells in
solver.pngvia alpha transparency. -
Apply the mask as a rotating grille. Rotating the mask 90° clockwise on a 6×6 grid maps each hole
(r,c) -> (c, 5-r). At each rotation, read the revealed cells in row-major (sorted) order and concatenate: Rotation Holes (sorted) Revealed chars 0° (0,5)(1,2)(2,3)(3,0)(4,2)(4,5) C 4 R D 4 N→C4RD4N90° rotated set 0 P U Z Z L→0PUZZL180° rotated set 3 1 S 4 W 3→31S4W3270° rotated set S 0 M 3 G G→S0M3GGConcatenated:
REDACTED -
Leet-decode (
4→A,0→O,3→E,1→I) reveals the plaintext: "CARDANO PUZZLE IS AWESOME GG" — a self-referential confirmation, since Cardano invented the grille. -
The flag is the leetspeak message without spaces.
python read-only • copy allowed#!/usr/bin/env python3 from PIL import Image import numpy as np, collections # 1. Detect transparent holes in the mask a = np.array(Image.open('solver.png').convert('RGBA')) h, w = a.shape[:2] cw, ch = w / 6, h / 6 ys, xs = np.where(a[..., 3] < 50) # transparent pixels cells = collections.Counter((int(y // ch), int(x // cw)) for x, y in zip(xs, ys)) holes = sorted(k for k, v in cells.items() if v > 500) # -> [(0,5),(1,2),(2,3),(3,0),(4,2),(4,5)] # 2. The 6x6 grid from task.png grid = [list('S0030C'), list('30417D'), list('2PMRUS'), list('D34ZG8'), list('L04W5N'), list('3Z8G2L')] # 3. Apply as a ROTATING grille (90deg CW: (r,c) -> (c, 5-r)) rotCW = lambda hs: [(c, 5 - r) for (r, c) in hs] H, msg = holes, '' for _ in range(4): msg += ''.join(grid[r][c] for (r, c) in sorted(H)) H = rotCW(H) print(msg) # REDACTED print('BugCTF{%s}' % msg) # final flag # 4. (Sanity) leetspeak -> plaintext plain = msg.translate(str.maketrans('4031', 'AOEI')) print(plain) # CARDANOPUZZLEISAWESOMEGG