TexSAW CTF 2026 The Imitation Game

There’s a spy amongst us! We found one of their messages, but can’t seem to crack it. For some reason, they wrote the message down twice.

The challenge provides two large blocks of ciphertext, both starting with what appears to be an encrypted flag.

Solution

1. Identifying the Cipher

We are given two different ciphertexts that supposedly represent the same message. This immediately suggests a polyalphabetic substitution cipher, most likely Vigenere, where different parts of the key are being applied to the same plaintext.

2. Deducing the Key Prefix

We know that the flags in this CTF follow the format texsaw{...}. By comparing the ciphertext prefixes with the known plaintext texsaw, we can calculate the key characters used at the start of each block (Key = Ciphertext − Plaintext).

Block 1 Prefix (twhsnz):

  • t - t = A (0)
  • w - e = S (18)
  • h - x = K (10)
  • s - s = A (0)
  • n - a = N (13)
  • z - w = D (3)
  • Key Prefix: ASKAND

Block 2 Prefix (brassg):

  • b - t = I (8)
  • r - e = N (13)
  • a - x = D (3)
  • s - s = A (0)
  • s - a = S (18)
  • g - w = K (10)
  • Key Prefix: INDASK

3. Recovering the Full Key

The fragments ASKAND and INDASK strongly suggest a famous quote from the Bible (Matthew 7:7):

Ask, and it shall be given you; seek, and ye shall find: ask, and…”

By removing spaces and punctuation, we derive the full 41-character repeating key: ASKANDITSHALLBEGIVENYOUSEEKANDYESHALLFIND

4. Decrypting the Message

Using the recovered key, we can decrypt the rest of the message. The first message block uses an offset of 0, while the second block starts at a different position in the key loop.

Decryption reveals a message from a spy:

“they know im here, and its only a matter of time before they find out who i am. tell the general what the flag is as soon as possible…”

The signature at the bottom, - john cairncross, refers to the real-life British intelligence officer who was a double agent for the Soviet Union during World War II.

5. Extracting the Flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
def vigenere_decrypt(ct, key, offset=0):
res = ""
k_idx = offset
key = key.lower()
for char in ct:
if "a" <= char <= "z":
shift = ord(key[k_idx % len(key)]) - ord("a")
res += chr((ord(char) - ord("a") - shift) % 26 + ord("a"))
k_idx += 1
elif "A" <= char <= "Z":
shift = ord(key[k_idx % len(key)]) - ord("a")
res += chr((ord(char) - ord("A") - shift) % 26 + ord("A"))
k_idx += 1
else:
res += char
# k_idx is NOT incremented for non-alpha
return res


key = "askanditshallbegivenyouseekandyeshallfind"

ct1 = "twhsnz{tngqmqdhqqygxrloyehuvxtwwvxklkiiudpxqcvqhbmkepledu}"
ct2 = "brassg{lhrrfxzgxvrpzmierkrkdbkdyeibpredxbrflvvvotgvfisacb}"

# just loop and try offset
print("CT1:", vigenere_decrypt(ct1, key, 0))

print("CT2:", vigenere_decrypt(ct2, key, 38))

msg1 = """zpzc xlcq aq lorr, dlh aas zyqg n paldee rn mate mpgsxm olrw tcfh set jkm m st.
tpwq buh gwxeedt pzht esf jrib mf yg mgsr ks crqwailp.
ejty whw qeahztd ieqzsi zpzc sgbx gyx ghruc me oiotso!
vi adv gbha pwsl, t'wm qkmo cbs on gyv pievr qwlttyl tbfalsoa wwfgyrzh bx sqyrvevn.
eeoo shuc cgb'rp ytb srldywrg

x.l. loe xzwmk "qhmgyhcgr kkmr" lq zwyy rztl. lru krohol psacs tu anmi cbs quf.
- nsrn pdgvfjrzdx
"""

print("\nMessage 1:")
print(vigenere_decrypt(msg1, key, 15))

Flag

texsaw{luojmfsgmkqltenaemdqlxgtyrfdlzxdmqmxysvdettsxpatcq}