TexSaw2025 Who‐Made‐This‐Anyway writeup - Pez1181/CTF GitHub Wiki

🧩 Challenge: "Ciphers Through Time"

Category: Cryptography
Vulnerability: Misattributed historical cipher (Vigenère)
Protection Bypassed: Polyalphabetic substitution


🔍 Recon

The challenge description gave us an immediate historical hint:

"Giovan Battista Bellaso introduced this polyalphabetic substitution cipher in his 1553 book..."

This is a dead giveaway for the Vigenère cipher, often incorrectly attributed to Blaise de Vigenère, but first published by Bellaso.

A .txt file was provided, containing the ciphertext:

GMCAIVQOQYMDMRAWSRLTDXGFHHAKXEWKARAXKNPMSLZANWXLXIJWAXKIP...

🔬 Vulnerability Analysis

This was a textbook example of a Vigenère cipher: a polyalphabetic substitution cipher where each letter of the plaintext is shifted by a different Caesar cipher based on a repeating key.

Our first task was to identify the key length. We used:

  • Index of Coincidence (IC) graphing to find periodic spikes
  • Friedman's test to mathematically estimate the key length, which gave us ≈ 5

Multiple lengths (4–8) were explored with frequency analysis and brute-force on common shifts, under the assumption that each column of the ciphertext corresponds to Caesar-shifted English.


🎯 Exploit Strategy

  1. Brute-force candidate keys of likely lengths (6, 5, 8), using top 3 letter frequencies per column.
  2. Search for texsaw{...} in each decrypted candidate.
  3. Fallback scoring using a list of common English trigrams (THE, AND, etc.) to rank outputs even if flag format didn’t appear.

The best-scoring decryption (key = SAWTEX) produced readable, enthusiastic plaintext:

OMG HEY YOU FIGURED OUT THE CIPHER ... THE FLAG IS EXAMPLESPOILERFREETEXT

🧪 Example Code

def vigenere_decrypt(ciphertext, key):
    decrypted = []
    key = key.upper()
    for i, c in enumerate(ciphertext):
        shift = ord(key[i % len(key)]) - ord('A')
        dec = chr((ord(c) - shift - ord('A')) % 26 + ord('A'))
        decrypted.append(dec)
    return ''.join(decrypted)

print(vigenere_decrypt(ciphertext, "SAWTEX"))

✍️ Final Notes

This challenge was a fun blend of classical crypto with some hands-on frequency analysis. Vigenère ciphers remain popular in CTFs due to their balance of history, logic, and brute-force resistance (until they're not).