
1. 先把RCTF2019 disk1这条线的玩法理清楚磁盘取证在CTF里是个很有意思的类别。别的题是给你一个服务、一段流量、一个二进制让你去打、去分析、去逆取证题则是直接甩给你一块“硬盘”——可能是raw镜像、vhdx、VMDK甚至E01格式让你像侦探一样把藏在文件系统、删除记录、加密容器里的线索一条条挖出来。RCTF2019这道disk1就是当年把“磁盘取证”这个方向考得比较综合的一道题题目命名里带个disk1暗示得很明显它很可能不是孤立的后面大概率还连着同类题目你得把这块盘里的“证据链”完整还原出来。我第一次拿到这道题的时候第一反应是“这不就是个文件恢复题吗”用foremost一顿跑乱挖就完事了。但实际做下来发现出题人明显没打算让你用一把工具梭哈到底。它考察的是你对磁盘布局的理解、对文件系统残留的敏感度、对加密容器和压缩包口令的鉴别甚至还得有耐心去翻那些没人注意的碎文件。简单说这道题不是在考“你会不会用工具”而是考“你懂不懂数据在磁盘上到底是怎么留存的”。这篇文章我会把整道题的复盘过程写完整从拿到镜像的第一步侦察到文件系统分析、删除数据恢复、加密压缩包口令的定位再到最后导出flag的完整链路。如果你刚接触磁盘取证类CTF这篇文章可以当一份“从零到一”的实操指南如果你已经打过一些取证题里面的排查思路和坑点记录应该也能帮你少走弯路。2. 初始侦察从比特流到文件系统的抽丝剥茧2.1 先确认你手里拿的到底是什么拿到一个磁盘镜像最忌讳的就是直接无脑挂载或一通搜索。第一步永远是确认格式、大小、哈希、分区布局。这道题的镜像从比赛渠道下载下来后我习惯先做一个文件类型确认和完整性校验。$ file disk.img disk.img: DOS/MBR boot sector, code offset 0x582, OEM-ID NTFS, sectors/cluster 8, Media descriptor 0xf8, sectors 20480001, FAT (12 bit? 16 bit?), started at 0x20000不同工具对镜像的识别结果不一样。file工具只是粗略判断更可靠的做法是先做哈希校验确保下载的镜像没有损坏然后用十六进制工具或者fdisk看一下分区结构。这里我用的命令是$ sha256sum disk.img $ fdisk -l disk.imgfdisk的输出会直接告诉你分区表类型、每个分区的起始扇区和大小。当时看到的信息大概是这个镜像很小只有1GB左右里面只有一个NTFS主分区。这种小镜像其实是非常友好的意味着你可以直接把整个镜像加载到取证工具里做全量分析不需要考虑采样问题。这里想多说一句镜像文件的大小本身就是一个线索。一个几百MB到一两GB的镜像通常考点不会太深顶多是压缩包嵌套、文件恢复和字符串隐藏但如果给你一个几十GB的大镜像往往就要考虑卷影复制、未分配空间深挖、甚至多磁盘关联了。RCTF2019这道题的大小属于“刚好够玩”的档次出题人没有在体力上为难你。确认完基本信息后我建议顺手看一眼十六进制头确认有没有隐藏的额外头或附加数据。这个镜像开头是标准的MBR引导代码后面是NTFS引导扇区没有看到明显异常但这一步不能省因为有些题会在镜像末尾追加数据块或隐藏分区。2.2 用Sleuth Kit把磁盘骨架搭出来磁盘取证的黄金工具箱是Sleuth Kit工具集。它跟普通挂载最大的区别是它直接基于镜像读取只读操作、不改变原始数据而且对未分配空间和删除文件都能看得见。我在这道题里用得最多的是这套命令$ mmls disk.img DOS Partition Table Offset Sector: 0 Units are in 1-sector blocks Slot Start End Length Description 000: Meta 0000000000 0000000000 0000000001 Primary Table (#0) 001: ------- 0000000000 0000002047 0000002048 Unallocated 002: 000:000 0000002048 0000204799 0000202752 NTFS (0x07)mmls输出的是分区布局这条命令比fdisk更专业因为它能识别出Meta区和未分配区。从结果能看出镜像只有一个NTFS分区从扇区2048开始没有隐藏的扩展分区。接着用fsstat查看文件系统细节$ fsstat -o 2048 disk.imgfsstat会列出NTFS的关键参数包括$MFT位置、扇区大小、簇大小等。这步是为了后面用fls、icat时确定偏移量。很多新手第一次用Sleuth Kit都忘了加-o指定分区偏移结果工具报错“file system not found”其实就是没告诉它数据区从哪开始。分区骨架搭起来之后关键一步是用fls递归列出文件尤其是要带上可以显示已删除文件标记的选项$ fls -r -o 2048 disk.img disk_fls.txt我强烈建议把输出重定向到文件里然后再用grep慢慢筛。因为递归列出整个NTFS分区会有大量文件系统内部的系统文件$MFT、$Secure等直接糊在屏幕上根本看不过来。当时我筛文件的第一轮命令是$ grep -i -E flag|key|secret|pass|hint|readme disk_fls.txt结果确实有不少有趣的命中但真正打开这些文件之后会发现要么是空的要么是故意放的诱饵。这类“伪线索”在取证题里太常见了出题人就是在试探你链路够不够完整你是不是看到flag字样就上头。2.3 strings和binwalk先扫一遍病灶再决定怎么开刀在深入文件系统之前我习惯先跑两遍“X光扫描”一遍strings一遍binwalk。strings的目的不是找最终答案而是快速了解这个盘里都出现过哪些字符、哪些路径、哪些可能的密码提示。这里要注意编码问题Windows系统里的字符串大部分是UTF-16LE直接用Linux的strings默认只提ASCII漏掉的东西会很多。正确姿势是$ strings -a -el disk.img strings_utf16.txt $ strings -a disk.img strings_ascii.txt然后两个文件交叉查看。为什么强调这个细节因为我在这道题里就在UTF-16编码中捞到了一条与密码提示相关的字符串而纯ASCII扫描完全没有记录。如果你一开始就跑错了编码等于蒙着眼睛找线索。binwalk则是从文件签名层面扫描整个镜像目的是看看裸的bit流里有没有“藏头露尾”的压缩包、图片或者其他嵌入文件。扫描命令$ binwalk disk.imgbinwalk的输出能告诉你这些签名大致位于哪个偏移位置。但binwalk有个缺点对于FAT/NTFS分区的镜像它会把文件系统内部正常的数据块误报成某种文件类型产生一堆无效候选。所以binwalk扫出来的东西只能当作索引真正提取还是要用更克制的方式要么针对偏移量做dd要么用foremost做定向恢复不能一上来就让foremost全盘跑否则过程漫长且结果冗余。这一步做完你对这块盘的“体检报告”就有了文件系统是NTFS存在若干已删除文件某些位置能看到压缩包和图片签名字符串扫描里发现了疑似密码提示。接下来就可以进入真正的寻宝环节了。3. 寻宝阶段数据残留与加密容器的层层剥离3.1 回收站、临时文件和“草稿”里到底有啥取证题绕不开的一个套路是出题人会把关键文件放在回收站或者故意“删除”某个文件然后指望你从未分配空间里把它挖回来。RCTF2019 disk1也不例外。在fls的列表里我注意到几个带有*号标记的文件——这是fls表示“已删除”状态的标准符号。其中有个文件名特别扎眼类似“note_old.txt”或“hint.pdf”具体名字记不太清了但它在另外一个正常文件里被引用过。我当时意识到这很可能是出题人故意留下的跳板正常文件里写了一点指向“删除文件”的提示然后删除文件里再指向下一步线索。恢复已删除文件用的是icat命令格式是$ icat -o 2048 disk.img 删除文件的inode编号 hint.txt打开一看里面其实没有直接给flag但给了一串看起来像口令的字符串还附带了一句话大意是“密码不是给压缩包用的是给容器用的”。这个信息非常关键它直接把我引向了加密容器识别那条路。除了回收站Windows系统里还有几个地方值得重点翻C:\Users\用户名\AppData\Local\Temp、Documents and Settings\*\Recent、Windows\Prefetch。Prefetch文件能反映出系统上最近运行过哪些程序这在真实取证里能帮你判断攻击者执行过什么工具在CTF里则会暗示你出题人用哪个软件生成过加密卷。我在这道题里就用fls翻了Prefetch目录确实看到了与磁盘加密工具相关的执行痕迹。这一步的实操价值在于你不是在瞎猜容器类型而是让系统自己告诉你它装过什么。3.2 未分配空间里的鱼饵加密压缩包的华丽陷阱当初次文件列表没有直接给出可读的flag时我的下一步是把注意力转向文件系统未分配空间和文件尾部残留数据。这里要区分两个概念未分配空间是文件系统层面不再属于任何文件的簇文件尾部残留则是一个文件实际大小和它占用的簇大小之间“抻出来”的多余数据。对于未分配空间的提取我用了foremost但只针对未分配区域而不是全盘。先把未分配空间抠出来$ dcalc -o 2048 disk.img 分区开始扇区 分区结束扇区 unallocated.bin这个命令实际使用时要根据前面的分区信息填参数。或者更简单一点直接用blkls提取$ blkls -o 2048 disk.img unallocated.bin然后用foremost处理这个unallocated.bin$ foremost -i unallocated.bin -o foremost_out这次foremost跑出来了几个候选文件里面有两个很有价值一个加密的zip压缩包和一个很小的图片。压缩包显然是经过精心构造的因为它在未分配空间里出现意味着它原本是存在过的后来被从目录项里删掉了但文件内容还在簇里躺着。图片则是那种尺寸可能只有几百字节的小图正常在资源管理器里根本不会有人去点开看。我当时的第一个冲动是拿那串在删除文件里找到的字符串去解这个zip结果失败了。zIP提示密码错误试了几个变体也都不行。后来我冷静下来想了一下出题人在删除文件里那句话说得清清楚楚“密码不是给压缩包用的是给容器用的”。也就是说这个zip要么是迷惑项要么它的密码线索压根在别处。这一步思维转换很重要很多人在拿到删除文件和加密压缩包之后会认定“它们必然是配对的”但取证题里的线索经常是交叉配对的A文件里的线索可能属于B文件B文件里的线索可能属于C对象。3.3 真正的入口可能是加密容器CTF磁盘取证里出现加密容器最常见的是TrueCrypt、VeraCrypt、BitLocker这几种。VeraCrypt卷的特征是文件头没有明显的可打印字符串前几百字节几乎全是随机数据但第一个扇区有一些结构化的字段比如卷格式版本号、加密算法ID、哈希算法ID这些字段位置是固定的。用十六进制编辑器翻容器文件头如果看到一组看似随机的字节里夹杂着少量非零的结构化字节就可以怀疑是加密卷。但还有一个更直觉的判断方法看文件的大小和熵值。加密卷的数据几乎全是高熵随机数据而普通有结构的文件比如磁盘镜像、压缩包、图片熵值没那么均匀。用ent工具或者010 Editor的熵值分析功能能很快把这类文件筛出来。我当时从那个小图片文件里发现末尾被追加了一大段随机数据把它单独切出来后熵值分析显示这一段接近均匀分布这就非常可疑了。再结合Prefetch里发现的加密工具执行痕迹我确定这个“图片”其实是一个伪装容器真正的体积远不止图片本身那点大小。出题人先是用一个正常的文件头作为诱饵再在后面附加了加密卷的数据块。对于装有VeraCrypt这类工具处理过的卷最常见的破解思路是如果密码文件中提到了“密码是给容器用的”那就得找到那串密码如果密码强度不高也可以尝试用hashcat暴力或字典跑。但这道题里密码的藏法不算特别刁钻——那张被追加到图片尾部的卷数据它的密码就藏在之前用strings扫描得到的某段文件内容里而且是经过一次简单的编码混淆不是直接明文。这里我想强调一个经验出题人在设置密码时通常不会把密码明文写在某个txt里等你读但也不会做很强的加密多半是base64、rot13、十六进制字符或者简单的凯撒位移。原因很简单出题人希望你能通过线索找到它而不是直接看天书。如果你在取证题里确认了一个VeraCrypt卷在Linux上挂载时命令大概是$ veracrypt -t -k --filesystemnone /path/to/container /mnt/container它会提示输入密码。如果你怀疑卷使用了keyfile还需要单独指定。挂载成功后会看到一个新的块设备再通过mount挂载其中的文件系统就能读取卷内的数据。在CTF里加密卷一旦解开里面通常就是最终的flag文件或者至少是下一阶段的线索。3.4 文件尾部残留隐藏在图片背后的第二张脸再单独说说文件尾部残留这个考点。取证题经常会故意把一个“容器”或者“压缩包”塞进一个正常图片的尾部字节这样你用普通看图软件打开图片一切正常文件大小和图片属性也能对上只有看十六进制时才会发现文件头之后的数据量明显超出了图片应有的体积。这是个非常经典的隐写思路但它出现在磁盘取证里往往不是做隐写题那种“像素里藏信息”而是直接在文件尾部拼接了其他数据块。我当时在处理那张小图片时先用identify验证了图片的宽高、色深和格式一切正常。然后用xxd翻看文件尾部发现从某个偏移开始出现了大段连续的非零数据且这部分数据完全不符合图片压缩流的特征。把这一段用dd切出来$ dd ifimage.jpg ofcontainer.bin bs1 skip文件偏移量得到的container.bin经过熵值检测后确认是一段加密数据。如果没有这个尾部拼接的视角你可能永远想不通为什么题目要在一张不起眼的图上花这么多功夫。也正是这类设计让磁盘取证题比单纯的文件隐写题更有层次感。4. 解锁与收网拿到有效载荷的完整实操记录4.1 从密码线索到容器挂载的完整链路把几条线索串一下我当时的解题链路是这样的在正常文件里发现了对“note_old”的引用原文提示该文件里有一句关于加密的口令说明从未分配空间里恢复出note_old.txt得到一段提示和一个混淆字符串对混淆字符串做解码得到一个看起来像口令的文本从一个小图片文件的尾部切出高熵随机数据确认是加密容器用VeraCrypt尝试挂载这个容器使用解码后的口令挂载成功。这里有一个小坑值得单独说VeraCrypt在Linux下的命令行如果参数顺序不对或者没有指定正确的方式会直接静默失败。比如挂载容器时应该先用--filesystemnone挂载为块设备确认密码正确后再mount文件系统如果你一步到位直接让它挂载文件系统密码错误和文件系统错误会显示不同的报错但新手很容易混淆。我当时是先切出了一个块设备$ veracrypt -t -k --filesystemnone --mount /root/container.bin /mnt/veracrypt回车后提示输入密码输入那段解码字符串后没有报错设备映射成功。接着用普通mount挂载$ mount /dev/mapper/veracrypt1 /mnt/vc_volume打开/mnt/vc_volume里面是一个干净的目录放着flag文件。到这一步题目唯一的目标已经达成。但复盘时我在想如果密码再多一重混淆或者VeraCrypt卷里再嵌套一个压缩包这题就变成了“二段跳”不过RCTF2019 disk1的整体难度控制得还算适中每个环节都有明确的线索衔接没有为了难而难。4.2 自动化脚本辅助别让重复劳动拖慢节奏在复现这道题时我写了一个简单的bash脚本用来批量跑strings、binwalk和fls的筛选。虽然单条命令手动执行也能做完但脚本能保证每次重新分析时结果一致也方便在多个相似的镜像上复用。脚本核心逻辑大概是#!/bin/bash IMG$1 OFFSET$2 echo [*] Strings ASCII strings -a $IMG $IMG.ascii.txt echo [*] Strings UTF-16LE strings -a -el $IMG $IMG.utf16.txt echo [*] fls recursive fls -r -o $OFFSET $IMG $IMG.fls.txt echo [*] binwalk binwalk $IMG $IMG.binwalk.txt脚本本身没有多高深但它能验证一个问题你是“有目的地复查”还是“每次都在重复造轮子”。取证题的实操效率很大程度上取决于你对自己工作流的熟悉程度把固定动作沉淀成脚本把精力留给真正需要推理的环节这是我从这场比赛里学到的很实际的一课。4.3 实操现场记录一次标准复现的日志参考下面是我在一次完整复现时留下的部分操作日志为了方便展示我把关键命令和输出摘出来了$ sha256sum disk.img 7f9e2d92b5e5c8d1d3a4c2e460b6a1f2b7f6e5d4c3b2a1908f7e6d5c4b3a2918f - 这种格式的哈希值通常是要在赛后第一时间确认的 $ mmls disk.img DOS Partition Table Slot Start End Length Description 000: Meta 0 0 1 Primary Table (#0) 001: ------- 0 2047 2048 Unallocated 002: 000:000 2048 2047999 2045952 NTFS (0x07) $ fls -r -o 2048 disk.img | grep -E \\*.*(txt|zip|jpg|png|pdf)shutdown输出省略但搜索结果里出现了r/r 123-128-3: document.txt d/d 456-128-4: Desktop * d/d 777-128-4: Recent r/r 888-128-3: note_old.txtnote_old.txt带有恢复符号用icat快速拉出$ icat -o 2048 disk.img 888 note_old.txt $ cat note_old.txt Its not for the zip. The key opens something bigger. HV9h3fFDsLq4...这一行HV9h3fFDsLq4...就是我说的混淆字符串。再看未分配空间提取出的加密zip用刚才的字符串直接解压失败确认是诱饵。最后从图片尾部切出的容器文件挂载后解出flag。这里特别说明一下上面这些哈希值和文件名都是示例格式并不代表RCTF2019 disk1的真实输出。不过操作流程和判断逻辑是一模一样的照着这个思路你拿到任何一道类似的取证题都能很快搭出分析框架。5. 复盘磁盘取证题的提速方法论与避坑清单5.1 工具链到底该怎么搭配才不会被工具牵着走磁盘取证题最容易把人困住的不是题目本身而是“乱用工具”。我看到过不少人拿到镜像就foremost全盘跑跑出来几百个文件然后对着输出发呆。这里的核心问题是你没有先用文件系统工具把“骨架”搭出来就急着去做“肌肉”层面的暴力扫描。我的推荐组合分四个阶段侦察阶段file、fdisk、sha256sum、xxd。目的是确认镜像格式、分区结构、完整性。文件系统分析阶段Sleuth Kit全家桶重点是mmls、fsstat、fls、icat、istat。目的是把目录树、删除文件、时间线捞出来。数据挖掘阶段strings一定要带-el、binwalk、foremost、blkls。目的是在文件系统之外寻找残留数据。深度解锁阶段veracrypt、7z、fcrackzip、john、hashcat。目的是处理加密容器和压缩包。每个阶段之间要有一个明确的“决策点”比如文件系统分析结果没有直接给flag才进入数据挖掘阶段strings扫出来的字符串无法直接解开压缩包才考虑加密容器。这个流程能最大限度减少无效操作。5.2 避坑指南实际踩过之后才懂的细节不要忽视UTF-16编码。Linux下默认的strings输出会漏掉大量Windows文件里的字符串。凡是涉及Windows系统的镜像跑完ASCII后一定要再跑一遍-el不然等于自己蒙掉一只眼睛。所有操作镜像的命令都要保持只读。能加-o ro,loop就加能用Sleuth Kit就用。取证题虽然数据都是出题人造的但养成不污染原始镜像的习惯对你做真实取证项目会有长远帮助。分清“诱饵”和“真线索”。遇到加密zip试试常用密码解不开时仔细看一下之前找到的提示特别是带引号、带“not for”这类否定表述的句子。出题人不会无缘无故写一句否定句。文件尾部残留要靠熵值或文件大小来识别。图片正常显示不代表图片没有问题。养成用xxd翻文件尾部的习惯很多嵌套数据就藏在最后几百字节到几千字节里。恢复出的文件可能带损坏。foremost恢复的压缩包可能因为文件碎片化而缺失部分数据这时可以用binwalk再看一下或者尝试修复zip头的CRC。别看小问题它可能卡住你半小时。时间线分析比想象中有用。用fls -m或者istat查看关键文件的修改、访问、创建时间有时能帮你判断哪些文件是出题人后造出来的哪些是系统原始产物。伪造的线索文件通常在时间戳上会暴露。5.3 遇到新题时的标准思考顺序做磁盘取证题如果不知道从哪里下手我建议用下面这个顺序问自己这个镜像是什么文件系统分区表有没有异常有没有已删除但内容还在的文件有没有需要从未分配空间才能恢复的内容有没有可疑的正常文件图片、文档、压缩包在尾部拼接了额外数据有没有加密容器是否有对应密码线索所有线索能否串成一个完整的“被删除-被隐藏-被加密”的链路RCTF2019 disk1实际上是这六问的完整演练。它没有考多冷门的知识也没有要求你背某个工具的参数而是把上面每一步都扎实地走了一遍。这恰恰是磁盘取证题最有价值的地方它训练的不是某个单一技巧而是整体分析思路的完整度。以后再遇到类似题目我的第一建议永远是先慢下来把镜像的“地图”看清楚再决定往哪里挖。挖的时候带着假设去验证而不是开着foremost碰运气。磁盘上任何你觉得“意外”的数据在CTF里都大概率是出题人精心安排的路线牌。最后说点实际感受。RCTF2019 disk1这题做完之后我对“镜像”这两个字的认知彻底变了。以前觉得磁盘镜像就是文件系统加文件集合现在会下意识去关注那些不属于任何文件的字节那些被标记删除的条目以及看似正常文件里“多余”的尾巴。这个思维转变比多会几个工具的收益要大得多。你在实战中也会慢慢体会到取证的乐趣不在于跑通一条标准流程而在于能比特种数据多想一步多看到一层。