CTF图片隐写入门:修改PNG高度字段破解‘大白1’的完整思路
1. 拿到“大白1”后的信息收集阶段别急着看图1.1 解压压缩包后的第一反应我第一次在BUUCTF的MISC分类里看到“大白1”这道题时第一反应是这名字太有迷惑性了看起来像是一张人畜无害的角色图片。下载下来是个压缩包解开之后里面躺着一张PNG文件文件名叫作类似baymax.png的东西双击一开屏幕上出现一只圆滚滚的白色机器人就是动画片里那个大白。如果你刚接触CTF很容易在这里停下来花五分钟对着大白傻笑然后得出结论这题没有问题出题人就是想要我欣赏大白。这就是新手最容易踩的第一个坑——把图片当成“图片”而不是当成“容器”。CTF杂项里绝大部分图片类题目图片本身只是一个包装壳真正的flag可能藏在文件末尾、藏在LSB最低有效位、藏在Exif信息里或者藏在一张被裁剪掉的画布区域中。所以我接手任何一道MISC图片题拿到文件的第一个动作永远是把它当作一个需要拆解的二进制对象而不是一张可以看的画。先复制一份原始文件到工作目录保证原文件不被破坏然后按下面的顺序做基础体检。1.2 用file / strings / binwalk 做基础体检在Windows环境下很多人第一件事是用各种图形化工具去点这不叫排查这叫碰运气。我更习惯开一个终端先把文件的基本信息和文件类型确认下来。file baymax.png正常情况下输出是PNG image data, 600 x 400, 8-bit/color RGB, non-interlaced这类信息。这一步能确认它是不是真的PNG。如果一道题名叫“大白”实际给了一个JPG或者一个没有文件头的文件那又是另一个玩法。接着用strings扫一下看里面有没有可直接读出的flag格式字符串。strings baymax.png | grep -i flag strings baymax.png | tail -50BUUCTF的题目flag格式一般是flag{...}所以直接搜索flag{是最快的判断方式。如果strings没有搜到再上binwalk做文件分离探测。binwalk baymax.pngbinwalk的用途是检查这个PNG文件里是不是还嵌了其他文件例如一个ZIP压缩包、另一张图片、或者有特征头的隐藏数据。很多新手在这一步容易忘记导致错过藏在文件末尾的flag。在我的实际排查中“大白1”这个文件用binwalk并没有扫出额外的隐藏文件文件头没有问题strings也没有直接暴露flag看起来就是一张干净图片。但这种“干净”反而更可疑——说明出题人把它做成了纯图片隐写方向而不是文件附加数据方向。这时候重点就转移了去研究PNG文件自身的结构参数。这里我先把当时的排查记录贴在下面方便你自己对照检查项结果结论file命令PNG格式尺寸感知正常基本排除改文件头伪装strings搜索未出现flag或提示文本排除明文附加数据binwalk扫描无嵌入式文件排除文件拼接/分离题型图片预览只有完整大白无异常需要深入PNG结构层走到这一步接下来要怀疑的就是PNG宽高字段。2. 图片尺寸被裁导致的隐写原理与判断依据2.1 PNG结构里的IHDR块藏了哪些信息PNG图片和JPG的存储逻辑完全不同。PNG是一个极其讲究“块结构”的格式文件头部8字节是固定签名之后是各种数据块。其中第一个数据块一定叫IHDR这个块里固定写入图片的宽度、高度、位深、颜色类型、压缩方法、过滤方法、扫描方式以及最后一个CRC32校验值。IHDR块的信息排列是固定的前4字节图片宽度接着4字节图片高度接着1字节位深接着1字节颜色类型接着1字节压缩方法接着1字节过滤方法接着1字节扫描方式最后4字节CRC32校验大部分人平时根本不会去关心这串字节但这正是很多隐写题的命门。出题人可以拿一张原本内容完整的图片用工具把高度字段改小比如把800高度改成300然后用支持辨色的看图软件打开只会看到上面300像素的内容。下面500像素的内容并没有被删除只是被图片查看器“裁掉”了。真正的flag很可能就藏在被裁掉的区域里。反过来说出题人也可以把一张大白图片的宽度改得很窄让你看到的大白被压扁从而藏起旁边的提示。无论是哪种只要PNG的IHDR块里宽高字段和真实解码内容不一致图片查看器就会按照修改后的值去渲染多出来的部分就会被吞掉。2.2 怎么判断图是被裁过而不是正常缩略很多新手会问我怎么知道一张图是被裁过还是天生就这么大这个问题没有万能公式但有三个很有效的判断方向。第一个方向是图片显示比例失衡。“大白”这个角色如果被改成600 x 60这样的高度图像会显得异常扁平看到这种比例就能初步怀疑高度被改过。第二个方向是看file命令输出的宽高数据和图片实际视觉内容是否匹配。如果文件属性里显示宽度600、高度60但图片内容是一个完整的大白全身这个比例根本装不下大白那就说明高度字段一定被动过。第三个方向是看PNG文件里IDAT数据块的实际解压数据量。一张高度只有60像素的PNG图片正常情况数据量会很小但如果文件体积明显偏大多了大量压缩数据它背后很可能隐藏着更大的真实画布。我当时在“大白1”这道题上遇到的情况是图片看起来是个正常的大白形象但整体比例有点“憋屈”人物被压得扁扁的底部似乎被截断。再一看file输出的尺寸参数高度明显偏小。到这里判断方向就非常明确了这个PNG的高度字段大概率被出题人改过需要手动把它改回真实值。这里我多说一句这种隐写方式在早期CTF题里很常见因为实现成本低出题人只需要改几个字节但做起来足够让新手卡很久。很多2020年前后的MISC入门题都长这样“大白1”就是其中很有代表性的一道。3. 使用十六进制编辑器修正PNG高度字段的完整过程3.1 定位IHDR并理解宽高字段的字节排列判断出高度被裁之后下一步就是动手改。这里我最常用的工具是010 Editor界面直观支持十六进制编辑左侧还有结构体解析能看到当前光标位置的字段含义。Windows下也可以用WinHex但功能不如010 Editor齐全如果你想用免费的HxD也完全够用。打开baymax.png后先找到前面我说的IHDR块。PNG的头部8字节固定是89 50 4E 47 0D 0A 1A 0A这8个字节后面紧跟着4字节长度00 00 00 0D再往后4字节就是块类型IHDR。所以宽度字段从文件偏移0x10开始连续4字节紧接着的4字节就是高度字段。以我当时看到的文件为例十六进制区域大约是00000000 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00000010 00 00 02 58 00 00 00 3C 08 02 00 00 00 ...其中00 00 02 58转为十进制就是600这是宽度00 00 00 3C转为十进制是60这就是被修改后的高度。真实高度不应该是60因为正常的大白图片不可能只有60像素高。那么真实高度是多少最直接的方式是把高度往大了猜。常见的高度可能是200、400、800甚至1000。如果你不确定可以先把它改成和宽度一样的600或者改成800再打开图片看效果。不过这里有个关键问题PNG文件是有CRC32校验的。如果直接改了高度字段工具在打开图片时可能会提示“PNG图片校验失败”或者干脆显示不出来。3.2 修改height后CRC校验失败的处理方案在PNG文件里IHDR数据块自身的CRC32校验值是针对IHDR块内所有字节计算的改了宽度或高度原本的校验值就对不上了。第一种做法最省事借助修改PNG校验和的小工具pngcrc或者直接在010 Editor里重新计算该数据块的CRC32然后覆盖到最后4字节。但我不推荐新手一上来就依赖工具因为一旦你理解了校验逻辑以后遇到类似题会更快。这里我给出010 Editor手动重建CRC的流程把高度字段从00 00 00 3C改成你推算的真实值例如改成00 00 01 90也就是400。选中IHDR块中从长度字段之后到CRC之前的全部字节也就是从49 48 44 52开始直到数据块结尾的内容。使用010 Editor菜单里的“Insert/Insert Pattern”或者工具面板中的CRC计算功能选择CRC32多项式通常是0x04C11DB7初始值0xFFFFFFFF输出按大端序填入文件。把计算得到的4字节值写回到高度字段之后原本的CRC位置。如果你觉得手动选范围容易出错还有一个更稳妥的脚本思路用Python解析PNG数据块自动算出新CRC并写入。写一个最小脚本读原始文件找到IHDR块把高度字段替换再计算CRC32最后覆盖。这样比手点更不容易出错尤其是当你以后要批量处理多张图片的时候。我当时实际用010 Editor改了高度后没有立刻改CRC而是直接保存打开图片——结果收到的是一张“图片损坏”的报错。这个报错不是坏事它反而进一步确认了文件结构确实被动过。接着我把CRC修正图片就能正常打开了。3.3 重新打开图片并定位flag把高度调整为推算值并修复CRC后重新打开图片原本被截断的画面底部会露出新的内容。在“大白1”这道题里完整的flag就出现在这种方式下图片底部显露出一句话里面直接带着flag{...}格式的答案。拿到flag之后建议顺手把flag复制到自带的记事本里检查格式是否完整避免漏字符或者把大小写搞错。BUUCTF平台的flag提交是不区分大小写的吗不同题目有不同要求稳妥的办法是严格按照图上显示的字符提交不要自行转小写或其他操作。另外还有一个细节如果你改高度后看到的是一堆乱码色条大概率不是高度不对而是本身的修改值不恰当或写错了位置。这个问题我在下一部分单独展开。4. 新手最容易在哪个环节翻车完整踩坑排查记录4.1 我一度以为原图就是这样第一次接触这种题型时我也干过一件蠢事下载图片后打开一看是个完整的大白我没觉得比例异常顺手就想关掉去做下一题。因为我的潜意识里一直认为“出题人会给我一张看起来有问题的图”实际上很多出题人恰恰喜欢把问题藏得很隐性。后来我复盘时发现我之所以没看出来是因为我用系统自带的“照片”工具打开的图片它会自适应窗口缩放导致一张被压扁的图片看上去好像很正常。这个问题在CTF做题中非常常见所以后来我对“目测”这个行为越来越警惕。这里有个建议拿到图片后不要只看视觉要先看尺寸数值。把文件拖进010 Editor直接看IHDR里的宽高或者用file命令读出来看宽高比是否合理。只有数值能骗不了人。4.2 CRC不匹配的教训直接用工具改完就打开再分享一个当时真实的翻车过程。第一次改高度时我用的不是010 Editor而是一个非常轻量的十六进制工具。我改了高度字段保存然后打开图片图片软件直接报错说“PNG文件损坏”可我原以为只要改了高度就能看到flag。那一次我在报错上卡了十分钟后来才意识到是CRC校验的问题。这个坑特别值得展开说一下PNG的CRC校验并不是可选项它是PNG规范里强制规定的东西。很多图片查看器在遇到CRC不对时会拒绝渲染或者在发现数据块异常时直接放弃显示。如果你改了宽高没有同步修正CRC看到的就是“损坏”二字的报错。所以正确顺序永远是改字段 → 修校验 → 再看图。这里也顺便提一个免费工具tweakpng它能在Windows上直接修改PNG块修改IHDR宽高后点一下保存会自动重算CRC。对新手来说这是最快的一种方式适合在不想手改十六进制的时候用。但我个人建议至少完整手动操作一次理解原理后再用工具提速。4.3 改完之后图片显示成“色块”怎么办还有一类情况高度改完之后图片能打开了但画面不是大白的完整形象而是变成下半部分是彩色噪点、黑色条纹或者大量重复色块。这种情况通常意味着修改后的高度和真实存储内容不匹配。为什么会出现高度不匹配因为PNG的IDAT数据块里存储了真正的像素数据这些数据是按行排列的。如果你把高度改得比真实像素行数少图像就会被截掉如果你把高度改得比真实像素行数多图像解码器就会去读取不存在的额外行这些“额外行”在文件里可能是空的也可能是其他数据于是渲染出乱七八糟的色带。这时候不要慌做法是往回调整高度值尝试不同数值或者结合宽高比推断一个更合理的值。如果你知道图片上大白是全身照而宽度是600那你应该把高度往300到600之间的方向试。多试几次直到画面完整清晰为止。我再补充一个判断技巧看修改后的图片是否出现“左侧或右侧有一列杂色”这是宽度设置不匹配的典型特征。如果是横向裁图题问题和解决思路与高度裁图完全对称。5. 从“大白1”延伸出的一套MISC图片题排查流程5.1 图片隐写常见玩法对比“大白1”这种改高度出flag的思路只是图片隐写里的一种。BUUCTF的MISC分类下面还有大量相似但不完全相同的题目常见玩法大致可以分成这么几类修改宽高隐藏画布内容这类题目的标志是最初图片比例异常或者图片底部/右侧有明显截断感解决手段是修改PNG的IHDR宽高并修正CRC。LSB隐写把信息藏在每个像素最低有效位中肉眼完全看不出来解决手段是用stegsolve、zsteg提取最低位通道。文件附加数据flag直接追加在图片文件末尾解决手段是用strings或foremost/binwalk分离。图片属性信息把flag写在Exif、注释块或者文件属性里解决手段是查看文件元数据。图像通道分离把flag藏在某个颜色通道中用StegSolve切换通道就能看到。GIF多帧拼接把flag分散在动图的某一帧里需要拆帧查看。把这些玩法放在一起看你就会发现做题不是靠单独一招而是需要一套固定的扫描流程按顺序排除。5.2 推荐工具链和检查顺序我自己的图片题检查流程大致如下file先确定文件真实类型。strings快速搜索可见字符串尤其是flag、key、txt这些关键字。binwalk检测是否嵌入了其他文件。用foremost试着做一次文件分离配合binwalk -e使用。用StegSolve打开图片依次切换红绿蓝通道、最低位平面观察是否有隐藏内容。用zsteg对PNG/BMP做LSB检测它会自动找出比较隐蔽的隐写内容。用010 Editor检查PNG的IHDR宽度高度、文本数据块、时间数据块等元信息。如果以上都没有结果再考虑图片是否被加密、压缩包是否伪加密等额外情况。在这套流程中“大白1”走到第7步就出结果了。你会发现很多MISC入门题其实不需要多么高深的技术只要流程完整、耐心足够答案自己就会浮出来。工具准备上我的建议是StegSolve、010 Editor、HxD、binwalk、zsteg、foremost这六个基本就能覆盖九成图片隐写题。如果是在Kali Linux环境下前几个都可以用apt直接装如果是Windows推荐装一个WSL再配Kali工具链会比较顺手。5.3 对刚接触BUUCTF的人的一点做题建议最后聊一点题外话。BUUCTF的题目难度跨度挺大“大白1”属于那种用一两个知识点就能解决的问题但它背后隐含的要求是你得能静下心来做基础信息收集而不是一上来就找答案。我见过不少新人拿到题目后第一个动作是去搜索引擎找writeup这可以理解但如果你根本不理解为什么改高度、为什么CRC要修正下次碰到类似的题换个皮你还是不会。我的建议是每道题先独立做半小时做不出来再看解题思路看完之后一定要自己重新操作一遍把关键字段位置、校验逻辑、工具用法都记下来。“大白1”这个题目本身不难但它能帮你打通一条很重要的思维链路文件不只是内容文件本身也是信息载体。在CTF里所有看起来没用的信息都可能在下一层解码中变成突破口。把这个思路建立起来后面再去看那些“二维码”“纳尼”“救世捷径”之类的题目你会发现自己已经有了第一个可复用的方法论。说回这道题本身我从下载压缩包到最终提交flag总共花了不到二十分钟。其中大部分时间不是浪费在解题技巧上而是浪费在我第一次改完CRC没更新被图片报错误导了。所以如果你也卡在“大白1”上不妨回头检查一下高度字段改对没有CRC更新没有。这两点没问题flag基本就摆在你眼前了。