ZIP 格式、伪加密、ZipCrypto 与 AES 加密及 KPA 攻击机制笔记
一、ZIP 文件基本结构与四大核心 PK 签名
ZIP 文件在二进制层面由三部分组成。一个正常的ZIP文件,必定是以 50 4B 03 04 开头,中间是文件数据,尾部则是中央目录区,并以 50 4B 05 06 结尾。
在十六进制编辑器中,我们会遇到以下四大核心签名(都以 50 4B,即 "PK" 开头):
| 签名 (Hex) | 结构名称 | 位置 | 作用 |
|---|---|---|---|
50 4B 03 04 |
本地文件头 (LFH) | 每个文件数据之前 | 描述单个文件的属性、加密标志 |
50 4B 07 08 |
数据描述符 (DD) | 文件数据之后(可选) | 用于流式写入时补充大小和CRC |
50 4B 01 02 |
中央目录项 (CDFH) | 文件数据之后,尾部区域 | 汇总所有文件的属性,相当于“文件清单” |
50 4B 05 06 |
中央目录结束记录 (EOCD) | 整个文件的最末尾 | 标志ZIP结束,记录清单的大小和偏移量 |
1.1 本地文件头 (LFH) 完整解析
本地文件头是压缩包里每个文件前面紧跟的真实数据头,记录了该文件的具体属性。 前 30 个字节是固定长度,基本结构如下:
偏移 0x00: 50 4B 03 04 ← 本地文件头标志 (PK开头)
偏移 0x04: 14 00 ← 解压所需版本 (低字节 14 = 2.0)
偏移 0x06: 01 00 ← 通用位标记 (Bit 0 = 1 表示已加密,伪加密常改这里)
偏移 0x08: 00 00 ← 压缩方法 (00 = ZIP_STORE 仅储存,08 = Deflate)
偏移 0x0A: 时间戳 (2字节) ← 最后修改时间
偏移 0x0C: 日期 (2字节) ← 最后修改日期
偏移 0x0E: CRC-32 (4字节) ← 未压缩数据的校验码
偏移 0x12: 压缩后大小 (4字节)
偏移 0x16: 未压缩大小 (4字节)
偏移 0x1A: 文件名长度 (2字节)
偏移 0x1C: 扩展字段长度 (2字节)
偏移 0x1E: 文件名 (变长)
...扩展字段...
[文件数据区]
1.2 中央目录项 (CDFH) 关键字段
中央目录项位于文件尾部,汇总了所有文件的信息。
偏移 0x00: 50 4B 01 02 ← 目录项标志
偏移 0x04: 14 03 ← 创建版本(低字节 14 = 2.0,高字节 03 = Unix 系统)
偏移 0x06: 14 00 ← 解压所需版本(应与本地文件头一致)
偏移 0x08: 01 00 ← 通用位标记(Bit 0 = 1 表示已加密,伪加密常改这里)
1.3 中央目录结束记录 (EOCD) 详解
EOCD 位于整个文件的最末尾,标准结构如下:
50 4B 05 06 00 00 00 00 01 00 01 00 38 00 00 00 A2 14 AC 02 00 00
各字段含义如下:
50 4B 05 06(PK结尾):EOCD 固定签名。00 00:当前磁盘编号(通常为0)。00 00:中央目录开始的磁盘编号(通常为0)。01 00:本磁盘上的中央目录记录数。01 00:中央目录记录总数(总共1个文件)。38 00 00 00:中央目录的大小(0x38 = 56 字节)。A2 14 AC 02:中央目录相对于文件开头的偏移量(告诉软件去哪个位置找前面的50 4B 01 02)。00 00:ZIP文件注释的长度。
二、实战解析:ZIP套ZIP与伪加密的二进制特征
2.1 ZIP 套 ZIP(嵌套结构)
- 当采用“仅储存”模式进行ZIP套ZIP时,外层ZIP的压缩方法字段是
00 00(ZIP_STORE),数据没有任何压缩。 - 因此,内层 ZIP 的完整二进制数据(包括其自身的
50 4B 03 04头和50 4B 05 06结尾)会原封不动地摆放在外层 ZIP 的数据区。 - 在十六进制编辑器的 ASCII 区域,可以直接看到内层 ZIP 的文件名(例如
signal.zip)。 - 分析思路:遇到这种情况,可以直接提取内层完整的数据块,或者逐层剥离(先解决外层,再分析内层)。
2.2 伪加密的标志位修改(实测关键结论)
- 在本地文件头(
50 4B 03 04偏移0x06)或中央目录项(50 4B 01 02偏移0x08)处,存放着“通用位标记”。 - 实测结论:经过测试,如果只有文件头或只有中央目录项其中之一被添加了加密标记(
01 00),现代压缩软件(如 7-Zip 等)会直接把它当成未加密处理并正常解压。 - 原因:软件在读取时往往会交叉验证。只有当这两处的通用位标记同时被修改为
01 00时,软件才会真正认为该文件已加密,从而弹出密码输入框(尽管实际数据并未加密,这就是典型的伪加密欺骗)。
三、ZIP 伪加密
3.1 原理与修复
ZIP 伪加密的本质是标志位欺骗。真正的伪加密需要将本地文件头和中央目录项的通用位标记同时改为 01 00。
如何修复伪加密:
在十六进制编辑器中,搜索 50 4B 03 04 找到本地文件头,将其偏移 0x06 处的 01 00 改为 00 00;再搜索 50 4B 01 02 找到中央目录项,将其偏移 0x08 处的 01 00 也改为 00 00。两处统一改回未加密状态后,即可正常解压。
3.2 CTF 快速判断思路
- 十六进制查看:搜索
50 4B 03 04和50 4B 01 02,对比两处的通用位标记是否均为01 00。 - 两处均为
01 00但解压报错 → 尝试修改为00 00测试是否为伪加密。 - 只有一处为
01 00→ 现代软件会自动无视并当成未加密处理,无需修改也能直接解压。 - 标记一致且确实加密 → 根据压缩方法字段判断是真加密类型(见下文)。
- 存在 ZIP 套 ZIP → 先解决外层,提取出内层 ZIP,再对内层进行加密类型分析。
3.3 速记
0304开头0102结尾 头空二字节尾巴空四字节
四、ZipCrypto 加密与已知明文攻击(KPA)
4.1 ZipCrypto 的固有弱点
ZipCrypto 使用三个 32 位状态变量(key0、key1、key2)生成密钥流,并与明文异或得到密文。Biham 和 Kocher 证明,只要知道至少 12 个字节的明文,且其中至少 8 个字节连续,就可以恢复密钥流生成器的内部状态。恢复状态后,不仅能解密整个文件,还能解密同一密码保护的其他条目。
注意:KPA 需要的“明文”是 ZipCrypto 加密前的原始字节流。如果文件经过了 Deflate 压缩,你需要知道的是“压缩后”的字节。因此,ZipCrypto Store(仅储存)模式最适合 KPA。
4.2 Windows:bkcrack 实战
bkcrack 是当前最主流的 ZipCrypto KPA 工具,Windows 有预编译版本。
- Step 1:确认加密类型
bkcrack -L archive.zip(确认是 ZipCrypto 而非 AES) - Step 2:准备明文
情况 A(完整明文):有与包内文件相同的完整文件。
情况 B(头部特征):如 PNG 文件头前 16 字节固定为
89504E470D0A1A0A0000000D49484452,可以构造明文文件:echo 89504E470D0A1A0A0000000D49484452 | xxd -r -ps > png_header.hex - Step 3:发起攻击
bkcrack -C flag.zip -c flag.png -p png_header.hex -o 0(若已知字节不连续或不足12字节,可用-x 偏移 十六进制补充。成功后输出三组 8 位十六进制内部密钥,如ab7d8bcd 6ce75578 4de51c12) - Step 4:解密文件
bkcrack -C flag.zip -c flag.png -k ab7d8bcd 6ce75578 4de51c12 -d flag.png(若原文件是 Deflate 压缩,解密后需用inflate.py解压) - Step 5:移除密码或爆破原始密码 `bkcrack -C flag.zip -k -U out.zip` (生成无密码新包)
4.3 Linux:pkcrack 与 bkcrack
- pkcrack(经典C实现):
pkcrack -C encrypted.zip -c "file.txt" -P plaintext.zip -p "file.txt" -d decrypted.zip - bkcrack(更推荐,功能更强):Linux 下编译安装
cmake .. && make,命令与 Windows 完全一致,对“部分已知明文 + 零散字节”支持更灵活。
4.4 KPA 实战注意事项
- 确认压缩方式:确保目标是 ZipCrypto(而非 AES)。
- Store 优先:Deflate 需额外解压步骤,且明文必须是“压缩后”的字节流。
- 格式文件头:PNG、JPEG、ELF、EXE 等格式的固定头部是 CTF 的常见突破口。
- 恢复内部密钥 ≠ 原始密码:恢复密钥后足以解密文件,原始密码需额外爆破反推。
五、ZIP AES 加密(WinZip AES)
WinZip 9.0 引入的 AES 加密扩展(AE-1 / AE-2),通过 Extra Field (ID=0x9901) 和 Compression Method = 99 来承载。它是目前 ZIP 中安全强度最高的方案。
5.1 密钥派生 (PBKDF2)
- 使用 PBKDF2-HMAC-SHA1(迭代 1000次)。
- 输入:用户密码 + 随机盐值 (Salt)。
- 派生:加密密钥(AES-CTR)、认证密钥(HMAC-SHA1)、密码验证值(2字节)。
- 盐值长度:AES-128 为 8字节,AES-192 为 12字节,AES-256 为 16字节。
5.2 数据布局
[Salt] → [2字节密码验证值] → [AES-CTR加密后的压缩数据] → [10字节 HMAC-SHA1 认证码]
5.3 AE-1 与 AE-2 的区别
- AE-1:保留原始 CRC-32 值,便于兼容旧工具。
- AE-2:CRC 字段置为 0,完全依赖 AES 认证码保证完整性(推荐,避免 CRC 泄露明文信息)。
六、方案对比与 CTF 实战决策
| 场景/特征 | 判断依据 | 首选工具/方法 |
|---|---|---|
| 伪加密 | 标志位两处均为 01 00,但数据可见 |
十六进制编辑器,两处均改回 00 00 |
| 仅单处标记 | 只有一处为 01 00 |
软件会自动当成未加密,直接解压 |
| 真 ZipCrypto 加密 | Method 非 99,有 12 字节加密头 |
bkcrack(Win/Linux)或 pkcrack(Linux) |
| 仅知部分零散字节 | 不足 12 字节已知明文 | bkcrack 的 -x 稀疏明文模式 |
| AES 加密 | Method = 99,存在 0x9901 Extra Field |
无法 KPA,只能暴力/字典破解 |
| 嵌套 ZIP | 外层 Store + 内层存在 | 先提取内层,再对内层判断加密类型 |
核心总结:处理 ZIP 文件,先看头(50 4B 03 04)、中(50 4B 01 02)、尾(50 4B 05 06)。伪加密必须两处同改才有效,单改一处会裸奔。遇到真正的 ZipCrypto 加密,优先寻找文件头作为已知明文,用 bkcrack 进行 KPA 攻击恢复内部密钥。遇到 Method 为 99 的 AES 加密,则放弃 KPA,转向密码爆破或字典攻击。

Comments NOTHING