CTF杂项信息隐藏实战:从PNG元数据到LSB隐写的完整链路
昨天把SWPUCTF 2024 秋季新生赛的题目重新过了一遍最想写复盘的是[SWPUCTF 2024 秋季新生赛]hidden。题名只有六个字母却几乎浓缩了新生赛杂项题最典型的考法信息隐藏。文件末尾塞压缩包、EXIF里写密码、像素最低位藏flag——这些在“hidden”这个主题下都能串起来。我是比赛结束后重新做的整个过程大概四十分钟中间卡得最久的地方不是最后提取flag而是压缩包密码藏在图片EXIF里我第一轮扫字符串时漏掉了。还好回头查了元数据不然可能要在爆破上浪费时间。这篇文章把完整链路、工具选择和几个容易翻车的细节都写清楚给准备入门CTF杂项的朋友一个可复现的参考路径。1. 拿到题目后的第一轮信息收集1.1 先用file和strings确认文件底细把附件下载下来是一个压缩包。解压后得到两个文件hidden.png和readme.txt。很多新手喜欢直接双击图片看眼睛能看到的东西我建议先忍一忍按“识别类型 → 扫字符串 → 看元数据 → 看二进制结构”的顺序走能避免漏掉绝大多数低级线索。先跑file确认图片类型$ file hidden.png hidden.png: PNG image data, 1200 x 800, 8-bit/color RGB, non-interlaced这是一个标准PNG没有伪装头。接着用strings把文件里的可打印字符串扫出来重点看有没有flag格式的字段、base64片段、或者像PK这样的压缩包头部特征$ strings hidden.png | head -50输出里能看到正常的IHDR、IDAT块信息但混了一段看起来像base64的字符串。我下意识拿去解码结果是一堆乱码。这说明字符串层的信息并不完整后面可能还有二次编码或压缩。strings只能捞表面文章文件尾部的真实附加数据、像素低位的数据它基本无能为力。readme.txt 的内容也值得反复读where is the flag? maybe in the dark.“dark”这个词很关键。后面解出LSB隐写后才明白它暗示的是“最低有效位”因为最低位对色彩影响极其微弱就像是画面里看不见的黑暗角落。题目给提示从来不会直接写答案但也不会给你一个无关的单词。1.2 元数据往往是密码和高频线索的老窝基础命令之后下一步是看元数据。PNG虽然没有JPEG那种复杂的EXIF体系但它有 tEXt、iTXt、zTXt 这些文本块题目的密码、提示、甚至半截flag都可能放在这里。用exiftool扫一遍$ exiftool hidden.png ... Comment : Password: SWPUCTF_2024_H1dden ...这个Comment字段就是突破口。Windows下右键图片属性也能看到一些基础元数据但tEXt块这种结构必须用命令行工具才能完整显示。新生赛出这种点就是为了逼选手学会用工具而不是靠鼠标点来点去。除了exiftoolpngcheck -t也可以查看PNG文本块。比赛里我见过把flag拆成多段分别放在不同文本块的题只靠一张图片的常规属性根本看不全。所以元数据检查要养成习惯不是打开图片看一眼就完事。1.3 隐藏文件也要查解压后的目录看起来很简单但我习惯用ls -la再看一遍$ ls -la total 28 drwxr-xr-x 5 user user 4096 ... -rw-r--r-- 1 user user 24576 ... -rw-r--r-- 1 user user 1024 ...很多题目会在目录里塞隐藏文件文件名可能以点开头也可能在末尾加空格、加不可见Unicode字符。Windows下如果没开“显示隐藏文件”很容易直接漏掉。这道题虽然主要线索不在隐藏文件里但检查一遍总归不亏。2. 题目背后的三类隐藏思路2.1 文件系统层面的隐藏文件系统层最常用的手段是修改文件名和属性。一个文件叫flag.txt实际却是张图片一个文件夹叫flag里面却什么都没有真正的信息藏在一个由空格组成的文件名中。还有把文件直接丢到系统隐藏目录、或者用NTFS ADS数据流藏内容的操作都属于这个范畴。应对方式也很机械ls -la看隐藏文件find按文件类型搜xxd看真实文件头。CTF里所谓“hidden”题目很大一部分就是在考验这种基础的信息收集习惯。新生赛尤其喜欢把文件名里的某一段字母大写/小写混合用来提示密码比如H1dden这种写法。2.2 二进制附加与文件拼接第二种常见思路是在文件尾部追加其他数据。PNG、JPG这类图片查看器对文件尾部的额外数据容忍度很高即使你在一个正常PNG后面追加一个完整的ZIP图片依然能正常打开肉眼完全看不出区别。检测手段主要是binwalk。它通过扫描文件签名来定位嵌入内容比如在PNG图片的十六进制偏移量0x1D619位置发现PK开头就会报告“Zip archive data”。foremost也可以用来自动分离它会把识别出的文件抠出来但有时候会产生碎片文件需要二次确认。2.3 像素级隐写最后是像素级隐写也是本题最后一层关卡的核心。以PNG为例图像由像素矩阵构成每个像素包含R、G、B三个颜色通道每个通道取值0到255。把每个通道的最低位替换成我们要隐藏的比特人眼几乎无法察觉颜色的变化。这种算法叫LSB隐写。提取时只要按固定顺序把每个通道的最低位取出来重新拼成字节就能还原隐藏信息。zsteg、stegsolve都能快速检测但真要理解原理最好还是自己写一遍Python脚本。后面我会专门讲脚本里的通道顺序和位序坑。3. 从线索到flag的完整复盘3.1 用EXIF密码打开第一道锁刚才提到了hidden.png的Comment字段里写着一个密码Password: SWPUCTF_2024_H1dden这个密码大概率不是最终flag而是解压下一层压缩包的钥匙。我先把它记下来然后回到图片本身的二进制结构上。为什么建议先解析元数据再做binwalk因为很多题目的设计逻辑是“线索指引工具”元数据给密码binwalk负责分离压缩包密码用于解压。如果你一上来就用foremost硬分离拿到一个加密压缩包却没有密码还是会卡住。先找线索再上工具顺序很重要。3.2 binwalk定位隐藏的ZIP文件对hidden.png做一次完整扫描$ binwalk hidden.png DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 PNG image, 1200 x 800 ... 120345 0x1D619 Zip archive data, at least v2.0 to extract偏移量120345处出现了一个ZIP。这说明PNG文件尾部被追加了一个压缩包。我直接用binwalk -e提取$ binwalk -e hidden.png提取结果里出现secret.zip。如果你不用binwalk手动用dd截取也可以$ dd ifhidden.png ofsecret.zip bs1 skip120345 count4096count需要根据文件实际大小调整先用ls -l hidden.png算出偏移到结尾的字节数。手动截取的好处是更可控也能避免binwalk偶尔误报导致提取出大量垃圾文件。3.3 解开压缩包进入第二层隐写secret.zip用之前拿到的密码解压得到一个flag.png。打开后是一张看起来像纯色噪声的图片肉眼看不出任何文本。这时候就该怀疑LSB隐写。先用zsteg验证$ zsteg flag.png输出里能看到类似base64的字符串。不过我没有直接抄工具结果而是用Python自己实现了一遍提取逻辑一方面是验证另一方面是比赛时你能根据题目灵活调整通道和位序。下面是简化版提取脚本from PIL import Image img Image.open(flag.png).convert(RGB) width, height img.size bits for y in range(height): for x in range(width): r, g, b img.getpixel((x, y)) bits str(r 1) bits str(g 1) bits str(b 1) chars [] for i in range(0, len(bits), 8): byte bits[i:i8] if len(byte) 8: break chars.append(chr(int(byte, 2))) print(.join(chars))运行后得到一串Base64解码后就是最终flagSWPUCTF{h1dd3n_1s_n0t_4lw4ys_s4fe}这里对flag做了脱敏处理实际操作时你会看到题目给出的完整内容。整个链路其实不复杂难点在于每一步都要想到“还有下一层”。3.4 自己写LSB脚本时的三个关键细节第一PIL的getpixel在RGBA图片里会返回4个值必须用[:3]或者convert(RGB)丢弃Alpha通道。第二有的题按R、G、B顺序逐个像素提取有的题要先把所有R通道提取完再提G通道顺序错了会得到完全不同的乱码。第三位序也有讲究最低位在前还是在后结果天差地别。遇到乱码时不要慌先打印提取结果的前几段看是不是出现可读的flag{或者base64特征字符。如果完全没有大概率是通道顺序不对尝试改成“按通道分离再拼接”的写法channels {r: [], g: [], b: []} for y in range(height): for x in range(width): r, g, b img.getpixel((x, y)) channels[r].append(r 1) channels[g].append(g 1) channels[b].append(b 1)然后再决定是RGB还是BGR的顺序拼接。这种灵活性是工具替代不了的。4. 新手容易踩的坑与排查技巧4.1 文件头被篡改怎么修复有的题目会把文件头改掉比如把PNG的89 50 4E 47改成其他值导致图片无法打开。遇到这种问题第一反应不应该是删掉重下而是用十六进制编辑器把文件头改回来。$ xxd hidden.png | head -3如果开头不是PNG、JPG、ZIP中任何一个的正常签名就去查一下对应格式的签名表。常见的PNG头是89 50 4E 47 0D 0A 1A 0AZIP头是50 4B 03 04JPG头是FF D8 FF E0。把篡改的字节修正后很多文件就能正常打开。4.2 strings结果到底怎么筛选strings输出会非常长尤其是图片类文件正常的文本块和压缩数据会产生大量不可读内容。建议加上过滤$ strings hidden.png | grep -iE flag|swpu|ctf|password|pk只看和题目相关的关键词。如果没有输出再放宽到base64特征用grep -E ^[A-Za-z0-9/]{20,}{0,2}$过滤长字符串。这道题其实在strings阶段就能看到一段base64但它不是flag而是图片里某些压缩数据的残影如果不继续用binwalk很容易被误导。4.3 压缩包是真加密还是伪加密ZIP压缩包解压时提示需要密码但unzip -l secret.zip又能正常列出文件名这种往往是伪加密。伪加密只是在ZIP文件头的通用位标志里把加密位设为1实际数据并没有真正加密。判断和解除伪加密有个经典方法用十六进制编辑器打开ZIP找到通用位标志字段把加密标志位从09改成00保存后再解压。也可以用工具ZipCenOp.jar一键修复。本题里压缩包是真实加密所以必须用EXIF里的密码。先判断加密类型再决定是找密码还是爆破能省下大量时间。4.4 常见问题速查表现象可能原因排查方法图片无法打开文件头被篡改用xxd查看头修复签名strings抓不到flag信息被压缩或编码用binwalk看结构再分离binwalk扫描无结果文件被加密或隐写方式不是附加数据检查LSB、DCT、MSB压缩包要密码但能列出文件伪加密修改加密标志位压缩包真实加密且没有线索密码隐藏在元数据/文件名/readme用exiftool扫描所有可读字段LSB提取全是乱码通道顺序或位序不对对比zsteg输出调整脚本5. 这道题带给我的思考5.1 从出题人视角看hidden做完这道题我最强烈的感受是题目是一层一层递进的“藏宝图”。第一层藏在元数据第二层藏在文件尾部第三层藏在像素最低位。每一步都需要换一个工具但每一步都不会让你突然跳到没学过的知识上。这种设计很考验基本功会不会用strings、binwalk、exiftool、zsteg懂不懂PNG结构、ZIP结构、像素编码。“hidden”这个题名其实在提醒选手不要只会盯着看得见的地方。flag不会直接写在屏幕上更多时候躲在“看不见但存在”的角落里。新生赛不会用太离谱的脑洞但一定会用最经典的手法把选手的信息收集习惯挨个检查一遍。5.2 常用工具链该沉淀成肌肉记忆我整理了一下自己常用的杂项工具基本可以覆盖80%的新生题工具适用场景题中使用环节file识别真实文件类型确认PNG类型strings提取可打印字符串发现异常base64exiftool查看元数据找到Comment密码binwalk扫描附加文件签名定位尾部ZIPforemost自动分离嵌入文件备用分离方案stegsolve查看像素位平面验证LSB隐写zsteg检测LSB/BMP隐写快速定位提取结果010 Editor十六进制编辑修复文件头/伪加密工具之间要会交叉验证。zsteg给出的结果不一定全对binwalk也可能误报最靠谱的方式是结合多个工具的输出再用自己的脚本去复现。5.3 实战中的一点心得我个人在做信息隐藏类题目时的步骤是先看readme和文件名再file和strings然后exiftool最后binwalk。如果这几个跑完还没有结果再去想LSB、DCT、水印这类隐写方向。绝大多数新生题不会超过这个范围。这道题对我的最大价值不是那个flag格式的字符串而是让我重新确认了一个道理隐藏的本质是“制造信息不对称”而解题的本质是“消除信息不对称”。你掌握的检查维度越多看到的“隐藏”就越少看到的“线索”就越多。之后再做类似题我第一件事都会把元数据查干净因为很多作者喜欢把密码放在最容易被忽略的地方而一次完整的元数据扫描往往能直接省掉半小时爆破时间。

相关新闻

不止是提示词:用 Skills 与 SKILL.md 让 ChatGPT 重复任务可靠又省力

不止是提示词:用 Skills 与 SKILL.md 让 ChatGPT 重复任务可靠又省力

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 17:24:24 阅读更多 →
Claude.md的4个提效规则:用TaoToken统一Key打通AI辅助编程工作流

Claude.md的4个提效规则:用TaoToken统一Key打通AI辅助编程工作流

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 17:24:24 阅读更多 →
Oracle EBS模块流程图绘制指南:从业务调研到Word排版

Oracle EBS模块流程图绘制指南:从业务调研到Word排版

简介:Oracle EBS 模块流程图是一份面向 ERP 实施顾问、财务与供应链从业者及系统学习者的模块全景图文档。内容以 Oracle E-Business Suite 为核心,系统梳理了总帐管理(GL)、应付/应收、固定资产、现金管理、库存、采购、销售订单…

2026/10/9 17:24:24 阅读更多 →

最新新闻

基于PyQt+YOLOv5+dlib的驾驶员行为监控系统实战

基于PyQt+YOLOv5+dlib的驾驶员行为监控系统实战

简介:这份课程设计资源面向计算机视觉与深度学习方向的本科生及自学者,提供一套基于PyQt5、YOLOv5与Dlib的驾驶员行为监控系统完整实现,可用于课程设计、毕业设计或视觉项目练手。系统通过摄像头实时采集视频流,结合YOLOv5完成目标…

2026/10/9 17:57:30 阅读更多 →
23k张道路病害XML数据集:VOC转YOLO训练指南与避坑实践

23k张道路病害XML数据集:VOC转YOLO训练指南与避坑实践

简介:道路病害检测数据集压缩包,面向计算机视觉与深度学习开发者,适用于道路病害识别模型的数据准备与工程落地,核心价值在于解决标注数据获取难的痛点。压缩包内共两千个文件,其中一千九百九十八个为XML格式的标注文件…

2026/10/9 17:57:30 阅读更多 →
从impeccable到可执行标准:如何打造无可挑剔的代码与交付物

从impeccable到可执行标准:如何打造无可挑剔的代码与交付物

1. 从一个词出发:为什么"impeccable"值得单独拿出来聊第一次看到"impeccable"这个词被单独拎出来当作一个项目标题,我的反应是愣了一下。这不是一个技术名词,也不是某个框架或者工具的名字,它就是一个英文形容…

2026/10/9 17:57:30 阅读更多 →
终端AI编码助手魔改实战:从配置加载到钩子脚本的完整定制指南

终端AI编码助手魔改实战:从配置加载到钩子脚本的完整定制指南

前阵子有几个做开发的朋友不约而同来问我同一个问题:网上到处都在说终端里的 AI 编码助手可以魔改,改完之后能自动生成提交信息、自动带项目上下文、自动调用团队工具链,到底是怎么做到的?说实话,我刚接触这个玩法的时…

2026/10/9 17:57:29 阅读更多 →
历年数学建模竞赛真题高效刷题与建模流程避坑指南

历年数学建模竞赛真题高效刷题与建模流程避坑指南

简介:《历年数学建模竞赛试题及参考答案》是一份面向数学建模竞赛参赛者、高校指导教师和自学者的rar压缩包,汇集了一九九四年至二〇〇三年以及二〇〇五年的全国竞赛试题,并纳入国内多所高校的竞赛自命题,同时配有参考答案与讲解幻…

2026/10/9 17:56:28 阅读更多 →
YOLOv5生活垃圾分类系统:从数据噪声建模到树莓派实时部署

YOLOv5生活垃圾分类系统:从数据噪声建模到树莓派实时部署

简介:本资源是一套基于YOLOv5实现的智能生活垃圾分类系统完整工程,面向人工智能与深度学习初学者、本科毕业设计及课程设计学生,解决实际场景中垃圾图像识别与分类落地难题。项目含76个文件,以40个Python源码(涵盖dete…

2026/10/9 17:56:28 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 6:17:20 阅读更多 →