Layer cake

整体解法路线是:先通过 Base64 和 Caesar 拿到 Vigenere 密钥,再用 Vigenere 解出 repeating-XOR 密钥,接着从一段 XOR 线索里提取 RC4 口令,最后解出最终密文。

notice.txt 先做 Base64 解码,得到一段全大写的 Caesar 密文。向回偏移 7 位后,可以得到提示语,从中提取出 Vigenere 密钥 ORACLE

接着用 ORACLE 去解 old_letter.txt,可以得到 repeating-XOR 的密钥 NIGHTFALL

再用 NIGHTFALL 去异或 traffic.txt,虽然结果中夹杂了一些不可见字符,但可以明显看出最长的一段纯小写字符串是 retrowizard,这就是最后解密 vault.txt 所需的 RC4 密钥。

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
42
43
44
45
46
47
48
49
50
import base64
import re
from itertools import cycle
from Crypto.Cipher import ARC4

notice = "QU9MIENQTkxVTFlMIFJMRiBQWiBWWUhKU0w="
old_letter = "HYE TPTSRTKYK LFR MPC WJ NKRLHWANW"
traffic = "282029293846332f584e222231542f324c3e2b3d3527232f3b2d3e2a"
vault = "mun76xkQmk/MM4u0BRQQipplWO4PI7iJioGFFYRczP4PSuwZxcoRNw=="


def caesar_dec(s: str, shift: int) -> str:
out = []
for ch in s:
if "A" <= ch <= "Z":
out.append(chr((ord(ch) - ord("A") - shift) % 26 + ord("A")))
else:
out.append(ch)
return "".join(out)


def vigenere_dec(ct: str, key: str) -> str:
out = []
ki = 0
for ch in ct:
if "A" <= ch <= "Z":
k = ord(key[ki % len(key)]) - ord("A")
out.append(chr((ord(ch) - ord("A") - k) % 26 + ord("A")))
ki += 1
else:
out.append(ch)
return "".join(out)


notice_stage = base64.b64decode(notice).decode()
vigenere_key = caesar_dec(notice_stage, 7).split()[-1]

xor_key = vigenere_dec(old_letter, vigenere_key).split()[-1]

mid = bytes(
c ^ k for c, k in zip(bytes.fromhex(traffic), cycle(xor_key.encode()))
)
rc4_key = max(re.findall(rb"[a-z]{4,}", mid), key=len).decode()

flag = ARC4.new(rc4_key.encode()).decrypt(base64.b64decode(vault)).decode()

print(vigenere_key)
print(xor_key)
print(rc4_key)
print(flag)

运行输出:

1
2
3
4
ORACLE
NIGHTFALL
retrowizard
flag{caesar_vigenere_xor_rc4_all_in_one}