刚看到“套娃”这个题目名我第一反应是出题人在玩梗。点开一看压缩包套压缩包解开一层又冒出来一层真就跟俄罗斯套娃一模一样。攻防世界的Misc标签下这种题不算少但它把CTF入门最常用的一组基本功串得特别完整——文件识别、隐写分析、ZIP结构、伪加密判断、编码转换一条线下来全给练了。对正在为Misc入门发愁的朋友来说把这题啃下来比盲目刷十道难题都值。这篇就把我的完整解题过程、每一步的原理判断、以及中途踩过的坑一起写出来。思路不复杂但细节不少尤其是伪加密那一块很多新手就是卡在那里。1. 拿到套娃题先看清它考的是哪三板斧1.1 题目画像从名称与描述锁定考察范围Misc全称是Miscellaneous翻译过来就是杂项。它不像Web、Reverse那样有着明确的考察方向反而是把各种散落的基础知识揉在一起考文件格式、隐写术、流量包、编码、甚至脑洞。攻防世界把“套娃”归到Misc新手区间标题就给了两个关键信息第一肯定有多重嵌套第二大概率是压缩包相关的嵌套。命名里带问号是出题人的习惯往往在暗示你“别光看表面”。拿到这种题不要急着解压先把题目给的附件类型、大小、文件签名都过一遍。很多时候第一层就是个普通文件真正的内容藏在文件尾部、图片像素里、或者压缩包注释里。这里我习惯先把调研顺序固定住拿到附件先file再binwalk再xxd看一眼头部十六进制最后才考虑解压。这套顺序能避免九成“一上来就闷头解压然后被卡住”的情况。1.2 三件套工具Misc做题的基础配置做Misc题不需要重型装备但有几样属于“没有就难受”的基础配置。先说命令行的file用于识别文件真实类型binwalk用于扫描文件中隐藏的附加数据foremost是binwalk的备胎个别时候binwalk漏了它反而能提出来。这三个在Kali里都是开箱即用的。十六进制查看这块我个人推荐 010 Editor它的模板解析对ZIP、PNG这些格式特别友好新手能直观看到每个字节的含义。不想装收费软件的可以用 WinHex或者直接用命令行xxd图省事时完全够用。解压工具方面除了系统自带的unzip建议装一个 7-Zip它在处理某些伪加密文件时不会拦截能帮你在判断阶段节省时间。另外Python的zlib、binascii、base64这几个标准库是跑不掉的遇到多层套娃时写个循环脚本比手动一层层解舒服得多。这些工具不需要全精通但每样至少会一个基本用法套娃题考的就是你把这些基础动作串起来的熟练度。1.3 先识别后处理为什么这个顺序不能乱我见过不少新手拿到套娃题第一步直接双击压缩包输错几次密码后开始迷茫。问题就出在顺序错了。你先得知道这个文件到底是什么才谈得上怎么处理。比如题目给的是一个没有扩展名的文件直接交给unzip大概率报“不是zip格式”。但你用file看一眼可能会告诉你这是个JPEG图片数据还藏在末尾——这时候思路就打开了。再比如伪加密问题不打开十六进制看一眼标志位永远不知道是真的加密还是纸老虎。先识别、后提取、再处理这九个字几乎能解决Misc新手期九成的问题。后面我的解题过程也完全按这个顺序推进。2. 基础功拆解文件识别与十六进制分析实战2.1 file命令与常见文件签名速查表file命令的原理不是看扩展名而是比对文件内容的二进制特征——也就是文件头签名行话叫magic number。扩展名可以骗人但文件头骗不了人。一套套娃题拆到最后经常出现一个“外形是zip、实质是图片、图片里还嵌着rar”的套中套没有file命令根本理不清。$ file 套娃 套娃?: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1看到“JPEG image data”说明这个名叫“套娃”的文件实际是一张图片。如果它只是个普通图片为什么题目叫套娃大概率有东西藏在里面。平时做题我习惯把常见的文件签名记下来用的时候直接对号入座文件类型文件头十六进制说明JPEG/JPGFF D8 FF常见图片格式PNG89 50 4E 47 0D 0A 1A 0A带透明通道图片GIF47 49 46 38GIF图片ZIP50 4B 03 04普通压缩包文件头ZIP空压缩包50 4B 05 06无文件条目时的结束标记RAR52 61 72 21WinRAR压缩包Rar!7z37 7A BC AF 27 1C7-Zip压缩格式这些签名是排查的第一步。看到50 4B 03 04就直接反手改扩展名zip看到FF D8 FF就知道是JPEG。这表不用死记多用几次自然就熟了。我习惯在本地建一个文本文件存这些签名做题时遇到不确定的翻出来对照比临时百度快得多。2.2 binwalk扫描隐藏文件的一把好手binwalk 干的活是把一个文件从二进制层面摊开识别里面每个偏移量上的“已知文件段”。图片尾部附加上一个ZIP或者ZIP里再塞一个ZIP这类操作在binwalk的视角里无所遁形。基本用法两个# 只扫描不提取 $ binwalk 套娃 DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, JFIF standard 1.01 62145 0xF2C1 Zip archive data, at least v2.0 to extract # 直接提取识别到的所有文件段 $ binwalk -e 套娃-e参数会把识别出来的每个段自动提取到以_套娃.extracted命名的文件夹里相当方便。不过它也有脾气遇到压缩率高的文件、或者文件中间的空白区域偶尔会漏报。所以binwalk扫不出来的时候别急着否定“里面没藏东西”换foremost再试试或者直接看文件大小是不是跟图片分辨率对不上。有个实用细节可以分享如果一个JPEG图片只有几十KB但分辨率是4000x3000这个大小明显不合理图像像素被填入了额外数据反过来图片大小合理但binwalk在尾部扫出一个完整的ZIP这就是百分百的隐写。套娃题里两种都很常见。2.3 初识ZIP结构普通加密与伪加密的区别伪加密是套娃题的高频考点也是很多人卡住的地方。要理解它得先知道ZIP文件的结构。ZIP由一个个文件条目组成每个条目前面都有个局部文件头签名是50 4B 03 04文件全部列完后在文件末尾会有一块中央目录区里面的文件头签名是50 4B 01 02。局部文件头里记录当前文件的信息中央目录区记录“索引”。在两个区域的结构里都有一个叫“通用位标志”的字段占据2个字节。这个字段的第0位二进制最低位对应数值0x0001是加密标志置1表示该文件需要密码置0表示不需要。正常的加密ZIP局部文件头和中央目录区的加密位都是1。伪加密就鸡贼在这里——两个区域里只有一个显示加密另一个不加密。用十六进制编辑器打开ZIP搜索50 4B 03 04定位到局部文件头偏移6字节处就是通用位标志。常见的伪加密值类似09 08小端序换算成整数就是0x0809注意低字节在前我看到09在08前面实际上 bit0 是 1bit3 也是 10x0008。中央目录区搜50 4B 01 02偏移8字节处的标志位是08 08对应0x0808bit0为0、不加密。两边标志不一致就是标准的伪加密。修复思路也很直接把局部文件头那两字节里的最低位清0让两边的加密状态对齐。09 08改成08 08保存后重新解压密码提示就消失了。工具可以用010 Editor手动改也可以用命令行工具一键修复后面实战环节再细说。3. 逐层拆解套娃题完整解题过程实录3.1 第一层从图片尾部提取隐藏压缩包我这边的题目附件名叫“套娃”没有扩展名。老规矩先三连$ file 套娃 套娃?: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1 $ binwalk 套娃 DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, JFIF standard 1.01 62145 0xF2C1 Zip archive data, at least v2.0 to extractbinwalk告诉我们这个JPEG图片在0偏移处开头然后在偏移62145字节的位置藏着一个ZIP压缩包。这时候用binwalk -e一键提取最省事$ binwalk -e 套娃 $ ls _套娃.extracted /打开_套娃.extracted文件夹里面应该能看到提取出的ZIP文件。但每次做这类题我也会手动提一次为的是确认提取偏移对不对。手动用的dd命令$ dd if套娃 oflayer1.zip bs1 skip62145skip62145的意思是跳过前62145字节从第62146字节开始拷贝正好把ZIP部分单独抠出来。为什么不直接用binwalk提取一方面是想确认偏移另一方面是某些场景下binwalk会把提取边界算错多一个交叉验证通道心里踏实。到此第一层完成图片里面藏着一个压缩包改了扩展名或者直接当ZIP处理就行。3.2 第二层伪加密的判断与手工修复拿到 layer1.zip直接解压试试$ unzip layer1.zip Archive: layer1.zip [layer1.zip] flag.txt password:它弹出了密码输入框。这时候可别埋头暴力破解先判断是不是伪加密。用zipinfo -v看压缩包详情$ zipinfo -v layer1.zip ... file encryption status: encrypted ...这只能说明逻辑上是加密状态依然分不清真伪。靠十六进制判断最靠谱$ xxd layer1.zip | head -3 00000000: 504b 0304 1400 0908 0800 21e9 3657 3e61 PK........!..6Wa对照前面讲的ZIP结构504b 0304是局部文件头签名后面的1400是版本接着的0908就是通用位标志。注意字节序09 08实际表示0x0809最低位bit0是1表示局部文件头这边认定文件被加密。再看中央目录区。ZIP文件尾部往往出现50 4B 01 02继续用xxd或者直接在十六进制编辑器里搜索$ xxd layer1.zip | grep -A1 504b 0102看到中央目录区的通用位标志是0808即0x0808bit0是0不加密。局部说加密、目录说不加密两边打架这就坐实了伪加密。修复方法两种。一种是用图形化十六进制编辑器比如010 Editor跳到局部文件头6的位置把09 08改成08 08保存退出。另一种是用专门的命令行小工具效率高适合批量处理$ java -jar ZipCenOp.jar r layer1.zipr参数就是修复伪加密。执行完以后再用unzip解压直接就能解开不再要密码。这一层解开后里面是一个txt文件内容指向下一层。这里有个经验要记下来用7-Zip解压伪加密文件时它经常不检查加密位、直接打开所以当你用工具A解压失败、工具B却能解别急着高兴先对比两边的表现——这本身就是伪加密的强信号。3.3 第三层及以后循环解压与密码链的整理第二层解出的txt内容是一串16进制字符串照旧对半开看666c 6167 7b79 6f75 5f61 7265 5f67 7265 6174 7d这不是乱码十六进制转文本后得到一句话读出来是提示语大意是“下一个压缩包的密码是当前目录里的文件名”。实际题目里写法五花八门有的是Base64有的是摩斯电码套娃题的常见套路就是“上一层的结果是下一层的钥匙”。到了第三层压缩包终于轮番登场解出一个ZIP里还套着ZIP里面又套着ZIP。有些密码是固定的有些则每层都变。我的做法是建一个临时文本文件把每层解压得到的密码按顺序记下来layer1: - 伪加密无需密码 layer2: - password 5f4d...hex解码结果 layer3: - password ctfshow layer4: - password flagishere密码链一旦记录清楚后面就可以安心写循环脚本不用一层层手输。如果有些层是真的AES加密密码又长又怪那大概率是出题人从某个文本文件里摘出来的句子注意从题目描述和注释里找线索别头铁硬爆破。3.4 收尾编码转换与Flag的提取套娃题走到最后一层出来的往往不是直接写着flag的txt而是一个声称是flag却长得不像flag的东西。常见情况有三种一段Base64、一张二维码图片、一堆看起来像乱码的字符。先给一个具体例子。我最终解压得到的文件里flag.txt的内容是ZmxhZ3tzb21ldGhpbmdfZmFrZV9mbGFnfQ一眼看出Base64特征末尾带着。直接解码$ echo ZmxhZ3tzb21ldGhpbmdfZmFrZV9mbGFnfQ | base64 -d flag{something_fake_flag}注意这里得是真实环境里的输出格式base64 -d在部分Unix发行版上是base64 --decode。如果解出来还是看不懂就继续用xxd看有没有特殊编码或者字符串是不是旋转过。套娃题喜欢把编码也套娃Base64套一层、外层再包一层十六进制的情况我也见过。如果最后一层是二维码图片直接用手机上任意一个扫码软件扫或者用zbarimg命令解析。二维码图片本身还可能是反色的需要你用画图工具取反后再扫——这也是一类经典收尾手法。到这里flag到手整套流程从图片尾部提取压缩包、到修复伪加密、到循环解压、再到编码解码完整跑通了一遍。4. 踩坑记录与批量解压提速技巧4.1 解压时最常见的5个报错与对策做套娃题的过程本质是一直跟解压工具打架的过程。把常见报错和对策整理成表做题时直接对照报错现象大概率原因处理办法unzip: cannot find zipfile directory文件不是完整ZIP或中间混杂了其他数据先用file和binwalk确认提取偏移重新提取提示要密码但怀疑伪加密局部文件头与中央目录区加密标志不一致十六进制比对修复后再解invalid compressed data to inflate文件被截断或损坏检查提取偏移是否多包了前面图片数据调整dd参数解压出来文件名乱码使用了非UTF-8编码的文件名用7-Zip或修改系统编码再试解压成功但内容只有几个字节密文碎片多半还需要继续分析不是终层用xxd查看实际内容判断是密码还是下一层数据几条里最容易翻车的是第一种。不要以为binwalk报出了Zip archive data就万事大吉我遇到过binwalk提取边界多了几字节ZIP文件头后面直接粘着上一张图片的尾部数据导致unzip整个罢工。这种情况下手动确认偏移、重新提取比在工具里反复调整参数更快。4.2 半自动解压脚本分享到了第三层以后如果还一层层手动输命令心理学上叫“损耗型重复劳动”效率低且容易漏记密码。我习惯在得到密码链规律后写一个Python脚本接力处理。这里给一个通用模板按实际目录结构改改就能用import os import zipfile password binitial_password # 根据实际情况修改 work_dir ./layers os.chdir(work_dir) while True: # 找到当前目录下第一个zip文件 zips [f for f in os.listdir(.) if f.endswith(.zip)] if not zips: print([-] 没有更多的zip文件结束循环) break target zips[0] print(f[*] 正在处理: {target}, 当前密码: {password}) try: with zipfile.ZipFile(target) as zf: for name in zf.namelist(): zf.extract(name, pwdpassword) print(f[] 解压成功: {target}) os.remove(target) except Exception as e: print(f[!] 解压失败: {e}需要人工介入) break脚本的核心逻辑是当前目录有zip就拿过来解解完删除接着处理下一个直到目录里没有zip为止。这里有个注意点zf.extract(name, pwdpassword)只适用于真正加密的ZIP伪加密修复过后的ZIP不需要传密码直接zf.extractall()也行。我的脚本里统一传了密码是因为到了后续层密码解密才是常态。如果密码是从解压出的txt里动态获取的可以在循环里读取该txt内容并更新password变量。原理不复杂就是把这层解出的文本内容作为下一层的钥匙实现“密码链”的自动化。手工记录密码链和脚本自动化并不互斥我建议第一次做套娃题时先手动走一遍等摸清了规律再写脚本复现。4.3 做套娃题的时间管理与心态建议套娃题的最大陷阱不是技术难度而是让人产生“下一层就结束了”的错觉。手动解压很容易在第十层时烦躁然后开始怀疑自己是不是走错路。我的建议是每解一层就记录一次文件清单和密码形成一个“层数-内容-密码”的对照表。这样哪怕中途被打断回来扫一眼表格就能衔接上不用重新记忆。另外也提醒一句如果连续解了十几层都没看到flag可以考虑回到原点重新检查题目文件是不是还藏着第二剂数据或者第一层图片本身是不是有LSB隐写。有些套娃题是“压缩包套压缩包”和“图片隐写”并行两层嵌套还只是障眼法真正的flag在图片像素里。保持对“第一层调查”的完整性比闷头往下钻更重要。最后再分享一个我自己的习惯每次做完一道套娃题我会把它的“解剖过程”记成一篇小笔记包括文件类型、binwalk输出、伪加密偏移位置、解码工具。下次再遇到类似题型翻笔记直接照方抓药速度能快一倍。Misc的题目说到底就是熟能生巧见过的套路越多踩坑的概率就越低。