ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CTF杂项套娃题实战:从ZIP伪加密到LSB隐写的层层拆解

CTF杂项套娃题实战:从ZIP伪加密到LSB隐写的层层拆解 前阵子刷攻防世界MISC分类碰到一道叫“套娃”的题。名字后面的问号非常灵性像是在提醒你别以为你看见的这一层就是全部。题目附件下载下来是一个没有扩展名的文件大小不大第一眼平平无奇但当你开始顺着文件结构一层层往下拆就会发现这个问号一点都没夸张。我不太喜欢一上来就喊“卡关”所以这道题用了比较系统的方式处理。整篇就是把拆解过程的复盘写清楚每一层遇到的坑、用到的命令、判断的思路都会提到。这篇内容适合还在入门MISC、对文件格式识别和隐写分析感兴趣的朋友参考。先说结论这类题考的基本不是冷门算法而是你对ZIP文件结构、图片隐写、常见编码的敏感程度。“套娃”就是把这种敏感度放到一个循环里循环到你觉得快撑不住的时候flag通常就出来了。1. 没有后缀的文件先让file教你怎么看东西1.1 双击永远不如一条file命令拿到附件的第一反应是打开终端。这种名字特别长的下载文件直接双击大概率只会得到系统弹窗“Windows无法打开此文件”。我习惯先执行一条file命令$ file 180a8f7a3b8c4c0a 180a8f7a3b8c4c0a: Zip archive data, at least v2.0 to extract看到Zip archive data之后顺手把文件改名为layer1.zip。改名不是必须的但能让后面的命令更顺至少不用每次敲一长串文件名。这一步看起来简单但我见过不少新手在“没有后缀”上卡很久各种改成.jpg、.txt甚至.docx去看结果只会让信息更乱。核心原则是文件系统里的后缀只是名字内容是什么由文件头决定。ZIP的头部签名是50 4B 03 04用xxd看前几个字节也能判断。以后遇到任何未知文件养成先file再动手的习惯能省下大量时间。1.2 文件名是一串哈希时的额外试探我注意到这个下载资源的文件名本身就是一长串类似md5的字符串。按照以往经验文件名也有可能暗示密码或编码方式。于是我先把这串字符串丢进hashid看了一眼又试了当成明文密码拼接常见后缀结果都没用。这一步虽然失败了但在CTF题目里属于常规操作值得保留。真正让我打开突破口的是strings。不是所有线索都存在文件内部但大多数CTF题会把提示放在肉眼可见的位置只是你得主动去扫。我对layer1.zip做了strings扫描$ strings -n 4 layer1.zip | tail -30在输出末尾看到一行非常显眼的提示you can find password in the tail of this file Password: TuoLua2D严格来说这条提示藏在ZIP的注释区里属于文件末尾区域。试了之后顺利解开第一层得到一个misc.jpg。第一层就这么过了但真正的麻烦也在后面等着。2. 第一层解压密码不会莫名其妙出现2.1 先试弱口令再跑字典但不能只依靠爆破解压layer1.zip时系统让我们输入密码。我先试了123456、password、misc这些常见弱口令全部失败。接着用zip2john导出哈希再丢给john配合rockyou字典跑了一阵也没跑出来。这一刻我意识到密码线索大概率不是猜出来的而是需要从文件本身读取。这里有个经验可以分享**MISC题里的ZIP密码八成会在题面、文件名、文件注释、图片属性或某段编码里出现直接硬刚字典是效率最低的方法。**爆破工具当然要会但不要把它作为第一选择。尤其是这种带“套娃”字样的题目作者想考的是循环拆解能力不是让你显卡冒烟。为什么会想到检查ZIP注释区因为外层ZIP本身没有附加任何数据解压又要求密码那密码信息只能放在ZIP标准允许的元数据区域里。ZIP结构有局部文件头、中央目录和尾部记录其中注释区可以存放一些文本肉眼直接看不到但strings能扫出来。很多CTF作者喜欢把这个地方当小纸条用。2.2 解压之后的“检查习惯”解出misc.jpg后我的常规三连是$ file misc.jpg $ strings misc.jpg | head -50 $ binwalk misc.jpg这三条命令分别负责“确认类型”“瞄一眼可见文本”“找隐藏附加数据”。binwalk的输出很快给了关键信息DECIMAL HEXADECIMAL DESCRIPTION 0 0x0 JPEG image data, JFIF standard 1.01 300536 0x49658 Zip archive data, at least v2.0 to extract图片文件的偏移量0x49658处有一段ZIP数据这说明misc.jpg不是一张单纯图片而是“图片压缩包”的合体文件。这种合体在MISC里太常见了常见到如果哪道题不是这种结构我反而会不太适应。3. 第二层撞上伪加密错误印象带来的正确解法3.1 从JPEG里抠出下一个压缩包binwalk只是告诉我们哪里有内容它不会自动帮你把那段ZIP完整分离出来。最稳妥的做法是直接用dd按偏移量切$ dd ifmisc.jpg oflayer2.zip bs1 skip300536切出来的layer2.zip能不能正常解压试一下$ unzip layer2.zip Archive: layer2.zip [layer2.zip] encode.png password:又需要密码。到这里很多人的第一反应是去爆破或者到处找密码。但我停下来想了一下这一层和第一层有点不一样第一层的外层ZIP是“正常加密”密码写在注释区这个第二层ZIP是从图片中间抠出来的作者不一定真的给它上了加密算法。3.2 伪加密一个被设置了加密标志但数据没加密的ZIPZIP压缩包里每个文件都有一个“通用标志位”general purpose bit flag。其中第0位表示“文件是否加密”。正常加密文件会把这一位置1并且文件数据确实经过ZipCrypto或AES处理。伪加密则只是在标志位上写了1数据本身还是普通的deflate流或store流。判断方法很简单用Python读一下flag_bitsfrom zipfile import ZipFile with ZipFile(layer2.zip) as zf: for info in zf.infolist(): print(info.filename, hex(info.flag_bits))输出类似于encode.png 0x90x9代表bit0和bit3同时为1其中bit0就是加密标志位。如果它只是标志位为1、而数据没有真正加密我们完全可以把flag_bits里的bit0清掉再读取from zipfile import ZipFile with ZipFile(layer2.zip) as zf: for info in zf.infolist(): info.flag_bits ~0x1 zf.extract(info)这一步如果能正常解出文件说明伪加密实锤。我在这儿确实解出了encode.png。那一瞬间很有成就感因为我没有被“需要密码”这个表象骗住。这里顺便给个避坑建议**遇到ZIP要密码先看flag_bits再看文件注释然后才轮到爆破。**伪加密在MISC题里几乎是固定考点很多人明明知道这个名词但真到手里却忘了检查和绕过白白浪费大量时间去跑字典。4. 图片隐写才是套娃的中心舞台4.1 encode.png不是结束是另一个开始拿到encode.png我又重复了一遍基础三连。binwalk再次给出附加ZIP信息$ binwalk encode.png DECIMAL HEXADECIMAL DESCRIPTION 0 0x0 PNG image, 640 x 480, 8-bit/color RGBA ... 345612 0x5460C Zip archive data, at least v2.0 to extract切出layer3.zip后这次不是伪加密是真要密码。密码不会凭空出现于是我对encode.png做了更细致的检查。先看元数据$ exiftool encode.png在Comments字段里出现了一行很明显的线索Comments : third password is f0r_th3_3nd拿着这个密码解开layer3.zip里面有一个message.txt。message.txt的内容是一串很长的Base64Vm0xd1NtUXhVbGhTV0d4VVYwZDRXRmxzWkZOWGJHUllVakF3...Base64解码一次还是Base64继续解重复几次之后出现了一段可读的Python样提示。大意是真正的密码被放在encode.png的LSB通道里。这个环节很有意思它像是一层“解码套娃”外层ZIP里放了个Base64文本文本本身不是flag而是告诉你回去看图片的像素最低位。如果不做这一步后面根本走不下去。4.2 LSB隐写图片像素的最低位里能藏整个文件LSB全称Least Significant Bit也就是像素颜色通道的最低位。PNG是无损格式把RGB每个通道的最低位替换成要隐藏的二进制数据肉眼几乎无法察觉。这是MISC题里出现频率最高的隐写方式。我习惯用zsteg快速扫描比如$ zsteg encode.png -a | head -50zsteg会尝试很多种LSB/MSB组合直接列出可能的隐藏数据。如果环境里没有zsteg也可以打开StegSolve的Analyze → Data Extract把Red、Green、Blue的0号位都勾上导出数据。我在这个环节得到了一段Base64特征明显的字符串解码后是oltEr_level_new_password:3607这就是下一层ZIP的密码。到这里“套娃”的模式已经非常清晰了检查文件类型图片先看尾部附加数据、再看元数据、然后LSB压缩包先判断伪加密、再找密码解出来的内容继续重复上面流程。4.3 用binwalk自动提取内嵌文件但别指望一键完成有人可能会问binwalk能不能直接分解这些嵌套文件答案是可以提取但不会自动帮你解密码。binwalk -e只会把能识别的签名数据按偏移量分离出来遇到加密ZIP它提取出来还是一个加密ZIP密码得自己找。不能指望一条命令把整套流程全自动化。实际我在每一层用的是$ binwalk -e encode.png然后进入_encode.png.extracted目录查看分离结果。这个目录名的规律是原文件名.extracted。如果嵌入了多个文件目录里会看到按偏移量命名的文件比如5460C.zip。命名里的数字就是ZIP数据在原始文件中的偏移量这也是很好的位置参考。5. 循环拆包从手动操作到写脚本偷懒5.1 手动拆解时一定要记账我拆到第四层时脑子里已经有点绕了。每一层都有“文件名、密码来源、解压结果”三样信息如果不记录半小时后很容易忘记上一层的密码对应哪个ZIP。我的习惯是随手开一个notes按行记layer1.zip 密码来自ZIP尾部注释 - misc.jpg layer2.zip 伪加密清flag_bits - encode.png layer3.zip 密码来自exif Comments - message.txt 下一段提示 layer4.zip 密码来自LSB隐写 - last.bin这份记录不只是备忘它还能帮你发现规律。如果连续三层都是“ZIP里有图片图片里又嵌ZIP”那就说明后面的流程大概率也是重复不用每层都痛苦地人肉拆一遍。5.2 简单的自动化循环脚本当层数开始变多我建议写一个小脚本让机器替你做重复劳动。思路是这样的import os, base64 import subprocess def file_type(filename): out subprocess.run([file, -b, filename], capture_outputTrue, textTrue) return out.stdout def extract_zip(filename, passwordNone): cmd [unzip, -o] if password: cmd [-P, password] cmd.append(filename) subprocess.run(cmd, checkTrue) current layer4.zip pw 3607 extract_zip(current, pw) while True: # 这里列出当前目录下所有内容逐个判断类型 for name in os.listdir(.): if name.startswith(flag): print(open(name).read()) exit() ft file_type(name) if Zip archive in ft: extract_zip(name, next_pw) elif PNG image in ft or JPEG image in ft: subprocess.run([binwalk, -e, name])这个脚本不够完美但它体现了“套娃题”的一种高效思路把重复判断交给脚本自己只处理密码和关键扰动。实战中层数特别多的时候手工一层层解既容易出错也容易漏掉某一层的隐藏密码。5.3 循环过程中的两个边界情况循环拆包时要注意两个边界第一解出来的不一定都是ZIP或图片可能是文本、脚本、甚至无扩展名的文件。这时候别急着套用“图片→隐写”的固定套路先file再判断。第二binwalk -e提取出的文件可能会包含很多误报。比如图片里有一些正常压缩段也会被识别成ZIP或gzip压缩数据。看到一堆文件时优先找文件大小合理、并且能正常打开的别把时间浪费在明显损坏的碎片上。我拆第四层时其实就碰到了“ZIP里套两个文件一个是readme.txt一个是final_data.zip”的情况。readme.txt写的是“最后一层不需要密码但需要解码”这就很关键。它告诉我不要再找密码了直接解压剩下的问题变成“解码”。6. flag收尾与避坑清单这类题其实很有规律6.1 最后一层是编码解码而不是密码解压final_data.zip后得到一个last.bin。名字带bin但file结果显示它是ASCII text。打开一看是一长串混合了Base64和URL编码特征的字符串。我依次做了几件事$ base64 -d last.bin tmp1 $ cat tmp1解出来仍然是带%特征的文本继续用python3 -c import urllib.parse; print(urllib.parse.unquote(open(tmp1).read()))处理。最终得到flag{Matryoshka_Is_More_Than_Dolls}提交通过。这个flag和题目名形成了呼应Matryoshka就是俄罗斯套娃整道题就是一版“数据结构的套娃”。最后一步不是隐藏很深而是把多重编码解开这也是MISC里常见的最终形态。6.2 值得记住的坑和工具清单把这次的实操经验汇总成一份清单以后再碰到“套娃”类题目按顺序走一遍会顺畅很多。文件没后缀先file再看图片先跑strings、binwalk、exiftoolZIP要密码先检查flag_bits判断伪加密PNG或者BMP优先查LSB隐写用zsteg或StegSolveBase64多次编码很常见连续解几次再说每一层都做记录避免后面绕晕别把时间浪费在爆破弱口令上先找提示。工具方面我这份清单常年放着file、binwalk、strings、exiftool、zsteg、StegSolve、zip2john、john、python3配合zipfile/PIL再加一个顺手点的十六进制编辑器。这些工具覆盖了八成MISC入门题。我后来复盘这道题时最大的体会不是哪个工具多厉害而是“怀疑”习惯的价值。怀疑ZIP不一定要密码怀疑图片不只是图片怀疑Base64背后还有Base64。每一个“怀疑”都会把你推进下一层直到最后那个确实存在的flag。再分享一个手工经验如果某层解出来的文件比预期多不要急着只盯最大的那个。readme、hint、甚至文件名本身都可能是下一层密码的线索。这道题的答案就是用一条readme提示收尾的。套娃嘛最外面的壳最唬人越往里走反而越接近实话。
返回列表