ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

压缩包隐写技术全解析:从ZIP结构到CTF实战与取证防御

压缩包隐写技术全解析:从ZIP结构到CTF实战与取证防御 1. 压缩包隐写到底是什么从文件结构说起很多人第一次听到“压缩包隐写”这个词脑子里浮现的画面可能是把一段秘密文字塞进压缩包里然后设个密码就完事了。实际上压缩包隐写远不止“加个密码”这么简单它属于信息隐藏Steganography的一个分支核心思路是把真正想传递的数据藏进一个看起来完全正常的压缩包文件中让接收方拿到文件时第一眼根本看不出里面还有别的东西。要理解这件事得先搞清楚压缩包本身的结构。以最常见的 ZIP 格式为例一个 ZIP 文件由三大部分组成本地文件头Local File Header、文件数据区、中央目录Central Directory最后还有一个中央目录结束记录End of Central Directory Record简称 EOCD。每个被压缩的文件都有自己的本地文件头里面记录了文件名、压缩方法、CRC-32 校验值、压缩前后大小等元信息。中央目录则相当于整本书的目录页告诉解压软件这个压缩包里到底有哪些文件、每个文件的偏移量在哪。关键点来了EOCD 记录里有一个字段叫“注释长度”Comment Length它允许在 ZIP 文件末尾附加一段最长 65535 字节的注释。很多隐写手法就是从这里下手的——把秘密数据编码后塞进注释区普通解压软件打开时只会正常解压文件根本不会去读那段注释。另一种常见做法更粗暴直接在 ZIP 文件末尾追加数据。因为大多数解压软件读取到 EOCD 就认为文件结束了后面的内容会被忽略但对于知道内情的人来说用十六进制编辑器一看就能发现端倪。还有一种思路是利用 ZIP 的“伪加密”特性。ZIP 格式中有一个“通用位标记”General Purpose Bit Flag其中第 0 位表示文件是否加密。有些工具会把这个位设为 1但实际并不对数据进行加密导致解压软件提示需要密码而实际上数据是明文存储的。这种手法在 CTF 竞赛的 Misc 方向里非常常见也是“压缩包隐写”这个词频繁出现在安全圈的原因之一。从应用场景来看压缩包隐写主要有三个方向CTF 竞赛与安全取证、日常文件伪装与隐私保护、恶意样本分析。CTF 里出题人会把 flag 藏在压缩包的注释、额外字段、甚至多个压缩包嵌套的结构里取证场景下调查人员需要从看似普通的压缩包中提取隐藏的通信内容而恶意样本分析中攻击者常把恶意载荷藏在压缩包的冗余区域来绕过静态检测。理解了这些背景后面动手操作时才知道自己在干什么、为什么要这么干。2. 核心隐写手法拆解与工具选型2.1 ZIP 注释区隐写最经典也最容易被忽略ZIP 的注释区是我个人最推荐新手入门的隐写位置原因很简单它合法、稳定、不破坏文件结构。你往注释里写东西压缩包照样能正常解压CRC 校验也不会报错。操作上Linux 下用zip命令就能直接加注释zip -z secret.zip # 然后输入注释内容以 . 单独一行结束或者用 Python 的zipfile模块import zipfile with zipfile.ZipFile(secret.zip, a) as zf: zf.comment bhidden_data_here读取的时候with zipfile.ZipFile(secret.zip, r) as zf: print(zf.comment)这里有个坑要注意注释区的最大长度是 65535 字节超过这个长度会被截断。如果你要藏的数据比较大就得考虑分片或者换其他位置。另外某些解压软件比如老版本的 WinRAR会在界面里显示注释内容所以如果你不想让人一眼看到最好对注释内容做一层编码比如 Base64 或者异或加密。2.2 文件末尾追加数据简单粗暴但有效在 ZIP 文件末尾追加数据原理是利用了解压软件“读到 EOCD 就停”的行为。你可以直接用cat命令拼接cat original.zip secret.txt stego.zip这样生成的stego.zip依然可以正常解压因为解压软件读到 EOCD 就认为文件结束了后面的secret.txt内容会被忽略。但如果你用binwalk或者十六进制编辑器查看就能明显看到 ZIP 结构结束后还有一段额外数据。注意这种方法虽然简单但容易被自动化工具检测到。binwalk、foremost这类文件 carving 工具会直接把追加的数据识别为独立文件。如果用于 CTF出题人通常会在这段追加数据上再做一层编码或加密。2.3 伪加密让解压软件“以为”需要密码伪加密的核心是修改 ZIP 本地文件头和中央目录中的“通用位标记”。具体来说把每个文件头的第 6 个字节从 0 开始数的第 0 位设为 1同时确保中央目录中对应文件的标记位也一致。这样解压软件会提示“需要密码”但实际上数据并没有被加密。手动修改可以用十六进制编辑器但更高效的方式是用 Python 脚本批量处理import struct def fake_encrypt(zip_path, output_path): with open(zip_path, rb) as f: data bytearray(f.read()) # 遍历本地文件头找到 PK\x03\x04 签名 pos 0 while True: pos data.find(bPK\x03\x04, pos) if pos -1: break # 通用位标记在偏移 6 处2 字节 flag struct.unpack_from(H, data, pos 6)[0] flag | 0x0001 # 设置加密位 struct.pack_into(H, data, pos 6, flag) pos 4 with open(output_path, wb) as f: f.write(data)对应的破解伪加密就是把这个位清零。CTF 中常见的“压缩包密码怎么解除”问题很多时候根本不是真的加密而是伪加密在作怪。判断方法很简单用7z l -slt查看文件详情如果显示Encrypted -但解压又要密码那基本就是伪加密。2.4 多压缩包嵌套与结构混淆进阶一点的玩法是把多个压缩包嵌套在一起或者利用 ZIP 格式允许“一个文件被多次记录”的特性。比如你可以创建一个 ZIP里面包含两个同名文件一个是真的一个是假的。解压软件通常会解出最后一个但用unzip -l可以看到两个条目。还有一种手法是利用 ZIP 的“数据描述符”Data Descriptor。当 ZIP 以流式方式生成时本地文件头中的 CRC 和大小字段可能为 0真实值放在文件数据之后的 Data Descriptor 中。有些工具在解析时会忽略 Data Descriptor导致读取到的文件内容出现偏差出题人就可以利用这个偏差来隐藏信息。工具选型方面我常用的组合是工具用途适用场景binwalk自动识别嵌套文件快速发现追加数据、嵌套压缩包010 Editor十六进制编辑与模板解析手动修改标志位、查看结构zipdetails详细解析 ZIP 结构分析中央目录、本地文件头差异Python zipfile脚本化批量处理自动化隐写与提取7z高兼容性解压处理异常 ZIP 结构实操心得遇到“压缩包分析”类题目第一步永远是用binwalk -e跑一遍看看有没有嵌套文件第二步用zipdetails看结构有没有异常第三步再考虑伪加密和注释区。这个顺序能帮你省下大量时间。3. 完整实操流程从隐藏到提取的闭环3.1 环境准备与基础文件构造先准备一个正常的压缩包作为载体。我习惯用命令行生成因为可控性强mkdir payload echo This is a normal file. payload/normal.txt zip -r carrier.zip payload/这样得到的carrier.zip就是一个标准的 ZIP 文件。接下来我们要往里面藏东西。假设要隐藏的内容是flag{stego_is_fun}先做一层 Base64 编码echo -n flag{stego_is_fun} | base64 # 输出ZmxhZ3tzdGVnb19pc19mdW59然后把这串 Base64 写入 ZIP 注释区import zipfile with zipfile.ZipFile(carrier.zip, a) as zf: zf.comment bZmxhZ3tzdGVnb19pc19mdW59现在carrier.zip表面上和普通压缩包没有任何区别解压出来只有normal.txt。但用zipdetails或者 Python 读取注释就能拿到隐藏内容。3.2 多层隐写的叠加与提取实际场景中单一手法往往不够需要叠加多层。我设计一个三层隐写的例子第一层在 ZIP 注释区写入 Base64 编码的提示信息。第二层在文件末尾追加一个用异或加密的文本文件。第三层把真正的 flag 拆分成两半一半藏在伪加密的标志位里通过标志位的奇偶性编码另一半藏在追加数据的文件名中。先构造追加数据import os # 异或加密 def xor_encrypt(data, key0x5A): return bytes([b ^ key for b in data]) secret bpart2_of_flag encrypted xor_encrypt(secret) with open(carrier.zip, ab) as f: f.write(b\n---HIDDEN---\n) f.write(encrypted)提取的时候先用binwalk定位追加数据的起始偏移然后读取并异或解密with open(carrier.zip, rb) as f: data f.read() marker b\n---HIDDEN---\n idx data.find(marker) if idx ! -1: encrypted data[idx len(marker):] decrypted xor_encrypt(encrypted) print(decrypted)这种多层叠加的思路在 CTF 中非常常见出题人会把不同线索分散在不同位置考验的是选手对 ZIP 格式的全面理解。3.3 自动化提取脚本的编写手动分析效率太低我通常会写一个综合提取脚本把常见隐写位置都扫一遍import zipfile import struct import binwalk def analyze_zip(path): print(f[*] Analyzing {path}) # 1. 检查注释区 with zipfile.ZipFile(path, r) as zf: if zf.comment: print(f[] Comment found: {zf.comment}) # 2. 检查伪加密 with open(path, rb) as f: data f.read() pos 0 while True: pos data.find(bPK\x03\x04, pos) if pos -1: break flag struct.unpack_from(H, data, pos 6)[0] if flag 0x0001: print(f[] Fake encryption detected at offset {pos}) pos 4 # 3. 检查追加数据 eocd_pos data.rfind(bPK\x05\x06) if eocd_pos ! -1: comment_len struct.unpack_from(H, data, eocd_pos 20)[0] extra_start eocd_pos 22 comment_len if extra_start len(data): print(f[] Extra data after EOCD: {len(data) - extra_start} bytes) print(f Preview: {data[extra_start:extra_start64]}) if __name__ __main__: analyze_zip(carrier.zip)这个脚本覆盖了注释区、伪加密、追加数据三个最常见的隐写位置。实际使用时根据输出结果再决定下一步怎么处理。注意事项binwalk在 Python 中调用需要安装binwalk模块而且不同版本 API 有差异。如果只是做结构分析直接用binwalk命令行工具更省事。4. 常见问题与排查技巧实录4.1 解压报错但密码明明是对的这种情况十有八九是伪加密或者文件头被篡改。排查步骤用7z l -slt carrier.zip查看每个文件的Encrypted字段。如果显示-但解压要密码就是伪加密。用十六进制编辑器对比本地文件头和中央目录中的通用位标记是否一致。不一致的话以中央目录为准把本地文件头的标志位改回去。如果标志位正常但还是报错检查 CRC 值是否被修改。CRC 不匹配会导致解压失败这时候需要用zip -FF修复或者手动重算 CRC。4.2 binwalk 识别不出追加数据binwalk的识别依赖于文件签名如果追加的数据没有明显的文件头比如纯文本它可能不会报出来。这时候可以手动计算 EOCD 结束位置import struct with open(carrier.zip, rb) as f: data f.read() eocd_pos data.rfind(bPK\x05\x06) comment_len struct.unpack_from(H, data, eocd_pos 20)[0] extra_start eocd_pos 22 comment_len print(fExtra data starts at: {extra_start}) print(fExtra data length: {len(data) - extra_start})4.3 注释区内容乱码ZIP 注释区默认使用 CP437 编码如果你写入的是 UTF-8 中文读出来就会乱码。解决办法是写入前先做 Base64 编码或者读取时用正确的编码解码with zipfile.ZipFile(carrier.zip, r) as zf: comment zf.comment try: print(comment.decode(utf-8)) except UnicodeDecodeError: print(comment.decode(cp437))4.4 常见问题速查表问题现象可能原因排查方法解决手段解压提示需要密码伪加密或真加密查看通用位标记清除标志位或爆破密码binwalk 无输出追加数据无签名手动计算 EOCD 偏移直接读取追加区域注释区乱码编码不匹配尝试 UTF-8/CP437统一用 Base64 编码解压后文件损坏CRC 被篡改对比 CRC 值重算 CRC 或修复压缩包多个同名文件结构混淆unzip -l查看条目逐个提取对比内容独家避坑技巧遇到 ZIP 相关题目先备份原始文件所有修改都在副本上进行。我见过太多人直接在原文件上改标志位结果把文件改坏了连原始结构都恢复不了。另外zipdetails这个工具比unzip -l详细得多能直接看到每个字段的十六进制值强烈建议加入工具箱。5. 取证与防御视角下的压缩包隐写5.1 取证分析的基本流程从取证角度看待压缩包隐写思路和 CTF 正好相反CTF 是“已知有隐藏去找出来”取证是“怀疑有隐藏去证明或排除”。我通常按以下流程走第一步文件类型确认。不要相信扩展名用file命令或者查看文件头签名。一个.zip文件可能实际上是 RAR 或者 7z。第二步结构完整性校验。用unzip -t测试压缩包完整性如果报错记录错误位置这往往是篡改的痕迹。第三步冗余区域扫描。计算 EOCD 结束位置检查后面是否有额外数据。同时检查每个本地文件头和中央目录之间是否有间隙。第四步元数据分析。查看文件的创建时间、修改时间、压缩方法、操作系统标识等。异常的时间戳比如 1980-01-01或者不常见的压缩方法都可能是线索。第五步内容提取与解码。对发现的隐藏数据尝试常见编码Base64、Hex、URL 编码、异或、ROT13 等。5.2 防御思路如何检测压缩包隐写如果你负责安全检测需要识别经过隐写处理的压缩包可以从几个维度入手统计特征正常 ZIP 的注释区通常为空或者很短如果注释区长度异常比如接近 65535就值得警惕。熵值分析对追加数据区域计算熵值加密或压缩后的数据熵值接近 8而纯文本熵值较低。结构一致性检查本地文件头和中央目录中的文件名、大小、CRC 是否一致。不一致的地方往往是篡改点。多引擎扫描把文件提交给多个杀毒引擎虽然隐写本身不是恶意行为但如果隐藏的是恶意载荷可能会被检出。5.3 法律与合规边界必须强调一点压缩包隐写技术本身是中性的用于 CTF 竞赛、安全研究、隐私保护都是正当的。但如果用于传播恶意软件、窃取数据、规避监管那就触碰了法律红线。在实际工作中我建议只在授权环境下进行隐写测试。不要将隐写技术用于任何未经授权的数据隐藏。发现可疑的隐写样本时按流程上报不要自行传播。个人体会技术本身没有对错关键在于使用场景。我在取证项目中处理过用压缩包隐写传递敏感信息的案例也见过 CTF 选手用同样技术拿高分。区别只在于是否获得了授权、是否在合规框架内操作。6. 进阶方向与工具链扩展6.1 从 ZIP 到其他压缩格式ZIP 只是最常用的载体RAR、7z、TAR、GZIP 都有各自的隐写空间。比如 RAR 的恢复记录Recovery Record区域可以藏数据7z 的头部结构比 ZIP 更复杂隐藏点更多。TAR 格式更是简单直接在文件末尾追加数据就行因为 TAR 没有中央目录解压软件读到文件结束标记就停。如果你要处理多格式隐写建议用libarchive这个库它支持几乎所有常见压缩格式而且提供了底层结构访问接口。6.2 结合图片隐写的复合手法热搜词里出现了“图片隐写”这其实和压缩包隐写经常结合使用。常见做法是把压缩包追加到图片文件末尾比如 JPG 的 EOI 标记之后然后修改图片的某些像素值来编码解压密码。这样一张图片既能正常显示又同时携带了压缩包和密码信息。提取的时候先用binwalk或者foremost从图片中分离出压缩包再从图片的 LSB最低有效位中提取密码。这种复合手法在 CTF 中非常流行也是“misc隐写”方向的典型题型。6.3 自动化工具链推荐经过多个项目的积累我整理了一套比较顺手的工具链环节工具说明结构分析zipdetails、7z l -slt查看 ZIP 内部字段文件分离binwalk -e、foremost自动提取嵌套文件十六进制编辑010 Editor、HxD手动修改标志位脚本处理Pythonzipfile、struct批量自动化密码破解fcrackzip、john处理真加密编码解码CyberChef、Pythonbase64多层编码还原最后分享一个小技巧如果你经常做压缩包分析可以写一个 Bash 别名把binwalk、zipdetails、unzip -l三个命令串起来一键输出所有关键信息。我自己的别名是zinfo用了三年至少省了几百次手动输入的时间。
返回列表