zakaria-zoulati/MTP-Attack
GitHub: zakaria-zoulati/MTP-Attack
一个用于破解流密码的 Python 工具包,通过空格投票和 Crib Dragging 技术恢复重复使用的密钥。
Stars: 0 | Forks: 0
# MTP 攻击 — 多次密码本 (Many-Time Pad) 密码分析
一个用于破解流密码(或 OTP)的 Python 工具包,适用于在多个消息中重复使用同一密钥的情况。这是一个众所周知的密码学漏洞,称为 **多次密码本 (MTP)** 攻击。
本项目使用的密文来自 OliCyber 培训平台:
[https://training.olicyber.it/challenges#challenge-331](https://training.olicyber.it/challenges#challenge-331)
## 漏洞原理:为什么重用密钥会破坏一切
**一次性密码本 (OTP)** 在理论上是不可破解的——但前提是密钥只使用一次且与消息长度相同。加密过程是一个简单的 XOR:
```
C = M XOR K
```
当同一个密钥 `K` 被重用于加密两个不同的消息 `M1` 和 `M2` 时:
```
C1 = M1 XOR K
C2 = M2 XOR K
```
攻击者可以将两个密文进行 XOR,密钥会相互抵消:
```
C1 XOR C2 = M1 XOR M2
```
现在攻击者得到了两个明文的 XOR 结果。如果已知或猜测出其中任一明文的任何部分,另一明文就可以被恢复——并且一旦任何明文字节已知,相应的密钥字节就会立即显露:
```
K[i] = C[i] XOR M[i]
```
## 空格投票启发式算法
初始密钥估计是通过一种称为 **空格投票 (space voting)** 的技术生成的。它利用了自然语言消息中包含大量空格字符 (`0x20`) 这一事实。
这种见解源于 ASCII 编码的一个特性:当一个字母(大写或小写)与空格 (`0x20`) 进行 XOR 时,结果是该字母的相反大小写形式。两种大小写仍然是有效的字母。
对于所有密文中的每个位置 `i`:
1. 对于每个密文 `Cj`,假设 `Cj[i]` 在明文中是一个空格。计算候选密钥字节:`k_candidate = 0x20 XOR Cj[i]`。
2. 在相同位置针对所有其他密文测试此候选字节。统计当与 `k_candidate` 进行 XOR 时,有多少密文会产生有效字母 (A-Z 或 a-z)。
3. 得票最多的候选字节胜出,并被存储为位置 `i` 的密钥字节。
这提供了密钥的一个粗略但通常准确的初步近似值。
## 拖拽猜测 (Crib Dragging)(手动细化)
**拖拽猜测 (Crib dragging)** 是用于在空格投票后细化密钥的技术。“Crib”是一个短的已知或猜测的明文片段——即可能出现在某条消息中的单词或短语。
过程如下:
1. 使用当前密钥解密所有消息,并寻找部分可读的文本。
2. 识别某条消息中看起来几乎正确的片段。
3. 使用 `update-key.py` 声明:“在消息 `i` 的位置 `p`,明文是 `word`”。脚本会直接根据已知明文和密文重新计算这些位置的密钥字节。
4. 重新运行 `print-messages.py` 并检查其他消息是否有所改善。在一个位置的正确猜测会同时改善所有消息的解密,因为它们共享同一个密钥。
5. 重复此过程,直到所有消息都可读且完整密钥被恢复。
## 项目结构
```
MTP-Attack/
├── output.txt # The ciphertexts (hex-encoded, one per line)
├── key.txt # The current key candidate (hex-encoded, persisted across steps)
├── utils.py # Shared helper functions
├── space-voting.py # Step 1: generates initial key estimate using space voting
├── print-messages.py # Step 2: decrypts and prints all messages with the current key
└── update-key.py # Step 3: refines the key using a known plaintext fragment
```
### `utils.py`
提供两个辅助函数:
- `getMaxLength(messages)` — 返回最长密文的长度。
- `isValid(b)` — 如果字节 `b` 对应一个 ASCII 字母 (A-Z 或 a-z),则返回 `True`。
### `space-voting.py`
实现空格投票攻击。对于每个字节位置,通过统计有多少密文在该位置产生有效字母来对每个候选密钥字节进行评分,然后将最佳密钥估计写入 `key.txt`。
### `print-messages.py`
读取 `key.txt` 和 `output.txt`,将每个密文与当前密钥进行 XOR,并打印所有解密后的消息。每次更新密钥后运行此脚本以评估进度。
### `update-key.py`
接受三个命令行参数并修补 `key.txt`:
| 参数 | 描述 |
|---|---|
| `cipher_idx` | 已知明文所在消息的索引(从 0 开始) |
| `start_pos` | 已知片段开始的字节偏移量 |
| `new_message` | 该位置的已知明文字符串 |
在内部,它为片段中的每个字节重新计算 `key[pos] = plaintext[pos] XOR ciphertext[pos]`。
## 运行指南:分步说明
### 第 1 步 — 使用空格投票生成初始密钥
```
python space-voting.py
```
这将把估计的密钥写入 `key.txt` 并打印到标准输出。
### 第 2 步 — 检查当前解密结果
```
python print-messages.py
```
所有消息都以 `[0]` 开始索引打印。有些将是完全可读的,其他则会在密钥字节仍然错误的地方包含乱码字符。
### 第 3 步 — 使用已知明文片段细化密钥
当你发现一条看起来几乎正确的消息时——例如,消息 `[3]` 在位置 `5` 看起来可能说的是 `the`——运行:
```
python update-key.py 3 5 "the"
```
这会使用消息 3 作为明文参考,更新位置 5、6、7 处的 `key.txt`。
### 第 4 步 — 重复
回到第 2 步并再次检查所有消息。每个正确的明文猜测都会传播到所有密文,因为它们共享同一个密钥。继续此过程,直到所有消息被完全解密。
## 示例工作流程
```
# 1. Bootstrap the key
python space-voting.py
# 2. Read the current state
python print-messages.py
# [0]: Jhe quick brXwn fox...
# [1]: garbled text...
# 3. Message [0] clearly says "The quick" — fix position 0
python update-key.py 0 0 "The quick"
# 4. Re-read — all messages improved at those positions
python print-messages.py
# [0]: The quick brXwn fox...
# 5. Keep iterating
python update-key.py 0 10 "brown"
python print-messages.py
# ...
```
## 文件参考
| 文件 | 作用 |
|---|---|
| `output.txt` | 输入:十六进制编码的密文,每行一个。从不修改。 |
| `key.txt` | 状态:当前密钥候选,以十六进制表示。由 `space-voting.py` 和 `update-key.py` 更新。 |
标签:CTF工具, Many-Time-Pad, MTP攻击, OliCyber, Python, Space-Voting算法, XOR异或加密, 一次性密码本重用, 密码分析, 密码学, 密钥恢复, 手动系统调用, 无后门, 明文还原, 流密码, 流密码攻击, 漏洞搜索, 网络安全, 逆向工具, 隐私保护