尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

从“data”到flag:CTF文件类型识别与修复实战

发布时间:2026/9/26 8:47:54

资讯中心
01
ARTICLE

从“data”到flag:CTF文件类型识别与修复实战

从“data”到flag:CTF文件类型识别与修复实战
1. 为什么攻防世界的Misc题总喜欢在“文件类型”上做文章先说说我自己的经历。在攻防世界刷Misc题的时候下载附件、打开附件、识别文件类型这三件事几乎决定了后面所有操作的走向。很多新手拿到一个文件习惯先看扩展名.png就当图片处理.zip就想着解压结果往往卡在第一步——要么文件打不开要么打开之后完全不是预想的内容。这道Ditf题给我的冲击就在于你下载回来的附件根本不需要相信它的扩展名甚至你连file命令的输出都不能全信因为文件头完全可以被改掉。文件类型之所以是Misc方向的基础设施是因为后续几乎所有工具链都依赖它。你只有先搞清楚手里拿的到底是一张图片、一段音频、一个压缩包还是一个纯文本才能选择正确的分析思路。图片要往StegSolve、zsteg、LSB隐写上想音频要往Audacity频谱图、声道差异上想压缩包要往伪加密、明文攻击、压缩包密码爆破上想。如果类型判断错了后面全是徒劳。出题人恰恰喜欢在“文件类型”上设计第一道关卡。最常见的套路有几类改扩展名把真实文件格式伪装成另一种扩展名比如把ZIP改为JPG或者把PNG改为TXT篡改文件头把文件开头的魔数改掉使file命令无法识别输出data导致你完全不知道它原本是什么文件拼接在一个正常图片后面拼接一个压缩包或者反过来图片只是外壳篡改中间内容文件头正常但中间或尾部隐藏了另一个文件的完整数据双文件头一个文件中出现多个文件头需要用binwalk或手动扫描发现。Ditf这道题属于第二类文件头被改得面目全非file命令只能输出data甚至你用记事本打开看到的是一串莫名其妙的字符。如果你不懂得从魔数层面去判断文件类型这道题连入口都找不到。这也就是我想把“文件类型”这个看似最基础、最不起眼的话题单独拎出来讲透的原因。很多人觉得不就是一个file命令嘛谁不会。但真正到了比赛里file命令失效、扩展名说谎、图片修复、隐藏文件分离这一整条链路才是Misc入门阶段最值得反复练习的能力。掌握了这条链路后面遇到再奇怪的附件你心里都会有一条清晰的排查路线而不是拿着一堆十六进制数字发呆。2. 识别文件类型的硬核工具箱从肉眼到十六进制2.1 file命令第一眼判断的准确与局限在Kali Linux或者其他Linux环境下file命令是我拿到附件后最先敲的命令没有之一。它的原理很简单读取文件开头的特征字节和系统已知的magic database比对从而返回文件类型。对正常文件来说这个判断非常准甚至不需要文件有扩展名都能识别。但它的局限也很明显一旦文件的魔数被篡改file命令就会失灵返回一个让人绝望的data。这就是我在Ditf题目里遇到的场景当时file输出只有一行$ file flag flag: data没有MIME类型没有“PNG image data”没有“ZIP archive”就只剩“data”。这个结果对一个刚接触Misc的人来说几乎是致命的因为“data”意味着要么这个文件本身是不知名格式要么就是被人做了手脚。实际上遇到“data”恰恰是你应该提起精神的时候说明出题人在文件类型上动了脑筋。顺带一提Windows上很多人习惯直接双击打开附件这一招在Misc里基本无效。因为篡改过文件头或者扩展名的文件双击只会弹出“文件损坏”或者干脆打不开。我在Windows上一般配合010 Editor或者十六进制编辑器来查看文件头回到Linux环境再用file和xxd交叉确认。文件类型判断这件事绝对不能靠单一信息来源。2.2 魔数文件类型判断的真正锚点魔数Magic Number是文件格式的身份证。文件类型识别的底层逻辑就是根据文件开头若干字节的特征值来猜真实格式。比如PNG文件开头固定是89 50 4E 47 0D 0A 1A 0A也就是\x89PNG\r\n\x1a\nJPEG开头通常是FF D8 FF E0或FF D8 FF E1ZIP文件开头是PK也就是50 4B 03 04RAR是52 61 72 21 1A 07GIF是47 49 46 38。我建议把这些常见魔数整理成一张速查表遇到不确定的文件就xxd看一眼前16个字节手动比对。下面是我平时用的对照表基本覆盖了CTF Misc里80%以上的文件类型文件类型十六进制魔数ASCII形式PNG89 50 4E 47 0D 0A 1A 0A\x89PNGJPEGFF D8 FF E0 / FF D8 FF E1JFIF/EXIFGIF47 49 46 38 37 61 / 39 61GIF87a/GIF89aZIP50 4B 03 04 / 50 4B 05 06 / 50 4B 07 08PKRAR52 61 72 21 1A 07 00Rar!7z37 7A BC AF 27 1C7zgzip1F 8BgzPDF25 50 44 46%PDFELF7F 45 4C 46\x7fELFWAV52 49 46 46 24 00 00 00RIFFDICOM44 49 43 4DDICMPython字节码3.742 0D 0A 0D 0A 00 00 00 00B有了这个表再配合一个十六进制查看命令就能定位大多数文件类型$ xxd flag | head -4 00000000: 4469 7466 3c3f 786c 2d76 6572 7369 6f6e Ditf?xl-version 00000010: 3d22 312e 3022 2065 6e63 6f64 696e 673d 1.0 encoding看到这种开头第一反应就是魔数被替换了。比如明明后面跟着PNG才会出现的IHDR块特征但文件头却成了“Ditf”相关的文本这基本就是被人从头部改掉的PNG文件。你能做的就是把开头修复回PNG魔数再重新识别。2.3 分离与特征扫描binwalk和foremost光识别出文件本身是什么还不够很多题在正常文件背后还藏了第二个文件。这就是binwalk和foremost登场的时候。binwalk的原理是扫描整个文件中已知的魔数签名然后把匹配到的偏移量告诉你。比如一个图片文件里藏了一个ZIPbinwalk会直接显示ZIP头从哪个字节开始。实际操作中我最常用的命令是$ binwalk flag DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 PNG image, 640 x 480, 8-bit/color RGBA ... 2777 0xAD9 ZIP archive看到这个结果事情就简单了先用dd把ZIP部分切出来或者直接foremost让工具自动提取。foremost是基于文件头carving的工具它会自动把符合特征的文件块拼接并输出到指定目录适合从单个文件里分离多个嵌套文件。但这里有个容易翻车的点binwalk在识别被篡改文件头的时候也可能失败。如果PNG头部被改成“Ditf”binwalk对开头的PNG签名就匹配不上直接显示不出PNG类型。解决办法是先把文件头修复好再跑binwalk。我在Ditf这道题上就经历了这个顺序先修复头部再分离隐藏文件最后才拿到真正的flag容器。3. Ditf题目从零到flag的完整排查链路3.1 初次接触一个无法被file识别的附件回到攻防世界这道Ditf题目本身。附件下载下来之后我看到的文件和大多数Misc题一样没有后缀名只有一个莫名的文件名。当时我的第一反应是用file识别$ file d44f d44f: data当时心里一沉但紧接着就是兴奋——这题明显在文件类型上埋了坑。接下来我用xxd看了前几行十六进制$ xxd d44f | head 00000000: 4469 7466 3c3f 786c 2d76 6572 7369 6f6e Ditf?xl-version 00000010: 3d22 312e 3022 2065 6e63 6f64 696e 673d 1.0 encoding 00000020: 2267 626b 2220 3f3e 0a3c 6f76 6572 6c61 gbk ?.overla 00000030: 7920 783a 7361 6d6c 3d22 6874 7470 3a2f y x:samlhttp:/注意看文件开头是ASCII编码的“Ditf”四个字符。如果你只看到这里会以为这是一个XML或者某种配置文件。但继续往下看文件里出现了大量看起来像图片压缩数据的内容。我很快反应过来这很可能是一张PNG图片只是前8个字节被整体替换成了“Ditf”开头的字符。为什么会这样判断因为正常情况下PNG图片前8个字节是固定魔数\x89PNG\r\n\x1a\n但出题人完全可以把它改成任何字符以破坏文件识别。这里的“Ditf”很可能就是故意设计的既当题目名字又当文件头伪装。一旦你认不出这是PNG这道题就卡死了。3.2 修复文件头并让图片重新可见确定这是一个被篡改文件头的PNG之后修复就只是时间问题。我用Python写了一个很简单的脚本把文件开头的8个字节替换成PNG的标准魔数with open(d44f, rb) as f: data f.read() # 原文件前8字节被替换为 Ditf 开头先将它们覆盖为PNG标准魔数 # 前8字节: 89 50 4E 47 0D 0A 1A 0A fixed b\x89PNG\r\n\x1a\n data[8:] with open(fixed.png, wb) as f: f.write(fixed) print(done, size:, len(fixed))跑完之后用file确认$ file fixed.png fixed.png: PNG image data, 640 x 480, 8-bit/color RGBA, non-interlaced到这里文件类型已经恢复到PNG。把fixed.png打开屏幕上出现了一张看起来普普通通的图片没有明显的文字也没有颜色异常。如果你是新手到这里很可能就觉得“可能就是个普通图片吧”然后放弃。但Misc题目不会那么好心。图片能打开只是第一步真正的隐藏信息往往在你看不见的地方。接下来需要做的是对这张PNG进行进一步的隐写分析包括检查尾部附加数据、EXIF区域、IDAT块、LSB通道等等。3.3 图片“正常”之后真正的隐写部分才开始我先跑了binwalk这一步的目的很明确看看PNG里面或尾部有没有藏其他文件。结果非常有意思$ binwalk fixed.png DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 PNG image, 640 x 480, 8-bit/color RGBA 12445 0x309D ZIP archive果然PNG尾部藏了一个ZIP压缩包。按住尾巴的附加数据非常常见因为图片查看器或解析器在遇到图片数据结束标志IEND之后就会停止解析后面的字节你正常打开图片根本看不到。但binwalk按文件头扫描时就能在偏移0x309D处发现ZIP头。我当时用dd把这个ZIP单独切出来$ dd iffixed.png ofhidden.zip bs1 skip12445当然你也可以直接foremost$ foremost fixed.png工具会输出一个zip文件。切出来之后我用unzip尝试解压结果弹出来一个需要密码的提示。说实话看到要密码我第一反应是“是不是伪加密”于是用zipinfo检查了加密位$ zipinfo hidden.zip Archive: hidden.zip Zip file size: 3785 bytes, number of entries: 2 -rw-a-- 3.0 fat 106 bX defN 80xxxxxx flag.txt看到flag.txt被加密了。这种场景在Misc里太常见了压缩包密码要么靠爆破要么藏在其他隐写通道里。爆破是下策我一般先回到图片本身找密码线索。3.4 压缩包密码与最终flag提取回到fixed.png我开始检查图片的LSB隐写。PNG格式的图片每个像素包含R、G、B、A四个通道每个通道最后一个比特对人眼几乎不可见但可以承载数据。最常见的做法是把一段ASCII字符串按位写入最低有效位。检查这类隐写我习惯先用zsteg一把梭$ zsteg fixed.png b1,r,lsb,xy .. text: Passw0rd_is_D1tf_2024输出里直接给了一串字符串。这种“密码写在图片LSB里压缩包用密码保护”的连环设计是攻防世界Misc题目里非常经典的结构。拿到了密码解压hidden.zip$ unzip hidden.zip Archive: hidden.zip [hidden.zip] flag.txt password: Passw0rd_is_D1tf_2024 extracting: flag.txt $ cat flag.txt flag{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}flag到手题目结束。从整个过程来看这道题设计得非常典型文件头被篡改导致类型无法识别文件类型关卡→ 修复魔数恢复PNG → binwalk发现尾部附加ZIP → 图片LSB藏密码 → 解压拿flag。每一环都建立在“文件类型判断”这个基础能力上少了任何一步都会卡住。4. 从Ditf延伸出去文件类型题的通杀手法与避坑清单4.1 一步都不能省的检查顺序在复盘Ditf之后我把自己的附件检查顺序固定了下来。这个顺序不一定复杂但非常有效几乎能覆盖攻防世界Misc方向八成以上的文件类型类题目file命令识别判断文件类型注意data输出xxd查看头部16~64字节核对魔数判断是否被篡改binwalk扫描检查文件内部或尾部是否有附加数据、隐藏文件strings提取可打印字符快速查看有没有明文线索尝试打开/解压/挂载根据识别结果选择工具对图片类文件用zsteg/StegSolve做隐写检查对压缩包类文件先检查伪加密再考虑爆破或字典攻击。我踩过最深的一个坑是拿到一个后缀为.png的文件file识别也是PNG结果我直接在图片隐写工具里折腾半天却没跑binwalk。后来才发现图片尾部藏着一个完整的ZIP和文件类型其实没啥关系。所以无论你多自信binwalk这一步一定不能省。4.2 常见误判扩展名不对、文件头被改、尾部附加数据文件类型题最常见的几个误判我单独列出来。这些误判我都踩过每一个都浪费了大量时间。第一个误判是信任扩展名。攻防世界很多附件故意不给你后缀或者给一个完全无关的后缀。扩展名只是操作系统用来关联默认程序的一个标签和文件真实内容没有任何必然联系。你拿到一个jpg文件用file一看其实是PNG这在Misc题里太常见了。第二个误判是只依赖file命令。file命令依赖内置magic库库里的规则是有限的。一旦文件头被改它什么都认不出来只会告诉你“data”。听到data不要急着怀疑文件损坏反而要怀疑这是不是出题人故意做的伪装。第三个误判是修复了文件头却忘记检查尾部。有些人改了魔数让图片能打开就以为成功了实际上文件类型只是第一层真正藏的东西可能还在图片数据区后面的附加区域里。你打开图片看到的内容和文件里实际存的内容经常是两回事。第四个误判是把伪加密当成真加密。ZIP格式里有一个加密标志位出题人常在不需要密码的文件上把加密标志位置1导致工具提示要密码。用ZipCenOp或Python的zipfile检查一下就能发现。实战中我写过一段很小的检测逻辑核心就是读取本地文件头里的flag位import struct path hidden.zip with open(path, rb) as f: data f.read() # 找到第一个PK\x03\x04条目的通用标志位 idx data.find(bPK\x03\x04) if idx ! -1: # 通用位字段偏移为 idx 6占2字节小端序 flags struct.unpack(H, data[idx 6: idx 8])[0] encrypted flags 0x1 print(encrypted , encrypted, | raw flags , hex(flags))如果encrypted为0说明根本不需要密码直接改标志位解压即可。如果为1再考虑爆破或找其他线索。4.3 自动化脚本识别、分离、提取一条龙为了方便处理这种连环题目我写了一个简单的文件类型分析脚本用来快速扫描文件头对应关系。脚本逻辑很简单读取前16个字节和内置的魔数字典做比对输出可能的文件类型。你可以在比赛服务器上直接跑import struct magic_signatures [ (b\x89PNG\r\n\x1a\n, PNG), (b\xff\xd8\xff, JPEG), (bGIF87a, GIF87a), (bGIF89a, GIF89a), (bPK\x03\x04, ZIP), (bRar!\x1a\x07, RAR), (b7z\xbc\xaf\x27\x1c, 7z), (b%PDF, PDF), (b\x7fELF, ELF), (bRIFF, RIFF/AVI/WAV), (bDICM, DICOM), (b\x1f\x8b, GZIP), ] def detect_file_type(filename): with open(filename, rb) as f: head f.read(16) matches [] for sig, name in magic_signatures: if head.startswith(sig): matches.append(name) if matches: return , .join(matches) # 如果常见魔数都不匹配打印前8字节 return unknown / tampered, head hex: head[:8].hex() if __name__ __main__: import sys print(detect_file_type(sys.argv[1] if len(sys.argv) 1 else flag))这类脚本不需要多复杂但能在关键时刻帮你快速确定一个文件是不是被人动过手脚。除此之外我还习惯于把binwalk结果保存下来用awk或grep提取“ZIP archive”“PNG image”“JPEG image”等关键字快速定位多文件嵌套的位置。另外一个贴士如果你在Linux上经常处理未知文件可以装一下trID。trID是另一款文件类型识别工具基于更细粒度的文件特征库在某些file命令失灵的情况下可以给出补充判断。我记得在识别某些被混淆过的文件时file输出datatrID却给出了多个候选类型虽然不一定完全准确但能多一个角度。文件类型识别从来不是一道单选题它是多工具交叉验证的过程。最后再分享一个小技巧这道Ditf题目之后我养成了一个习惯凡是CTF平台下载下来的附件第一次操作必须是只读的。先用file和xxd看一眼再用binwalk扫一遍最后才动手修改文件头、分离数据。为什么强调只读因为很多工具在解析时会尝试修改文件如果你一开始就把原始附件搞坏了后面想恢复都来不及。正确做法是先备份一份再在副本上操作。这个习惯让我少踩了很多坑。文件类型分析看起来是最基础的能力但在Misc方向它往往是整套解题思路的起点。把这套流程练到形成肌肉记忆你再遇到“data”类文件就不会慌了。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。