← Back to Writeups
CUSTOMMediumCrypto

ХЛЕБ (BREAD) — Mayan Numeral Hex Encoding

XESXOR9/10/20266 min read
#crypto#custom#medium#base20_to_decimal#hex_ascii_decode#mayan_numeral_decoding#positional_numeral_system

ХЛЕБ (BREAD) — Mayan Numeral Hex Encoding

Platform: taipanbyte | Category: Crypto | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-02-21 | Status: Solved Techniques: base20_to_decimal, hex_ascii_decode, mayan_numeral_decoding, positional_numeral_system

Summary

Task: Decode a message encoded in Mayan numeral glyphs with Latin hex letters between groups. Solution: Convert Mayan base-20 columns to decimal, concatenate with hex letters (b,d,e,f), decode resulting hex string as ASCII.

ХЛЕБ (BREAD) — TaipanByte CTF

Description

"Те, у кого батон вместо нуля, передали нам какое то сообщение" (Those who have a baton/loaf instead of zero sent us some message)

Given an image file IMG_20240508_011853_788 (1).jpg containing Mayan numeral system glyphs arranged in groups, with Latin letters b, d, e, f between them.

Flag format: ctf{...}

Analysis

Step 1: Identifying the Encoding System

The hint "baton instead of zero" points to the Mayan numeral system:

  • Dot (•) = 1
  • Horizontal bar (—) = 5
  • Shell/oval = 0 (resembles a baton/bread — hence the task name "BREAD")
  • Numbers 0–19 in each glyph
  • Vertical columns — positional system with base 20 ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Mayan numeral system (vigesimal):

  • Each column is read from bottom to top
  • Bottom level: units (×1)
  • Second level: twenties (×20)
  • Third level: four-hundreds (×400)
  • And so on: ×8000, ×160000...

Step 2: Reading Mayan Glyphs from the Image

The image contains 12 columns of Mayan numbers with Latin letters b, d, e, f between certain columns.

Structure (left to right):

Col1 Col2 b Col3 d Col4 Col5 e Col6 f Col7 Col8 Col9 d Col10 f Col11 b Col12 d

Reading each column (top level = most significant digit): ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

ColumnMayan digits (top→bottom)CalculationDecimal value
Col1[15, 18, 14]15×400 + 18×20 + 146374
Col2[1, 13, 7]1×400 + 13×20 + 7667
Col3[6]66
Col4[15, 8, 19]15×400 + 8×20 + 196179
Col5[1, 10, 16]1×400 + 10×20 + 16616
Col6[5]55
Col7[18, 8, 19]18×400 + 8×20 + 197379
Col8[18, 8, 14]18×400 + 8×20 + 147374
Col9[1, 12, 16]1×400 + 12×20 + 16656
Col10[5]55
Col11[2, 6, 12, 14, 6, 16]2×3200000 + 6×160000 + 12×8000 + 14×400 + 6×20 + 167461736
Col12[7]77
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 3: Key Insight — Letters as Hex Digits

The Latin letters b, d, e, f between columns are not just separators, but hexadecimal digits that become part of the hex string!

Decoding method:

  1. Convert each Mayan column to decimal (base-20 → decimal)
  2. Write each number as a decimal string
  3. Latin letters b, d, e, f are hex digits inserted between numbers
  4. Concatenate all decimal strings + hex letters into one long hex string
  5. Decode the hex string as ASCII

Solution

Building the Hex String

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

6374 + 667 + b + 6 + d + 6179 + 616 + e + 5 + f + 7379 + 7374 + 656 + d + 5 + f + 7461736 + b + 7 + d

Concatenation result:

6374667b6d6179616e5f73797374656d5f7461736b7d

Decoding hex → ASCII

63='c'  74='t'  66='f'  7b='{'  6d='m'  61='a'  79='y'  61='a'  6e='n'
5f='_'  73='s'  79='y'  73='s'  74='t'  65='e'  6d='m'  5f='_'
74='t'  61='a'  73='s'  6b='k'  7d='}'

Solution Script

python
#!/usr/bin/env python3
"""
TaipanByte CTF — ХЛЕБ (BREAD)
Mayan numeral system → hex → ASCII decoder
"""
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

def mayan_to_decimal(digits: list) -> int:
    """
    Convert Mayan vigesimal (base-20) digits to decimal.
    digits: list of values 0-19, from most significant to least significant.
    """
    result = 0
    for d in digits:
        result = result * 20 + d
    return result


# Mayan columns read from the image (top-to-bottom = MSB-to-LSB)
columns = [
    [15, 18, 14],           # Col1  → 6374
    [1, 13, 7],             # Col2  → 667
    # letter 'b'
    [6],                    # Col3  → 6
    # letter 'd'
    [15, 8, 19],            # Col4  → 6179
    [1, 10, 16],            # Col5  → 616
    # letter 'e'
    [5],                    # Col6  → 5
    # letter 'f'
    [18, 8, 19],            # Col7  → 7379
    [18, 8, 14],            # Col8  → 7374
    [1, 12, 16],            # Col9  → 656
    # letter 'd'
    [5],                    # Col10 → 5
    # letter 'f'
    [2, 6, 12, 14, 6, 16], # Col11 → 7461736
    # letter 'b'
    [7],                    # Col12 → 7
    # letter 'd'
]
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Letters between columns (hex digits, not separators!)
# Structure: Col1 Col2 b Col3 d Col4 Col5 e Col6 f Col7 Col8 Col9 d Col10 f Col11 b Col12 d
letters = {
    2: 'b',   # after Col2
    3: 'd',   # after Col3
    5: 'e',   # after Col5
    6: 'f',   # after Col6
    9: 'd',   # after Col9
    10: 'f',  # after Col10
    11: 'b',  # after Col11
    12: 'd',  # after Col12
}

# Build hex string
hex_parts = []
for i, col in enumerate(columns):
    decimal_value = mayan_to_decimal(col)
    hex_parts.append(str(decimal_value))
    col_num = i + 1
    if col_num in letters:
        hex_parts.append(letters[col_num])
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

hex_string = ''.join(hex_parts)
print(f"Hex string: {hex_string}")

# Decode hex to ASCII
flag = bytes.fromhex(hex_string).decode('ascii')
print(f"Flag: {flag}")

# Verification
assert flag == "ctf{REDACTED}", f"Unexpected: {flag}"
print("✅ Verified!")

Output

Hex string: 6374667b6d6179616e5f73797374656d5f7461736b7d
Flag: ctf{REDACTED}
✅ Verified!

Challenges During Solution

  1. Col5 misread: Initially read as [1, 16] (2 levels, value=36) instead of [1, 10, 16] (3 levels, value=616). The middle level of 2 bars (=10) was missed due to spacing between elements in the image. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

  2. Col11 misread: Initially read as [2, 6, 12, 6, 16] (5 levels) instead of [2, 6, 12, 14, 6, 16] (6 levels). The 4th level was incorrectly identified as 6 (1 bar + 1 dot), when it was actually 14 (2 bars + 4 dots).

  3. Recognizing hex letters: The letters b, d, e, f between columns are not just separators, but hexadecimal digits that become part of the hex string. This was the key insight for solving the task.

Reference: Mayan Numeral System

Symbols:
  •         = 1
  ••        = 2
  •••       = 3
  ••••      = 4
  ———       = 5  (horizontal bar)
  ———•      = 6
  ———••     = 7
  ———•••    = 8
  ———••••   = 9
  ═══       = 10 (two bars)
  ═══•      = 11
  ...
  ═══•••• = 14
  ≡≡≡       = 15 (three bars)
  ≡≡≡•      = 16
  ...
  ≡≡≡••••   = 19
  🥚 (oval) = 0  (shell/bread)
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Positional system (bottom to top):
  Level 0: ×1
  Level 1: ×20
  Level 2: ×400
  Level 3: ×8000
  Level 4: ×160000
  Level 5: ×3200000

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍