← Back to Writeups
HTBEasyCryptography

Atomic Substitution Theory

D3v0o0Nu119/2/20265 min read
#cryptography#htb#easy#periodic_table_coordinate_deco#element_symbol_substitution#letter_index_selection

Atomic Substitution Theory

Platform: Broncoctf2026 | Category: Cryptography | Type: Challenge | Difficulty: Easy | OS: NA | Author: D3v0o0Nu11 | Date: 2026-07-11 | Status: Solved Techniques: periodic_table_coordinate_decoding, element_symbol_substitution, letter_index_selection

Summary

Task: a chemist's secret message given as a list of (row, column[, k]) tuples. Solution: read each tuple as a periodic-table coordinate identifying an element symbol (third value picks one letter), concatenate, and lowercase to get the flag.

Recon

Port scan

bash
nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
PortServiceVersionNotes
<PORT><SVC><VER><notes>

Enumeration highlights

  • Event: broncoctf2026 | ID: 20260711_broncoctf2026_atomic_substitution_theory
  • Tags: encoding, periodic_table, substitution, coordinate_cipher, chemistry
  • Indicators: chemist / atomic / substitution theme, tuples of (row, column[, k]), prefix decodes to BrONCo (Br O N Co), third tuple element selects a single letter of a 2-char symbol
  • Source: 20260711_broncoctf2026_atomic_substitution_theory.md

Foothold

Vulnerability / Misconfiguration

  1. periodic_table_coordinate_decoding
  2. element_symbol_substitution
  3. letter_index_selection
bash
<command>

Exploitation

  • See original writeup content for detailed exploitation.

Privilege Escalation

Enumeration

bash
sudo -l
find / -perm -4000 2>/dev/null
getcap -r / 2>/dev/null
cat /etc/crontab
ps aux

Exploitation

  1. N/A for challenge-type writeup; see exploitation above.
  2. Flag obtained via challenge solve.
bash
<command>

Flags

FlagLocationValue
flagREDACTED

Key Takeaways / Lessons

  • periodic_table_coordinate_decoding
  • element_symbol_substitution
  • letter_index_selection
  • Tags: encoding, periodic_table, substitution, coordinate_cipher, chemistry

Original Writeup

<details><summary>Click to expand original content</summary>

Atomic Substitution Theory — broncoctf2026

Description

This text file is what happens when a chemist tries to send you a top secret message.

Hint: all letters in the flag should be lowercase.

We are given secret.txt, a single line of tuples plus the literal characters {, }, and _:

(4, 17), (2, 16), (2, 15), (4, 9), { , (3, 2, 1), (5, 3), _ , (2, 17), (3, 13, 1), (4, 5), (2, 16), (4, 17, 2), (2, 1, 2), (4, 4, 1), (2, 2, 2), _ , ... , (3, 16), (9, 6), (3, 15), (4, 17, 2), (2, 1, 2), (3, 16), (2, 2, 2), }

Goal: decode the message into the bronco{...} flag.

Analysis

The title "Atomic Substitution Theory" and the word "chemist" point directly at the periodic table. "Substitution" means we replace each token with a letter.

Each tuple is a coordinate on the periodic table:

  • (row, col) → the element sitting at that period/group → take its full symbol.
  • (row, col, k) → the k-th letter (1-indexed) of that element's symbol.
  • Literal {, }, _ pass through unchanged; _ acts as a word separator (space).

The prefix confirms the scheme immediately:

TokenElementLetters
(4,17)BrBr
(2,16)OO
(2,15)NN
(4,9)CoCo

→ BrONCo — the CTF's brand ("Bronco"). Then {, (3,2,1)=Mg→M, (5,3)=Y → {MY.

Three-element tuples slice a single letter of a two-letter symbol, e.g. (4,17,2)=Br→r, (2,1,2)=Li→i. The detached actinide row is treated as period 9, so (9,6) is Uranium.

Reading everything out and lowercasing yields:

bronco{REDACTED...REDACTED}

Pitfall (lesson learned)

A naive letter-for-letter read produces two plausible-but-wrong words:

  • The word decoded from (3,13,1)(4,4,1) = Al→A + Ti→T = "at", but the intended English (matching the pun "an element of surprise") is "an".
  • The final word only spells "surprise" if (9,6) (Uranium) is read as its two-letter form "Ur": S + Ur + P + r + i + S + e = surprise. Reading Uranium as a single U gives the misspelled "suprise".

So don't trust the first plausible English guess — confirm against the pun's meaning: the message is literally "my favorite messages have an element of surprise."

Solution

Map every coordinate to an element symbol, apply the letter-index for 3-tuples, pass through the literals, and lowercase. Fix the single ambiguous word at → an to match the intended phrase.

python
#!/usr/bin/env python3
import re

# (period, group) -> element symbol, covering every coordinate in secret.txt.
PT = {
    (1, 1): "H",
    (2, 1): "Li", (2, 2): "Be", (2, 15): "N", (2, 16): "O", (2, 17): "F",
    (3, 2): "Mg", (3, 13): "Al", (3, 15): "P", (3, 16): "S", (3, 17): "Cl",
    (4, 4): "Ti", (4, 5): "V", (4, 9): "Co", (4, 13): "Ga", (4, 17): "Br",
    (5, 3): "Y",
    (9, 6): "Ur",   # Uranium (actinide row), two-letter form so we get "surprise"
}

def decode(path="secret.txt"):
    tokens = re.findall(r"\([^)]*\)|[{}_]", open(path).read())
    out = []
    for tok in tokens:
        if tok in "{}_":
            out.append(tok); continue
        n = [int(x) for x in re.findall(r"-?\d+", tok)]
        if len(n) == 2:
            out.append(PT[(n[0], n[1])])          # full symbol
        elif len(n) == 3:
            out.append(PT[(n[0], n[1])][n[2] - 1])  # k-th letter

    return "".join(out)

# Naive read gives proper English except one ambiguous word ("at" -> "an").
FIXUPS = {"at": "an"}

def to_flag(raw):
    low = raw.lower()
    head, _, rest = low.partition("{")
    content, _, tail = rest.rpartition("}")
    words = [FIXUPS.get(w, w) for w in content.split("_")]
    return f"{head}{{{'_'.join(words)}}}{tail}"

if __name__ == "__main__":
    raw = decode()
    print("raw  :", raw)                 # BrONCo{REDACTED..._AT_..._SUrPriSe}
    print("naive:", raw.lower())         # ...have_atREDACTED
    flag = to_flag(raw)
    print("flag :", flag)
    assert flag == "bronco{REDACTED}"

Running it:

raw  : BrONCo{REDACTEDTREDACTED}
naive: bronco{REDACTEDtREDACTED}
flag : bronco{REDACTED}
</details>

signed by XESXOR