CTF里的misc杂项,说难不难,说简单也真能卡得人头皮发麻。上篇写了编码、压缩包和流量分析,评论区一直在催隐写术,今天就把这块补上。所谓隐写术,就是把flag藏在你一眼看不出来的地方:可能是图片像素的最低位,可能是音频频谱里的一段波形,也可能是二维码的定位角被人改掉了一个黑块。换句话说,misc里的隐写题,考的不是你加密算法背得多熟,而是你能不能从看似正常的文件里发现那一点点不正常。这篇文章主要讲图片隐写,顺带把压缩包、音频、二维码里常见的藏法一起梳理一遍,适合刚接触CTF的新手,也适合已经刷过一些题但总在隐写上卡壳的朋友。
先说明一下,这里默认你已经会用虚拟机、能装工具,知道什么叫PNG头、什么叫字符串。上篇结尾我留了一张文件格式对照表,如果你还没存,建议先看一眼,因为下面所有技巧都建立在一个基础上:任何隐写题,本质都是信息藏在另一个信息载体里,你要做的就是找到那个不自然的角落。本篇会尽量把每个判断步骤说清楚,也会把我踩过的坑写出来,照着做至少能让你在比赛里少走半小时弯路。
1. 隐写术解题的整体逻辑:别急着上工具
1.1 先看文件类型和十六进制结构
拿到一个misc隐写题,第一件事不是上工具,而是确认文件真实类型。很多题目喜欢把文件后缀去掉,或者给一个看似正常的图片,实际上里面还塞了压缩包和脚本。先用 file 命令看真实类型,再用010 Editor打开,看前几十字节的文件头是否正常。JPEG是 FF D8 FF,PNG是 89 50 4E 47,GIF是 GIF8,ZIP是 50 4B 03 04。如果文件头被改过,比如把PNG的头改成其他格式,图片在资源管理器里打不开,那大概率只是文件头被篡改,手动改回来就能看到正常图。这类题属于送分题,但恰恰有很多新手栽在这里,一看到打不开就开始胡思乱想,其实改个字节就完事了。
判断文件头是否正常有个小技巧:把文件拖进010 Editor,看右侧ASCII区域是否出现“PNG”“JFIF”这类可读标记,同时看文件大小是否和图片分辨率匹配。如果大小明显偏大,比如一个800x600的PNG却有5MB,那必然藏了额外数据。这时候用 binwalk -e 可以直接分离;如果binwalk没识别出来,就去文件尾部手动找另一个文件的签名,常见的是 PK(zip)、7z、RAR 等。文件尾部藏着另一个压缩包是misc题的老套路,所以养成习惯:不管用什么工具,最后都要看一眼十六进制尾部。
1.2 隐写题的三种常见藏法
我在反复刷题之后,把隐写题的藏法归成三类,理解这三类之后,你就不会看到题目就无脑上工具了。
第一类是附加文件。把flag直接或加密后塞在原图后面,或者把两张图拼在一起,用 foremost 或 binwalk 分离即可。第二类是隐藏字符。信息藏在注释、元数据、文件名里,用 strings 扫一遍就能看到可打印字符,有时候是明文,有时候是base64。第三类是载体内部修改。信息藏在像素的LSB、音频频谱、二维码的掩码这些肉眼看不出来的地方,需要专门的隐写分析工具,这也就是题目真正想考的“隐写术”本体。
这三类的破题顺序也有讲究:先做附加文件和隐藏字符的检查,因为成本最低;确认没有之后再考虑LSB、频谱这些需要工具分析的场景。很多新手一上来就开Stegsolve乱点,结果真正的flag其实就在图片注释里,白白浪费了时间。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 图片隐写技巧:从文件头到像素的层层剥茧
2.1 先用binwalk和foremost把图片“切开”
图片是最常见的隐写载体,原因很简单:图片字节数多、结构复杂,藏点东西很难被注意。处理图片第一步,我习惯先用 binwalk 扫描整个文件,看看有没有嵌入其他文件。命令很简单:
bash复制binwalk -e image.png
-e 参数会尝试自动提取识别出来的文件。比如一张图片后面跟了一个zip,binwalk能直接把它切出来。如果binwalk没出结果,再用 foremost 暴力一点地按文件签名恢复:
bash复制foremost image.png -o extracted
foremost会把所有符合常见文件头的数据块都抽出来,哪怕是破碎的也能还原一部分。有个坑我必须提一下:有些题目故意在图片尾部补了很多 00 字节,把zip签名藏在一堆零后面,binwalk默认可能识别不到。这时候不要放弃,直接用010 Editor在文件末尾附近搜索 50 4B 03 04,找到后手动把zip部分复制出来另存为新文件,再继续后面的分析。还有更阴的题,会把图片和zip的头互换成伪文件,比如把zip头改成PNG头,文件打不开,用binwalk也扫不到,但用010 Editor改回头尾就能解开,所以“改文件头”永远是必修课。
2.2 LSB隐写与颜色通道分析
图片隐写里最经典的考点就是LSB(Least Significant Bit)。原理很直白:每个像素的RGB值,比如红色分量是255,二进制是 11111111,如果把最低位改成0变成 11111110,人眼根本看不出来。把要隐藏的文本转成二进制,一位一位写进这些最低位,就完成了信息嵌入。因为PNG和BMP是无损压缩,所以这类题基本只出现在PNG/BMP里;JPEG有损压缩会破坏最低位,所以很少用。
Stegsolve是处理LSB的标配工具。打开图片后进 Analyse -> Data Extract,勾选R、G、B对应的Bit 0,有时还要勾Bit 1或Bit 2,然后看左侧提取出来的内容。如果提取出来像一串二进制或乱码,试试把“Bit Order”从LSB换成MSB,把“Bit Planes”从RGB换成RGBA,多换几次组合,结果可能是文本、可能是另一个图片、也可能是一个二维码。如果你懒得一个个点,可以用 zsteg 一键检测:
bash复制zsteg -a image.png
zsteg会把常见的LSB、MSB、RGB/RGBA组合都扫一遍,识别到可读字符串或嵌入图像会直接提示。我个人建议先跑zsteg再手动确认,因为自动工具可能会漏掉二维码这类需要人工识别的结果。还有一点要记住:Stegsolve的 Data Extract 里有个“Preview”区域,如果看到宽高比例正常的小图,不要犹豫,直接 Save Bin 导出,很可能就是隐藏的flag图片。
2.3 Exif信息与strings中的可读字符串
有些题简单得让人怀疑人生,flag就写在图片的Exif信息里。用 exiftool 一条命令能看所有元数据:
bash复制exiftool image.jpg
重点看 Comment、Artist、Copyright、Image Description 这些字段。作者名可以是flag,注释可以是base64串,标题可以是PNG内容提示。我遇到过一次,图片的 Software 字段写了一串看起来像MD5的东西,拿去解码发现是flag的一部分。所以不要嫌exiftool输出长,每一行都值得扫一眼。
strings 更暴力,直接提取文件里的可打印字符串。配合grep可以快速定位:
bash复制strings image.png | grep -Ei "flag|ctf|buu|key|\{"
如果strings输出里出现一串长度明显不正常的大小写字母和数字混合体,大概率是base64或base32。这里我会用“随波逐流CTF编码工具”快速验证,里面的Base64、Base32、十六进制、URL解码都是现成的,比自己在脑子里算快得多。别看到乱码就头疼,先想想是不是最常见的编码,再决定要不要写脚本。很多新手忽略这类简单检查,直接去调LSB,结果绕了一大圈回来发现flag就在文件末尾字符串里,真的亏。
3. 压缩包与文件结构隐写:伪加密、CRC32爆破与“藏中藏”
3.1 zip伪加密的识别与修复
压缩包隐写是misc里另一大巨头,尤其是zip伪加密,几乎每个CTF练习平台都会出。所谓伪加密,就是压缩包实际没有加密,但在文件结构里把“加密标志位”改成了1,导致双击解压时提示需要密码。原理在zip格式里,本地文件头(LFH)和中央目录头(CEN)都有一个通用位标记字段,其中第0位是加密标志。伪加密就是把这一位置1,但压缩数据本身没有加密。
识别伪加密有两种快速方法。第一种是用 binwalk 扫描,它有时候会提示 ZIP, encrypted 但解包不需要密码;第二种是用010 Editor打开zip文件,搜索 50 4B 03 04(本地文件头)和 50 4B 01 02(中央目录头),找到通用位标记字段。正常情况下这个字段是两个字节,比如 00 00,伪加密会被改成 01 00 或 09 00 等。修复方法也很简单,把对应字节改回 00 00,保存后再解压就能直接出文件。
如果嫌手动改麻烦,可以用老牌的 ZipCenOp.jar:
bash复制java -jar ZipCenOp.jar r encrypted.zip
这个命令会自动修复伪加密标志。注意它只对伪加密有效,如果压缩包是真加密,跑这个命令大概率没反应或报错,这时候就要考虑字典爆破或明文攻击。判断真伪还有一个土办法:把文件拖进WinRAR,如果WinRAR能直接打开并显示文件内容但解压要密码,那基本是伪加密;如果WinRAR连文件列表都看不见,说明是真加密。这个经验不一定100%准确,但做题时很管用。
3.2 加密小文件与CRC32碰撞爆破
还有一种zip题目特别爱出:压缩包里有一个很小的文件,比如 flag.txt,文件只有三五个字节,但是被加了密码。真加密状态下暴力破解密码可能很难,但小文件有个特殊漏洞——CRC32碰撞爆破。
CRC32是对文件内容计算出来的32位校验值,zip在压缩时会把CRC32记录在文件头里。如果明文只有几个字节,那理论上所有可能的字节组合是有限的,我们可以遍历所有组合,计算每个组合的CRC32,和zip里记录的值比对,一旦匹配就说明找到了原始内容。
常见工具是网上的crc32爆破脚本,也可以用Python手动写一个简化版,大致思路如下:
python复制import binascii
import itertools
import string
target = 0x12345678 # 从zip文件头中读到的CRC32
for length in range(1, 7):
for chars in itertools.product(string.printable, repeat=length):
data = ''.join(chars).encode()
if (binascii.crc32(data) & 0xffffffff) == target:
print('Found:', data)
raise SystemExit
实际使用时要先通过010 Editor或zipinfo读取目标文件的CRC32值,填进脚本里,然后跑。长度超过6个字节时,遍历空间会指数级增长,一般题目限制在4字节以内。这个技巧对RAR也类似,RAR文件头里同样有CRC32,但暴力恢复的难度会高一些。另外,如果压缩包里有一个已知内容的文件(比如readme.txt的内容题目给了),那还可以尝试zip明文攻击,用 pkcrack 或 bkcrack 工具,有时能直接还原出加密后的其他文件,这种题在进阶场次很常见。
4. 音频与视频隐写:用耳朵和眼睛同时找线索
4.1 频谱图里的文字与图案
音频隐写最常见的是把信息藏在频谱图里。你听到的可能只是一段相对平缓的背景音,但把音频打开到频谱视图,就会看到一排排清晰的文字或二维码图案。处理音频,我主力工具是Audacity,免费开源且够用。导入WAV或MP3后,点击轨道左侧下拉菜单,把波形视图切换成频谱图(Spectrogram),然后放大查看。如果把“频谱图”的窗口大小从默认的2048调到4096或8192,频率分辨率会变高,隐藏的文字会更清楚。
如果你在频谱图里看到类似点横线的图案,先别急着截图,那是摩斯电码的概率很高。把图案按时间轴切分,记录长音和短音,再转成文本。还有一种情况是频谱中反复出现高频轮廓,看起来像人说话但听不清,那可能是用类似Sonic Visualiser做了更强的频谱分析,或者故意把信息调制到人耳不敏感的频率范围。用Audacity的“音高检测”功能也能看到隐藏频率的变化,有些flag就是通过一组特定的频率值映射出来的。
音频题最怕两种心态:一是听到声音正常就直接放弃,二是试图靠听去解。正确的做法是永远先看频谱。别怕“看着贼像二维码的频谱图扫不了”,你可以用截屏软件把频谱区域截下来,再用二维码扫描,很多时候flag就藏在那张频谱图里。
4.2 摩斯电码与DTMF拨号音
摩斯电码在音频隐写里出现频率很高,因为声音本身就可以由长音和短音构成。解题时首先要确定节奏:把音频波形放大,观察每个信号持续的时间。一个点(短音)通常是一个时间单位,一个划(长音)是三个时间单位,同一个字母内部的间隔是一个单位,字母之间的间隔是三个单位,单词之间的间隔是七个单位。用Audacity看波形的时间轴,可以精确量出这些间隔,然后用在线摩斯解码器或脚本转成明文。
另一种容易被忽略的是DTMF双音多频信号,也就是电话拨号音。一段音频听起来像按键音“哔哔嘟嘟”,里面其实藏了数字序列。每个按键对应两个频率的组合,比如数字2是697Hz和1336Hz,数字8是852Hz和1336Hz。通过Audacity的频谱分析读出每个时间段的主频率组合,就能还原数字。还原出来的一串数字可能是十进制ASCII码,比如 70 76 65 71 就是 flag;也可能是一串手机号,进一步映射成坐标或字典索引。
这类题目做多了之后,你会形成一种直觉:听到音频背景里出现规律性很强的高频“滴滴”声,先往摩斯想;听到类似拨号音,先往DTMF想。做题顺序上,我习惯先看频谱图,再听节奏,再去找对应解码器,遇到拿不准的也可以用“随波逐流CTF编码工具”里的音频解码模块先扫一遍,省得自己手工数点点划划。
5. 二维码与GIF中的视觉陷阱
5.1 二维码定位角与容错纠错的利用
二维码在CTF里频繁出镜,不只是因为可以直接扫码,更多时候需要你先“修图”。二维码有三个角上的定位图案(左上、右上、左下),扫码器靠它们确定方向和识别范围。如果题目把一个或多个定位角涂掉、遮挡、裁剪,扫码就会失败。这时可以用Stegsolve的Stereogram或颜色通道查看,有时候被涂掉的定位角其实在某个颜色通道里还保留着,保存出来补回原图就能扫了。
还有一类题是二维码本身正常,但里面存的信息是一串被加密或编码的文本。扫码得到 ZmxhZ3... 这类base64,再解码就拿到flag。如果扫码得到的是像 BVCTF{...} 这种,说明二维码没有额外套路。遇到扫码失败时,先检查是不是反色了:打开Stegsolve点 Invert,把黑白反转,再扫码往往就能成功。还有可能是定位角被加了噪音点,用小画板把缺失的角落补成标准黑色/白色块就行。二维码容错率是L、M、Q、H四档,高容错率意味着即使图片被污损了30%也能扫出来,所以题目经常会用“涂抹一部分区域”来刁难你,但如果涂抹面积不大,扫码器还是可能通过,值得多试几次。
5.2 GIF多帧与图像差分的隐写
GIF格式支持多帧动画,这给了题目作者极大的藏东西空间。最简单的藏法是把flag写在其中某一帧上,平时播放一闪而过,肉眼很难注意到。用Stegsolve的 Frame Browser 可以逐帧浏览,把每一帧都截图或直接记录。还有一种更阴间的藏法:每一帧看起来都一样,但两帧之间的像素差异构成了隐藏信息。这时可以用Python的PIL库做逐帧差分,把两帧对应像素相减,差异结果保存成新图:
python复制from PIL import Image, ImageChops
frame1 = Image.open('frame1.png')
frame2 = Image.open('frame2.png')
diff = ImageChops.difference(frame1, frame2)
diff.save('diff.png')
如果diff图里出现了明显的文字或图案,那flag就藏在两帧的差异里。类似的方法也可以用于两张普通图片:题目给你两张看起来几乎一样的图片,相减或异或之后会出现第三张图。比如两张大图分别做了XOR,合并回原图用 ImageChops.logical_xor 或 numpy 计算即可。这个思路要形成肌肉记忆,看到两张相似图片的第一反应不是“选哪张”,而是“它们之间差在哪”。
6. 实战checklist与常用工具清单
6.1 一套可以照着做的解题流程
我在比赛里不管题目多“阴”,都会遵循一套固定的检查顺序,这里直接分享给你,可以作为自己的checklist。
先做静态检查:file 看类型,strings 扫可打印字符串,exiftool 看元数据,010 Editor 看文件头和尾部。这一轮能解决大约两成到三成的隐写题。接着做分离:binwalk -e,不行就 foremost,还不行就手动搜 PK、7z 等签名。如果目标是图片,用Stegsolve看各颜色通道和Data Extract,再用zsteg补漏。如果是音频,切到频谱图看有无文字或摩斯。如果是压缩包,先判断伪加密还是真加密,再考虑CRC32爆破或字典攻击。如果是二维码,先尝试扫码,失败就检查定位角、反色、颜色通道。
这套流程看起来长,但实际操作熟练后,每道题平均十分钟内能完成基础轮。遇到特别复杂的题,不要在一个环节死磕超过半小时,发行不出现象就先放下,去做其他题换换脑子,回来往往会有新发现。我在多个比赛里靠这个策略捡回过不少分。
6.2 常用工具速查表
| 工具 | 用途 | 典型操作/命令 |
|---|---|---|
file |
识别文件真实类型 | file mystery.bin |
strings |
提取可打印字符串 | strings img.png | grep -E "flag" |
exiftool |
查看图片元数据 | exiftool img.jpg |
| 010 Editor | 十六进制编辑、文件头修改 | 手动改PNG/JPEG/ZIP头 |
binwalk |
扫描并提取嵌入文件 | binwalk -e file |
foremost |
按文件签名恢复文件 | foremost file -o out |
| Stegsolve | 查看颜色通道、LSB提取、帧浏览 | Analyse -> Data Extract / Frame Browser |
zsteg |
自动检测PNG/BMP LSB隐写 | zsteg -a file.png |
exiftool + strings |
快速定位元数据与明文线索 | 两者配合使用 |
| ZipCenOp.jar | 修复zip伪加密标志 | java -jar ZipCenOp.jar r file.zip |
| Audacity | 音频频谱图、波形分析 | 视图切到Spectrogram |
| 随波逐流CTF编码工具 | 常见编码解码、摩斯/DTMF等 | 一键Base64、Hex、摩斯解码 |
| Python/PIL | 图像差分、XOR、通道还原 | ImageChops.difference / logical_xor |
| crc32爆破脚本 | 小文件内容恢复 | 遍历CRC32匹配 |
这个表格基本覆盖了我目前做过的大部分misc隐写题。工具不在多,在于会用。比如010 Editor你可能只用来改一个字节,但那个字节往往就是整道题的门槛。再比如zsteg和Stegsolve,自动和手动结合,漏检率会低很多。对于新手,我的建议是先不急着背命令,而是拿BUUCTF里misc模块的隐写题刷几道,把工具都跑一遍,自然会形成记忆。
最后再分享一个小技巧:做题时把每道题的“文件头、strings里奇怪字符串、binwalk分离结果、Stegsolve提取后保存的文件”都截图或存下来,赛后统一复盘。你会发现自己对“不自然”的敏感度提高得很快。我个人的体会是,隐写题与其说考技术,不如说考细心和耐心。希望这篇下篇能让你在下次比赛里少折腾几小时,如果还有更怪的藏法,欢迎在评论区交流。
