$ cat writeup.md…
$ cat writeup.md…
broncoctf2026
Task: a handwritten JPEG letter names the ADFGVX cipher, draws its 6x6 Polybius square, and embeds a ciphertext plus the transposition keyword SUGAR. Solution: undo the columnar transposition (columns read in alphabetical key order), split the ADFGVX-pair stream, and look up each pair in the square to recover JELLYDONUT.
Grandma wants to protect her wifi password. Can you find out what it is before grandpa does?
A single JPEG (Letter.jpeg) is provided — a handwritten letter from Grandma. The letter
itself is the whole challenge: it names the cipher, draws the substitution square, embeds the
ciphertext, and hides the transposition keyword. The goal is to recover the WiFi password and
wrap it as bronco{...}.
The file is a JPEG, so stego is the obvious first guess — but it is a red herring:
exiftool — ordinary EXIF, nothing hidden.binwalk — only JPEG/JFIF + TIFF (EXIF) signatures, no appended/embedded data.strings — no flag, password, or steghide/steg markers.Conclusion: nothing is hidden in the file. The puzzle is entirely the text of the letter — a classical ADFGVX cipher.
So the letter hands you everything:
A D F G V X).GVXXFVXVAFXFXVGADAFF (20 chars — only A/D/F/G/V/X, the ADFGVX signature).SUGAR (in "alphabetically sorted SUGAR"). A D F G V X
A B 3 M R L I
D A 6 F 0 8 2
F C 7 S E U H
G Z 9 D X K V
V 1 Q Y W 5 P
X N J T 4 G O
(The 0 cell is drawn as a slashed-zero glyph ø.)
ADFGVX is a fractionating cipher, encryption in two stages:
Decryption reverses this: undo the transposition, then look up each letter pair as (row, col) in the square.
GVXXFVXVAFXFXVGADAFF — 20 chars.SUGAR, length 5 → 20 / 5 = 4 rows.S U G A R gives column read order A(3) G(2) R(4) S(0) U(1),
i.e. index order [3, 2, 4, 0, 1].XDFGAVAVVFGFXXXAFVXF.JELLYDONUT — which
fits Grandma's "sugar filled treats" theme.#!/usr/bin/env python3 # Solve: undo ADFGVX columnar transposition, then Polybius-square lookup. ct = 'GVXXFVXVAFXFXVGADAFF' key = 'SUGAR' # Column read order = alphabetical order of keyword letters -> [3,2,4,0,1] order = sorted(range(len(key)), key=lambda i: key[i]) n = len(ct) // len(key) # 4 rows per column cols = [''] * len(key) p = 0 for i in order: # ciphertext was read out in alphabetical column order cols[i] = ct[p:p+n] p += n # Read the grid row-by-row to recover the ADFGVX pair-stream stream = ''.join(cols[c][r] for r in range(n) for c in range(len(key))) # stream == 'XDFGAVAVVFGFXXXAFVXF' labels = 'ADFGVX' grid = [list('B3MRLI'), list('A6F082'), list('C7SEUH'), list('Z9DXKV'), list('1QYW5P'), list('NJT4GO')] pt = ''.join(grid[labels.index(stream[i])][labels.index(stream[i+1])] for i in range(0, len(stream), 2)) print(pt) # JELLYDONUT print("bronco{%s}" % pt) # bronco{REDACTED}
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar