← Back to Writeups
CUSTOMEasyCrypto

Grandma's Secret

XESXOR9/10/20264 min read
#crypto#custom#easy#adfgvx_decryption#columnar_transposition_undo#polybius_square_lookup

Grandma's Secret

Platform: CUSTOM | Category: Crypto | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: adfgvx_decryption, columnar_transposition_undo, polybius_square_lookup

Summary

Walkthrough of the CUSTOM Crypto Grandma's Secret. 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


title: "Grandma's Secret" id: 20260711_broncoctf2026_grandmas_secret category: crypto difficulty: easy event: broncoctf2026 tags: ["classical_cipher", "polybius_square", "columnar_transposition", "adfgvx", "fractionating_cipher"] techniques: ["adfgvx_decryption", "columnar_transposition_undo", "polybius_square_lookup"] tools: ["python3", "exiftool", "binwalk"] indicators: ["cipher named explicitly in prose (ADFGVX)", "6x6 Polybius square drawn with headers A D F G V X", "ciphertext uses only the letters A/D/F/G/V/X", "keyword hidden in phrase 'alphabetically sorted SUGAR"]

Grandma's Secret — broncoctf2026

Description

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{...}.

Analysis

Recon (ruling out steganography)

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.

Key excerpts from the letter

  • "I used my favorite ADFGVX cipher from long ago for you my child."
  • "I know you always loved my alphabetically sorted SUGAR filled treats."
  • "Especially GVXX FVXV AFXF XVGA DAFF. Have fun surfing the Web!"

So the letter hands you everything:

  • Cipher name: ADFGVX.
  • Substitution table: the 6×6 Polybius square drawn on the page (row/column headers A D F G V X).
  • Ciphertext: GVXXFVXVAFXFXVGADAFF (20 chars — only A/D/F/G/V/X, the ADFGVX signature).
  • Transposition keyword: SUGAR (in "alphabetically sorted SUGAR").

The Polybius square

    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 ø.)

How ADFGVX works

ADFGVX is a fractionating cipher, encryption in two stages:

  1. Substitution — each plaintext character maps via the 6×6 Polybius square to a pair of the six letters A/D/F/G/V/X (its row header + column header).
  2. Columnar transposition — the resulting pair-stream is written under a keyword and the columns are read out in alphabetical order of the keyword letters.

Decryption reverses this: undo the transposition, then look up each letter pair as (row, col) in the square.

Solution

  • Ciphertext (transposition output): GVXXFVXVAFXFXVGADAFF — 20 chars.
  • Keyword SUGAR, length 5 → 20 / 5 = 4 rows.
  • Alphabetical order of 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].
  • Refilling the columns in that alphabetical order and reading row-by-row reconstructs the pre-transposition ADFGVX-pair stream: XDFGAVAVVFGFXXXAFVXF.
  • Splitting into pairs and looking up (row, col) in the square yields JELLYDONUT — which fits Grandma's "sugar filled treats" theme.
python
#!/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}

Key Takeaways / Lessons

  • Mastering adfgvx_decryption, columnar_transposition_undo, polybius_square_lookup was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-07-11 • CUSTOM • Grandma's Secret • Difficulty: Easy