zip压缩包取证笔记

ZIP 格式详解

简述

ZIP 文件格式是一种数据压缩和文档储存的文件格式,原名 Deflate(压缩算法),现通常以 .zip 后缀表示该类压缩文件。

目前来说 ZIP 是一种主流的压缩方式,但存在许多缺陷,例如明文攻击、字典攻击和暴力破解等。

可以使用 Bandizip 等多种主流工具进行 Deflate 压缩。

数据

ZIP 压缩包的数据以块为单位,常见的有以下几个块:

文件数据块(ZIPFILERECORD

名字 字节 意义
Signature 4 值为504B0304,是 ZIP 特有签名
Version 2 解压文件所需版本
Flags 2 通用标志位
Compression 2 压缩方式
FileTime 2 被压缩文件最后修改时间(DOSTIME类型)
FileDate 2 被压缩文件最后修改日期(DOSDATE类型)
Crc 4 CRC 校验码
CompressedSize 4 压缩后大小(同时也是 Data 块大小)
UncompressedSize 4 文件原始大小(即解压后的大小)
FileNameLength 2 被压缩文件名长度
ExtraFieldLength 2 扩展区长度
FileName FileNameLength 被压缩文件名
Data CompressedSize 被压缩文件数据
ExtData ExtraFieldLength 扩展区数据

其中默认压缩版本一般为 20,即 Deflate 压缩方式。

Version

当前最低功能版本定义如下:(压缩包记录的解压版本都是需要版本10,比如:2.0 10 = 20)

  • 1.0 - 默认值

  • 1.1 - 文件是卷标

  • 2.0 - 文件是一个文件夹(目录)

  • 2.0 - 使用 Deflate 压缩来压缩文件

  • 2.0 - 使用传统的 PKWARE 加密对文件进行加密

  • 2.1 - 使用 Deflate64™ 压缩文件

  • 2.5 - 使用 PKWARE DCL Implode 压缩文件

  • 2.7 - 文件是补丁数据集

  • 4.5 - 文件使用 ZIP64 格式扩展

  • 4.6 - 使用 BZIP2 压缩文件压缩

  • 5.0 - 文件使用 DES 加密

  • 5.0 - 文件使用 3DES 加密

  • 5.0 - 使用原始 RC2 加密对文件进行加密

  • 5.0 - 使用 RC4 加密对文件进行加密

  • 5.1 - 文件使用 AES 加密进行加密

  • 5.1 - 使用更正的 RC2 加密对文件进行加密

  • 5.2 - 使用更正的 RC2-64 加密对文件进行加密

  • 6.1 - 使用非 OAEP 密钥包装对文件进行加密

  • 6.2 - 中央目录加密

Flags

第0位为1时(即0x1),表示文件被加密;当第3位为1时(即0x3),表示有数据描述部分

有时虽然第3位为1,但仍然本地头存有真实值(理论上文件头内的描述应该被设为0,然后将描述放在数据描述部分)。

Compression

不同的值代表不同的压缩方式,默认为 8,即 Deflate 压缩。

  • 0 - The file is stored (no compression)
  • 1 - The file is Shrunk
  • 2 - The file is Reduced with compression factor 1
  • 3 - The file is Reduced with compression factor 2
  • 4 - The file is Reduced with compression factor 3
  • 5 - The file is Reduced with compression factor 4
  • 6 - The file is Imploded
  • 7 - Reserved for Tokenizing compression algorithm
  • 8 - The file is Deflated
  • 9 - Enhanced Deflating using Deflate64™
  • 10 - PKWARE Data Compression Library Imploding
  • 11 - Reserved by PKWARE
  • 12 - File is compressed using BZIP2 algorithm

Crc

该 CRC 校验码为文件未压缩时的 CRC-32 校验码,在 Python 中可借助 binascii 模块进行计算。

1
2
import binascii
crc = binascii.crc32('hello')

文件目录块(ZIPDIRENTRY

一个数据块就对应一个目录块,其中 FlagsCompressionFileTimeFileDateCrcCompressedSizeUncompressedSizeFileNameLengthExtraFieldLength 与数据块同值。

名字 字节 意义
Signature 4 值为504B0102,是 ZIP 特有签名
VersionModeBy 2 压缩版本
VersionToExtract 2 解压所需最小版本
Flags 2 通用标志位
Compression 2 压缩方式
FileTime 2 被压缩文件最后修改时间(DOSDATE类型)
FileDate 2 被压缩文件最后修改日期(DOSDATE类型)
Crc 4 CRC 校验码
CompressedSize 4 压缩后大小(同时也是 Data 块大小)
UncompressedSize 4 文件原始大小(即解压后的大小)
FileNameLength 2 被压缩文件名长度
ExtraFieldLength 2 扩展区长度
FileCommentLength 2 文件注释的长度
DiskNumberStart 2 文件开始位置的磁盘编号(默认为0)
InternalAttributes 2 内部文件属性
ExternalAttributes 4 外部文件属性
HeaderOffset 4 数据区本地文件头相对于压缩包开始位置的偏移量
FileName FileNameLength 被压缩文件名
ExtraField ExtraFieldLength 扩展区数据
Comment FileCommentLength 注解

ZIP 尾块(ZIPENDLOCATOR

名字 字节 意义
Signature 4 值为504B0506,是 ZIP 尾特有签名
DiskNumber 2 中央目录记录尾部区所在磁盘编号
StartDiskNumber 2 中央目录开始位置所在的磁盘编号
EntriesOnDisk 2 该磁盘上所记录的核心目录数量
EntriesInDirectory 2 zip压缩包中的文件总数
DirectorySize 4 目录大小
DirectoryOffset 4 目录开始位置的相对偏移量
CommentLength 2 注解长度
Comment CommentLength 注解

需要注意的是,一个 ZIP 文件只有一个 ZIP 尾块,如果出现多个尾块可能是文件损坏或多文件重合。

ZIP 破解

伪加密

原理

在 ZIP 文件中,数据区与目录区中的 Flags 应该是相同的值。当数据区的标志位为加密标志,而目录区的标志位为非加密标志时,这个时候许多 ZIP 软件会将其识别为被加密,但实际上并不存在对应的密码。

破解

只需要将数据区和目录区的标志位置为0即可。

弱密码爆破

原理

Deflate 压缩方式的解压速度是十分优秀的,但是这也导致我们进行爆破的效率十分之高。

破解

一般来说推荐使用 ARCHPR 对 ZIP 压缩包进行暴力破解处理。一般是范围内枚举(例如纯数字或数字与字母的排列组合)或是使用专有的字典进行弱密码爆破。

同时也可以使用 hashcat 和 bkcrack 对 ZIP 的密码进行破解。

CRC 碰撞

原理

当压缩文件内容较少或仅有部分明文未知时,可以采用 CRC 碰撞的方式去暴力破解明文(因为压缩包中保存的 CRC 校验值为压缩前内容的校验)。

破解

可以使用 Python 脚本进行爆破,但下面的脚本仅是示例,如果有特别的需求(例如未知明文在已知明文中间段时)需要自行修改脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import binascii
import itertools

def CRCcrack(unknownBytes: int, targetCRC: int, known: bytes = b'') -> list[bytes]:
"""
CRC 碰撞
Parameters:
unknownBytes - 未知的字节数
known - 已知明文(默认为拼接)
Returns:
完整明文
"""
rst = []
for m in itertools.product(range(128), repeat=unknownBytes):
msg = known + bytes(m)
crc = binascii.crc32(msg)
if crc == targetCRC:
rst.append(msg)
return rst

print(CRCcrack(3, binascii.crc32(b'123')))

如果仅是碰撞,我们可以使用 GitHub 上的开源项目(CRC32 Tools

使用方法:

1
python crc32.py reverse 0x4FA83D8C

需要注意的是,因为 CRC-32 是哈希算法的一种,所以可能会存在多个明文的 CRC-32 值相等。

CRC32 Tools 不带有特殊字符,如果明文存在特殊字符需要手动添加在脚本中。

明文攻击

原理

ZIP 加密模式本质上是将密码转化为内置的一个密钥进行加密的,而密钥是有限长度的,所以如果我们知道一个压缩包内其中一个文件的完整明文,那么我们就可以通过算法将密钥百分百地计算出来(理论上可以把密码也百分百计算出来),且理论上破解速度只与被压缩文件大小有关。

破解

推荐使用 ARCHPR 的明文攻击模式进行破解,需要一个明文压缩包和含有明文文件的密文压缩包,其中他们的压缩算法应该是相同的。

同时也可以使用 bkcrack 进行破解。

注意

  • 文本文件如果编码不同(例如 UTF-8GBK)可能导致压缩出来的大小不同,所以各种编码都需要尝试一次。
  • 不同的压缩软件压缩出来的文件可能会导致 ARCHPR 报错,一般推荐 Bandizip 与 WinRAR 都尝试一次。
  • 铭记:WinRAR 可能修改了 Deflate 算法,不同版本、不同系统下的压缩内容 ARCHPR 可能无法识别。

深入明文攻击

原理

ZIP 加密模式本质上是将密码转化为内置的一个密钥进行加密的,而密钥是有限长度的,而且 ZIP 的加密方式使得我们可以只解密部分密文,如果我们知道某个文件的部分密文,那么我们可以遍历所有可能的密钥去进行比对(类似 CRC 碰撞的原理)。

但仅有 ZipCrypto Deflate 算法和 ZipCrypto Store 算法可以用此方式破解。

破解

使用 bkcrack 需要已知明文的十二个字节和偏移,其中八个字节必须连续。

需要使用到的参数:

参数 含义
-C file.zip 加密压缩包
-c file 提取的密文文件(密文部分)
-p key.txt 指定明文部分
-x offset hex 指定偏移量和部分明文十六进制值
-o offset 指定明文部分的偏移量
-k key1 key2 key3 指定三段解密密钥
-d file 解压缩得到的目标文件

例如针对 JPG 的压缩包,我们可以使用以下命令:

首先生成明文文件:

1
echo -n "FFD8FFE000104A4649460001" | xxd -r -ps > key.txt

随后进行密钥爆破:

1
bkcrack -C 1.zip -c t.jpg -p key.txt -o 0

或者是针对 ZIP 的压缩包,我们可以使用以下命令:

首先我们这个 ZIP 中存在 flag.txt 的明文:

1
echo -n "flag.txt" > key.txt

其次我们知道 ZIP 文件的头,故进行密钥爆破:

1
bkcrack -C 1.zip -c flag.zip -p key.txt -o 30 -x 0 504B03041400

需要注意的是,当已知字节数越多时,得到的密钥越准确。

得到三段密钥后我们进行提取:

1
bkcrack -C 1.zip -c flag.zip -k 683a571e f954e70c 49da18ac -d 111.zip

需要注意的是,如果文件是使用 Deflate 算法进行压缩的话,需要使用其自带的 inflate.py 脚本(在 ./tools/ 文件夹中)进行解压缩:

1
python inflate.py < 1.jpg > 2.jpg

长密码哈希

ZIP 内部使用 PBKDF2 算法,在 AES-256 加密模式下(包括 ZIPX),如果输入密码长度超过 64 字节,它会使用 PBKDF2 重新生成密钥,新生成的哈希值作为文件的实际密码。

换句话说,就是实际密码=SHA1(输入密码)。

特殊实例

以时间戳作为密码

原理

时间戳一般是一个两位小数,ZIP 文件所保存的创建时间和更新时间并不一定与当时所获取的时间戳是一致的,也许会差几秒,所以我们需要先将文件创建时间转化为时间戳,然后进行掩码爆破。

破解

例如文件创建时间为 2019/05/17 8:25:28,我们使用如下脚本进行转化时间戳

1
2
3
4
import time

a = time.struct_time((2019,5,17,8,25,28,-1,-1,-1))
print(time.mktime(a))

得到结果 1558052728.0,设置掩码 15580?????.?? 或者 15580?????.? 爆破即可。

中文密码

中文密码在不同的系统上可能会以不同的编码作为输入,在中文Windows系统中大多使用GBK。