1. 图片隐写术入门从文件头开始第一次接触CTF的Misc图片隐写题时我完全摸不着头脑。直到遇到那道修改文件后缀名的题目才恍然大悟——原来图片文件头里藏着这么多秘密。PNG文件的89 50 4E 47就像身份证号JPG的FF D8 FF E0则是它的独特标记。这些十六进制魔术数字Magic Number是每种文件格式与生俱来的基因。记得有次比赛遇到个txt文件用hex编辑器查看时发现开头是47 49 46 38这不就是GIF的签名吗果断改后缀后果然出现了会动的flag。常见的文件头还有BMP的42 4D、ZIP的50 4B 03 04建议新手把这些常见文件头像背单词一样记下来。当你在Linux环境下用xxd命令可以快速查看文件头xxd -l 8 example.png | head这个命令会显示文件前8个字节正好覆盖大部分常见格式的签名区。Windows用户可以用010 Editor或HxD这类工具直观看到十六进制和ASCII对应关系。遇到不认识的文件头时可以去文件签名数据库比对比如FileSignatures.net就是个不错的参考站。2. 解剖PNG结构IDAT块里的玄机PNG文件就像俄罗斯套娃由多个数据块Chunk嵌套组成。IHDR块存放宽高等元数据PLTE是调色板而真正的图像数据都藏在IDAT块里。有次比赛我遇到张显示不全的图片用TweakPNG检查发现IDAT块CRC校验错误修复后果然露出了被截断的flag。实战中最常遇到两种IDAT块隐写异常CRC值正常情况应该匹配块数据但出题人可能故意改错多余IDAT块在正常图像数据后追加隐藏内容用Python的PyPNG库可以直观看到块结构import png reader png.Reader(mystery.png) for chunk in reader.chunks(): print(chunk[0], len(chunk[1]))如果发现可疑块可以用binwalk -e自动提取或者手动用dd命令精确切割dd ifsuspect.png ofhidden.zip skip1234 bs1这里的skip参数是隐藏数据的起始偏移量bs1表示按字节操作。记得先用binwalk或hexdump确认偏移位置不然就像我最初那样总提取出一堆垃圾数据。3. 工具链实战从binwalk到zsteg工欲善其事必先利其器我的工具包里常备这些神器binwalk文件分析瑞士军刀能识别300种文件签名foremost基于文件头的提取工具适合分离复合文件TweakPNGWindows下的PNG块编辑器可视化操作超友好zsteg专攻LSB隐写的利器支持PNG/BMP格式有道题用binwalk分析显示DECIMAL HEXADECIMAL DESCRIPTION ------------------------------------------------ 0 0x0 PNG image, 512 x 512 12345 0x3039 Zip archive data但foremost死活提取不出来后来发现是出题人修改了压缩包头。这时候就得祭出dd大法dd ifchallenge.png ofsecret.zip skip12345 bs1 count2048count参数限制提取大小避免把整个文件都dump出来。对于LSB隐写zsteg的自动检测比StegSolve手动调通道高效得多zsteg -a mystery.png | grep -i flag曾遇到张图用StegSolve调半天没发现zsteg一秒就报出extradata:0里有3544字节隐藏数据。4. 元数据挖掘EXIF里的宝藏相片不只是像素还有EXIF这座信息富矿。有次比赛flag就藏在相机的序列号里用exiftool查看时发现Serial Number : 686578285826597329这串数字转十六进制后正好拼出flag。常用的元数据查看命令exiftool -n -G1 -a suspicious.jpg参数说明-n禁止值转换保持原始数值-G1按标签分组显示-a显示重复标签特别注意这些字段Comment注释Software生成软件GPS Coordinates坐标信息Thumbnail缩略图有道题的flag藏在Photoshop的图层历史里常规工具看不到最后是用strings配合grep才找到strings creative.psd | grep -i ctfshow5. 二进制奇技淫巧hex与字节操作真正的隐写高手都直接玩二进制。有次遇到flag被拆散藏在文件末尾用010 Editor看到63 74 66 73 68 6F 77 7B ... 7D这明显是ctfshow{的hex编码。Python处理这种数据最方便with open(stego.bin,rb) as f: data f.read()[0x100:0x200] # 读取特定偏移量 print(data.decode(latin-1)) # 尝试多种编码常见套路还有字节倒序[::-1]切片隔位取值[::2]或[1::2]异或解密单字节或多字节key字节差值相邻字节相减比如这道题的解题脚本hex_str 631A74B96685738668AA6F4B77B07B216114655336A5655433346578612534DD bytes_obj bytes.fromhex(hex_str) flag bytes_obj[::2].decode(utf-8) print(flag)6. 避坑指南新手常见误区踩过无数坑后总结的血泪经验文件权限问题在Linux下用sudo运行工具可能导致生成的文件root权限后续操作报错字符编码陷阱Windows和Linux的换行符差异会导致脚本失效工具版本兼容性Python2/3的bytes处理差异可能引发解码错误隐写多层嵌套解出一层flag别高兴太早可能还要base64/rot13二次解码有次用binwalk提取出压缩包解压密码却藏在图片的ICC_Profile里。后来养成了习惯binwalk -e --run-asuser challenge.png # 避免root权限 file extracted/* # 检查提取文件类型 strings extracted/* | grep -i pass # 搜索密码提示对于CRC校验错误的问题推荐用pngcheck快速定位pngcheck -v broken.png输出会明确告诉你哪个块的CRC不匹配比起肉眼比对hex编辑器高效得多。7. 实战演练完整解题流水线以一道典型题目为例演示我的标准操作流程初步侦查file mystery.dat # 识别文件类型 xxd -l 32 mystery.dat # 查看文件头深度扫描binwalk -e -M mystery.dat # 递归提取 foremost -i mystery.dat -o output # 备用提取方案元数据检查exiftool -n -G1 -a mystery.dat identify -verbose mystery.dat # ImageMagick工具特殊处理zsteg -a mystery.png # LSB检测 stegsolve.jar # 手动分析通道(Java环境)自定义提取# 当标准工具失效时 with open(mystery.dat,rb) as f: data f.read() flag_pos data.find(bctfshow{) print(data[flag_pos:flag_pos100])最终解码echo ZmxhZ3tleGFtcGxlfQ | base64 -d这个流程不是固定的就像有次我死活找不到flag最后发现要先把图片亮度调到最高才能在角落看到微弱的flag文字。CTF的魅力就在于永远有意想不到的解题角度。