Zip Bomb 的演进:从递归嵌套到非递归重叠压缩
Zip Bomb(压缩炸弹),又称 "Zip of Death" 或解压炸弹,是一种利用压缩算法特性构造的恶意档案文件。传统认知中,zip bomb 主要通过递归嵌套实现 —— 一个压缩包内包含另一个压缩包,层层嵌套,最终在解压时产生指数级的数据膨胀。经典的 "42.zip" 文件仅 42KB,解压后可达 4.5PB,压缩比超过 1000 亿倍。
然而,2025 年的安全态势显示,zip bomb 攻击已经进化。根据 Penligent.ai 2025 年 12 月的分析,现代 zip bomb 不再局限于简单的递归嵌套,而是采用了更复杂的非递归重叠压缩技术。这种技术通过让多个文件头(header)引用同一高度压缩的数据块,实现惊人的压缩比 —— 可达 2800 万倍以上。
2025 年新型压缩算法实现:工程细节与内存边界
非递归重叠压缩的核心机制
非递归 zip bomb 不依赖嵌套结构,而是利用 ZIP 格式的设计特性:一个压缩档案可以包含多个文件条目,每个条目指向相同的压缩数据。攻击者通过精心构造,让数百甚至数千个文件头都引用同一段高度压缩的数据(通常是全零或重复模式)。
工程实现上,这种攻击的关键在于:
- 数据块复用:创建一个小型的高度压缩数据块(如 1MB 压缩到 1KB)
- 多 header 引用:生成大量文件条目,每个都指向该数据块
- 压缩比欺骗:解压工具会为每个文件条目分配解压缓冲区,导致内存消耗呈线性增长而非指数增长,但总量依然惊人
内存安全边界的挑战
现代编程语言如 Go 的zip.Reader默认不递归读取嵌套档案,这在一定程度上防御了传统递归 zip bomb。然而,非递归重叠压缩仍然构成威胁。Go 的标准库实现需要开发者手动设置以下边界:
// 内存安全边界设置示例
reader, err := zip.NewReader(file, fileSize)
if err != nil {
return err
}
// 边界1:最大文件数限制
if len(reader.File) > 1000 {
return errors.New("too many files in archive")
}
// 边界2:单个文件最大解压大小
for _, f := range reader.File {
if f.UncompressedSize64 > 50*1024*1024 { // 50MB
return errors.New("file too large")
}
// 边界3:压缩比限制
if f.UncompressedSize64 > 0 && f.CompressedSize64 > 0 {
ratio := float64(f.UncompressedSize64) / float64(f.CompressedSize64)
if ratio > 1032 { // DEFLATE算法理论最大压缩比
return errors.New("suspicious compression ratio")
}
}
}
2025 年实际漏洞案例:从资源耗尽到权限提升
CVE-2025-46730:MobSF 的磁盘空间耗尽漏洞
2025 年披露的 CVE-2025-46730 影响 Mobile Security Framework(MobSF)。该漏洞允许攻击者上传特制的 zip bomb 文件,在解压过程中耗尽服务器磁盘空间。关键在于应用程序没有在解压前检查总解压大小,一个 12-15MB 的压缩文件可以膨胀到数 GB,导致服务拒绝。
7-Zip 双重漏洞:CVE-2025-11001 与 CVE-2025-0411
更危险的是,2025 年 7-Zip 曝出的漏洞展示了 zip bomb 攻击的新维度:
- CVE-2025-11001:符号链接处理不当,允许精心构造的 ZIP 档案在解压时写入目标目录之外的文件,实现路径遍历
- CVE-2025-0411:Mark-of-the-Web(MoTW)绕过漏洞,使嵌套档案能够逃避 Windows 安全检查
这两个漏洞的共同点是:将资源耗尽攻击与权限提升机制结合。攻击者不再满足于简单的拒绝服务,而是通过 zip bomb 制造系统混乱,然后利用解析漏洞获得更高权限。
防御检测机制:工程化参数与监控体系
四级防御策略
基于 2025 年的攻击模式,建议建立四级防御体系:
1. 预解压静态分析
在解压前对档案进行静态分析,设置以下硬性限制:
- 最大文件数:≤1000 个
- 最大压缩档案大小:≤100MB
- 预估最大解压大小:≤1GB(基于文件头信息计算)
2. 流式解压与实时监控
采用流式解压而非完整解压到内存 / 磁盘:
import zipfile
MAX_EXTRACTED = 1024 * 1024 * 1024 # 1GB
extracted = 0
with zipfile.ZipFile('archive.zip') as zf:
for info in zf.infolist():
with zf.open(info) as f:
chunk_size = 8192
while True:
chunk = f.read(chunk_size)
if not chunk:
break
extracted += len(chunk)
if extracted > MAX_EXTRACTED:
raise Exception("Extraction limit exceeded")
# 处理chunk...
3. 资源沙箱化
在容器环境中运行解压操作,设置硬性资源限制:
docker run --memory=512m --cpus=1 --storage-opt size=10G \
-v $(pwd):/data alpine unzip /data/archive.zip -d /tmp
4. 递归深度检测
虽然非递归 zip bomb 不依赖嵌套,但仍需检测递归结构:
def check_nesting(zip_file, max_depth=3, current_depth=0):
if current_depth > max_depth:
return False, "Too many nested archives"
for item in zip_file.infolist():
if item.filename.endswith(('.zip', '.tar', '.gz')):
# 递归检查嵌套档案
nested = zipfile.ZipFile(zip_file.open(item))
return check_nesting(nested, max_depth, current_depth+1)
return True, "OK"
压缩比异常检测算法
非递归 zip bomb 的核心特征是异常压缩比。检测算法应考虑:
- 理论边界检查:DEFLATE 算法的理论最大压缩比约为 1032:1,超过此值即为异常
- 统计异常检测:计算档案内所有文件的平均压缩比,识别离群值
- 模式识别:检测大量文件具有完全相同压缩大小的模式(重叠压缩特征)
def detect_compression_anomaly(zip_file):
compression_ratios = []
file_sizes = {}
for info in zip_file.infolist():
if info.compress_size > 0:
ratio = info.file_size / info.compress_size
compression_ratios.append(ratio)
# 检测相同压缩大小的文件
key = (info.compress_size, info.file_size)
file_sizes[key] = file_sizes.get(key, 0) + 1
# 检查异常压缩比
if max(compression_ratios) > 1032:
return True, "Theoretical compression ratio exceeded"
# 检查重叠压缩模式
for size, count in file_sizes.items():
if count > 50: # 超过50个文件具有相同大小特征
return True, f"Suspicious overlapping pattern: {count} files with same size"
return False, "Normal"
可落地参数清单:生产环境配置指南
基础防御参数(必须配置)
| 参数类别 | 推荐值 | 说明 |
|---|---|---|
| 最大文件数 | 1000 | 单个 ZIP 档案内最多文件数 |
| 单个文件最大大小 | 50MB | 解压后单个文件最大尺寸 |
| 总解压大小限制 | 1GB | 整个档案预估最大解压大小 |
| 最大压缩比 | 1032 | 基于 DEFLATE 算法理论最大值 |
| 递归深度 | 3 | 允许的最大嵌套层数 |
| 解压超时 | 30 秒 | 解压操作最长执行时间 |
高级监控指标
-
资源消耗监控:
- 解压过程内存峰值(应≤512MB)
- 解压过程 CPU 使用率(应≤80% 持续 10 秒)
- 磁盘 I/O 速率(应≤100MB/s 持续写入)
-
异常模式检测:
- 相同压缩大小的文件数量(阈值:50 个)
- 压缩比分布标准差(异常:>500)
- 文件类型一致性(全部为 0 字节文件或重复模式)
-
运行时保护:
- 使用
RLIMIT_AS设置地址空间限制 - 使用
setrlimit限制进程资源 - 在 cgroup 中运行解压进程
- 使用
Go 语言具体实现示例
package safezip
import (
"archive/zip"
"errors"
"io"
"time"
)
type Config struct {
MaxFiles int
MaxFileSize int64
MaxTotalSize int64
MaxCompressionRatio float64
MaxDepth int
Timeout time.Duration
}
func SafeExtract(r io.ReaderAt, size int64, config Config) error {
if config.Timeout > 0 {
// 设置超时控制
}
zr, err := zip.NewReader(r, size)
if err != nil {
return err
}
// 应用所有限制
if err := applyLimits(zr, config); err != nil {
return err
}
// 安全解压
return extractWithLimits(zr, config)
}
func applyLimits(zr *zip.Reader, config Config) error {
if len(zr.File) > config.MaxFiles {
return errors.New("too many files")
}
var totalSize int64
for _, f := range zr.File {
if f.UncompressedSize64 > uint64(config.MaxFileSize) {
return errors.New("file too large")
}
if f.CompressedSize64 > 0 {
ratio := float64(f.UncompressedSize64) / float64(f.CompressedSize64)
if ratio > config.MaxCompressionRatio {
return errors.New("suspicious compression ratio")
}
}
totalSize += int64(f.UncompressedSize64)
}
if totalSize > config.MaxTotalSize {
return errors.New("total size too large")
}
return nil
}
结论:2025 年的压缩安全新范式
Zip bomb 在 2025 年并未过时,反而以更复杂的形式持续威胁现代系统。非递归重叠压缩技术的出现,要求安全工程师重新审视压缩档案的处理逻辑。单纯的递归深度检测已不足够,必须结合压缩比分析、模式识别和资源沙箱化等多层防御。
关键要点总结:
- 算法演进:从递归嵌套到非递归重叠,压缩比可达 2800 万倍
- 攻击复合化:资源耗尽与权限提升漏洞结合(如 7-Zip 漏洞)
- 防御体系化:需要静态分析、流式处理、资源限制、实时监控四层防护
- 参数具体化:必须设置文件数、大小、压缩比、递归深度等硬性边界
在自动化处理压缩档案的 CI/CD 流水线、云服务、邮件网关等场景中,实施上述防御策略不再是可选,而是必须。只有通过工程化的参数设置和系统化的监控体系,才能在享受压缩便利的同时,避免成为 zip bomb 的下一个受害者。
资料来源:
- Penligent.ai - "Zip of Death Explained (2025): How Decompression Bombs Still Crash Systems and What You Can Do" (2025-12-15)
- Stack Overflow - "How does one make a Zip bomb?" 讨论中的技术细节