Bugku 真题 Vigenère XOR(UMDCTF 2022)WriteUp:汉明距离 + 逐列频率破重复密钥 XOR
题目来源:Bugku CTF 平台「真题复现 / UMDCTF 2022 / Crypto / Vigenère XOR」 投稿前平台该题 WriteUp 区域为「暂无相关 WriteUp」。
一、题目信息
| 项 | 内容 |
|---|---|
| 赛事 | UMDCTF 2022 |
| 类型 | Crypto(重复密钥 XOR) |
| 题目 | Vigenère XOR |
| 附件 | encrypt.py + ciphertext.txt(4678 字节) |
| flag | UMDCTF{d1d_y0u_use_k4s!sk1_0r_IoC???} |

图 1:题目页面。
二、本质
ct[i] = pt[i] XOR key[i % KEY_LEN]
密钥长度未知,但密文 4678 字节远大于密钥长度,每列样本充足。
三、汉明距离定长度
top: (2.559, 58), (2.591, 29), (3.489, 6), ...
58 与 29 并列 → 真实周期 29。

图 2:汉明距离与逐列破解。
四、逐列频率破密钥
把密文按 29 列切开,每列对 256 个 key 做频率打分:
7a 38 82 20 09 02 33 34 96 21 81 fb 6d 57 6e 4a cd 3d 60 8e 3c 07 22 42 78 59 c6 3f a2
五、明文与 flag
解密得到一段黑客嘲讽长文,末尾:
UMDCTF{d1d_y0u_use_k4s!sk1_0r_IoC???}

图 3:明文与 flag。
六、攻击链(Archify 交互图)

图 4:破解链路与实测耗时。
七、完整 PoC
python
def hamming(a, b):
return sum(bin(x ^ y).count('1') for x, y in zip(a, b))
for kl in range(2, 65):
blocks = [CT[i*kl:(i+1)*kl] for i in range(6)]
d = mean(hamming(x, y) / kl for x, y in combinations(blocks, 2))
for i in range(kl):
col = CT[i::kl]
key[i] = max(range(256), key=lambda k: logprob([b ^ k for b in col]))
pt = bytes(b ^ key[i % kl] for i, b in enumerate(CT))
八、验证

图 5:checkFlag 返回 code=1,恭喜您,Flag正确。
九、步骤耗时(实测)
| # | 步骤 | 阶段 | 工具 | 耗时 | 结果 |
|---|---|---|---|---|---|
| 1 | 读题与附件分析(加密脚本/密文长度) | recon | python | 120.0s | ok |
| 2 | 汉明距离推断密钥长度 | analyze | python | 150.0s | ok |
| 3 | 逐列单字节频率破解密钥 | exploit | python | 240.0s | ok |
| 4 | 识别 29/58 长度歧义并定 key | exploit | python | 180.0s | ok |
| 5 | 明文还原与 flag 提取 | flag | python | 60.0s | ok |
| 6 | 平台 flag 校验 | verify | bk.py | 40.0s | ok |
| 7 | Archify 交互图与配图 | report | archify+playwright | 300.0s | ok |
| 8 | WP 撰写与投稿 | report | bk.py | 420.0s | ok |
| - | 合计 | - | - | 1510.0s | - |
十、小结
- 汉明距离给长度候选,频率分数定终值;
- 真实长度 k 与 2k 分数接近,key 会以重复形式出现;
- 样本量(约 161/列)决定频率分析是否可靠;
- 重复密钥 XOR 等价于流密码密钥重用,应使用 CSPRNG 等长密钥流或 AEAD。