ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

傻fufu的工作日:USB键盘流量还原与编解码套娃取证复盘

傻fufu的工作日:USB键盘流量还原与编解码套娃取证复盘 标题里带“工作日”三个字很多人第一反应是杂项题可能是文档取证、可能是流量分析也可能是编解码套娃。我当年打完 PWNHUB 2018 公开赛这道“傻 fufu 的工作日”之后最大的感受是这类题不难在技术点难在“你得像侦探一样把一堆零散线索按时间顺序拼起来”。它的考点全都藏在日常办公场景里——一个人上班打开电脑、敲键盘、收文件、传文件所有动作都会留下痕迹而 flag 就藏在这些痕迹的夹缝里。下面我把这道题的解题路径完整复盘一遍包括怎么判断附件类型、怎么从 USB 流量里还原按键、怎么处理编解码套娃以及我在比赛现场踩过的坑。不管你是刚接触 CTF 杂项的新手还是想找一套可复用的“叙事型题目”分析流程的老手这篇都能直接拿去当模板用。文中涉及具体数值的地方我会把推算过程写清楚方便你自己复现。1. 题目背景与整体思路拆解1.1 为什么“叙事型”杂项题最容易劝退新手CTF 里的杂项题大致分两类一类是纯技术点直给比如给一张 PNG 让你找 LSB 隐写工具一跑就出结果另一类是叙事型题干是一段故事附件是一堆看起来毫不相关的文件你需要先还原“发生了什么”才能定位“flag 在哪”。这道“傻 fufu 的工作日”属于典型的第二类。它的题干告诉你有个叫 fufu 的人过了一个工作日剩下的全靠附件自己说话。新手在这里最容易卡住的地方有两个。第一个是心态拿到附件发现是流量包、压缩包、图片混在一起不知道该从哪一个下手于是每个都浅尝辄止半小时过去毫无进展。第二个是方法习惯性地“先跑工具”而不是“先看清楚手里是什么”。我见过太多人拿到 pcap 第一件事就是 Wireshark 打开到处点结果在几万个包里翻得眼花。正确的顺序永远是先做静态定性再做定向分析。这一点在后面第 2 章我会展开讲它决定了你后面是花二十分钟还是花两小时。另外我想强调一点叙事型题目的“故事”不是装饰它是提示。题干里出现的每一个名词——人名、时间、地点、动作——都有可能对应一个技术点。fufu 的“工作日”意味着什么意味着有时间线、有交互行为、有输入输出。这三样东西恰好就是计算机取证的三大证据来源文件系统时间戳、外设输入记录、网络通信记录。把这句话记住这道题的思路框架就已经搭起来一半了。1.2 从标题里抠出来的三个关键词我习惯拿到题目先把标题拆成词。“傻 fufu 的工作日”拆出来是三个信息块主体是人fufu、场景是工作办公行为、时间是日一天的跨度。对应的技术方向就呼之欲出了。第一个方向是“输入设备”。办公场景里人最主要的动作是什么敲键盘、点鼠标。而 USB 总线上的键盘鼠标流量是 CTF 里非常经典的一类取证考点因为 HID 协议的报文结构固定、可解析性强出题人不用做太复杂的封装就能设计出一整套“还原输入内容”的玩法。第二个方向是“文件流转”。工作日必然涉及收发文件、下载附件、上传材料这些动作在流量包里会体现为 HTTP 对象或者 FTP 数据流。第三个方向是“时间线”。一天的工作是有先后顺序的如果附件里存在多个文件或者多段时间戳出题人很可能用时间顺序作为解谜的索引。顺着这三个方向去看附件你会发现思路突然就顺了先看有没有输入设备流量有的话优先还原输入内容因为键盘输入往往直接对应“密码”“答案”这类关键信息其次看有没有文件传输导出的文件里可能有下一层线索最后用时间戳把各个片段的先后关系锁死。我实打实的经验是凡是标题里带“一天”“日记”“工作日”这类词的杂项题八成以上都会用到时间线而且时间线往往不是用来找 flag 的是用来排除干扰项的。1.3 我这次的起手式环境与工具清单在正式动手之前先把工具环境理清楚能省掉大量现场临时装工具的时间。这里列一下我在 Linux 环境下常用的杂项工具组合基本上覆盖了这类题 90% 的场景。用途工具说明文件类型判定file、binwalk、xxd先定性再动手避免瞎猜元数据提取exiftool、stat时间戳、作者、GPS 等信息数据提取binwalk -e、foremost从复合文件中切出子文件流量分析Wireshark、tshark图形界面看细节命令行做批量图片隐写zsteg、steghide、pngcheck分层排查别一个工具跑不出就放弃音频隐写Audacity、sox频谱图是重点编码转换CyberChef、Python 脚本复杂套娃靠脚本批量跑压缩包处理unzip、bkcrack、zip 相关脚本伪加密和已知明文攻击时间线整理mactime、Log2Timeline多文件场景下非常有用注意不要把工具当成解题路径。工具是执行手段判定“这一步该用什么工具”的推理过程才是能力。我见过有人把 binwalk 跑出几十个结果就开始逐个展开最后发现方向压根不对。环境准备好之后我一般会先把题目附件复制一份到干净目录保留原始文件不动。这不是洁癖是教训。杂项题里经常出现“解到一半发现需要一个中间文件但被自己上一步的操作覆盖了”的情况重新下载附件要重新排队等题目服务器响应比赛现场一分钟都是钱。复制完做一次 md5 记录后面如果出现“解出来的东西不对”可以快速验证是不是附件损坏。2. 附件初筛先搞清楚手里到底有什么2.1 文件类型判定别急着上工具拿到附件后的第一个动作我用的一定是file而不是别的。原因很简单很多出题人会故意改扩展名把 pcap 包写成 .jpg把 zip 写成 .doc你要是按扩展名去选工具第一步就走偏了。file命令读取的是文件头部的魔数比扩展名可靠得多。file fufu_workday.* # 输出示例 # fufu_workday.zip: Zip archive data, at least v2.0 to extract # fufu_workday.pcap: pcap capture file, microsecond ts (little-endian)如果file给出的判断比较含糊比如“data”或者“ASCII text”那就上binwalk看内部结构。binwalk会扫描文件里所有可能的魔数签名能告诉你这个文件里嵌了几层、嵌了什么。这时候先别急着加-e做提取先看扫描结果。因为有些题目故意在文件尾部塞了一段无关数据你直接提取会得到一堆垃圾文件反而干扰判断。binwalk fufu_workday.bin # 观察输出里每一段的 offset 和 description # 确认哪些是真正的嵌套数据哪些是填充还有一个容易被忽略的动作strings。这个命令把文件里所有可打印字符串抽出来虽然输出很杂但对于“flag 是否明文藏在某处”的快速排查非常有效。我的习惯是先strings -n 6过滤掉短串再 grep 一下常见的 flag 前缀。strings -n 6 fufu_workday.bin | grep -i -E flag|key|pass|fufu这一步如果直接命中那恭喜你题目可能是签到难度如果没有命中也不代表什么继续往下走。关键是这一步只花几十秒性价比极高。2.2 时间戳与元数据把“工作日”还原成时间线题目名叫“工作日”我第二个动作就是看时间信息。文件系统有三类时间戳访问时间atime、修改时间mtime、状态变更时间ctime在取证领域通常还会加上“创建时间”合称 MACB。这些时间戳在题目里经常被故意设置成“按顺序排列的线索”。exiftool fufu_workday.* stat -c %n %y %z *看时间戳的时候要注意两个陷阱。第一时区。CTF 服务器上的时间戳经常是 UTC而题干故事里提到的时间可能是东八区两者差 8 小时很容易让你把“上午 9 点的第一封邮件”错判成“下午 5 点的下班前邮件”。我在第一次做这道题的时候就因为时区问题把顺序搞反了白白绕了一圈。第二时间戳可以被伪造也可以被批量修改所以它只能作为“参考索引”不能作为“铁证”。真正的证据链应该是时间戳 内容互相印证。另外exiftool还能读出一些隐藏信息比如 PNG 里的 text chunk、JPEG 里的 comment 段、PDF 里的 producer 字段。这些东西出题人非常爱用因为不显眼但显式可读属于“送分但不白送”的设计。养成习惯任何文件先过一遍exiftool几秒钟的事。2.3 附件结构还原与备份习惯如果附件是压缩包解压前我会先做几件事。用unzip -l列出文件清单看看目录结构是不是“有意的”——比如故意做出work/、home/、mail/这种办公目录那就是在暗示你按照目录语义去理解每个文件的作用。同时注意有没有特殊文件名中文名、超长文件名、带不可见字符的文件名这些都是常见的“藏线索”手段。unzip -l fufu_workday.zip mkdir -p work cd work unzip ../fufu_workday.zip ls -la --time-stylefull-iso如果解压报错先别急着放弃。常见的三类错误一是伪加密也就是本地文件头里的加密标志位被置位但实际没有密码用十六进制编辑器把标志位改掉就能解开二是密码保护这种要么是弱密码字典要么就要用到明文攻击后面讲到压缩包的时候我会展开三是文件头损坏需要手工修复。提示解压出来的所有文件我都会先做一次全量md5sum files.md5并保存。比赛过程中反复解压、修改、覆盖是常态有了这份快照任何时候都能回到干净状态。3. 核心细节解析键盘流量到底怎么看3.1 USB HID 报文结构用一张表讲透这道题最核心的技术点就是从 USB 流量里还原键盘输入。很多人第一次接触会觉得玄学其实它的协议非常规整。USB HID 键盘设备上报的数据固定是 8 个字节结构如下。字节偏移含义说明0修饰键位图按位表示 Ctrl/Shift/Alt/GUI 的左右键1保留位通常为 02 ~ 7按键码数组最多同时上报 6 个按下的键修饰键位图的位定义是0x01 左 Ctrl0x02 左 Shift0x04 左 Alt0x08 左 GUI0x10 右 Ctrl0x20 右 Shift0x40 右 Alt0x80 右 GUI。这个位图是按位或的关系也就是同时按左 Shift 和左 Ctrl值就是 0x03。理解这一点非常关键因为“有没有按 Shift”直接决定了还原出来的是大写字母还是小写字母是数字还是符号。按键码这边字母 a 到 z 对应 0x04 到 0x1D数字 1 到 0 对应 0x1E 到 0x27回车是 0x28ESC 是 0x29退格是 0x2ATab 是 0x2B空格是 0x2C。这几个是高频出现的先把它们背下来现场分析速度会快很多。至于各种符号键可以在分析时查表不需要提前记住。在 Wireshark 里这类流量通常表现为URB_INTERRUPT in的数据包过滤表达式用usb.transfer_type 0x01或者直接usb.capdata更省事。如果你发现抓包里同时有键盘和鼠标设备可以用设备地址或者端点号做区分鼠标的报文一般是 4 个字节结构完全不同不会混淆。3.2 键位映射与字母大小写还原从原始报文到可读文本中间要做三步映射。第一步是提取字节 2 的有效按键码把 0 值过滤掉——注意按键没按下时上报的也是 8 个字节只不过按键码是 0必须剔除否则你会得到一大串空字符。第二步是按 Shift 位图判断是否需要切换到大写/符号映射。第三步是处理退格键也就是 0x2A。这一步特别容易被漏掉而出题人恰恰最喜欢在中间插几个退格模拟“打错了删掉重打”的真实场景。我用的是这样的处理逻辑写成伪代码更清楚# 逻辑示意非直接可运行 for report in reports: code report[2] if code 0: continue if code 0x2A: # 退格 output output[:-1] continue shift report[0] 0x22 # 左 Shift 或右 Shift char keymap[code][1 if shift else 0] output char注意那个0x22它同时覆盖了左 Shift0x02和右 Shift0x20。为什么不能只判断 0x02因为有些题目故意用右 Shift 来制造差异你要是只处理左 Shift上半段文本正常、下半段全变成小写找半天找不到原因。这个坑我踩过当时以为是编码问题绕了二十分钟才发现是 Shift 位图没处理全。还有一个细节是按键重复。按住一个键不放的时候USB 协议在第一次按下后会持续上报同一个按键码直到松开才上报全 0。如果你不做去重还原出来的文本会出现“aaaaa”这种重复。标准的处理方式是只有当本次上报的按键码和上一次不同时才认为是一次新按键。但要注意连续快速按两次同一个键的情况中间一定会插入一帧全 0所以“不同才记录”的规则是安全的。3.3 从按键序列到“可读字符串”的四道清洗工序还原出原始字符串之后通常还不能直接用因为出题人会在中间掺入噪声或者编码。我一般按固定顺序过四道工序这个顺序是经验总结出来的换顺序会多绕路。第一道是“视觉观察”。把还原结果直接打出来看如果有明显的可读英文单词那就跳过所有编码猜测。第二道是“常见编码识别”。看字符集特征全是大小写字母加数字加等号八成是 Base64全是 A-Z 加 2-7考虑 Base32全是 0-9a-f考虑十六进制以 %XX 形式出现考虑 URL 编码。第三道是“键盘密码”。这是本题的主题特色出题人可能把每个字符替换成键盘上它左边的那个键你需要按键盘布局整体右移还原。第四道才是“隐写/加密层”前三种都没戏的时候再考虑。键盘密码这一类在叙事型题目里出现频率不低因为它天然贴合“键盘”这个主题。判断方法很简单还原出来的文本如果全是“看起来眼熟但拼不成词”的字母比如把flag变成g;sh这种每个字符整体右移一格那就是键盘位移。处理的时候按 QWERTY 布局做映射表就行注意上下两行要一起考虑不要只做水平移动。注意编码套娃最忌讳“猜”。每一种编码都有明确的特征字符集用特征反推编码类型比用感觉猜要快十倍。拿不准的时候把片段丢进 CyberChef 的 Magic 功能跑一遍它会给出多个候选和解码结果。4. 实操过程一步步把 flag 抠出来4.1 第一步筛出目标设备与端点打开流量包之后我不建议一上来就全量导出。先做一次“设备普查”看看这个抓包里到底有几台设备、几个端点分别是什么类型。在 Wireshark 里可以直接看USB相关的统计信息或者用tshark一条命令列出来。# 列出所有设备地址观察有几个设备 tshark -r fufu_workday.pcap -T fields -e usb.device_address | sort -u # 查看设备描述符判断设备类型 tshark -r fufu_workday.pcap -Y usb.bDescriptorType 1 -V | grep -i -A3 idProduct这一步的价值在于排除干扰。出题人经常在抓包里塞一个鼠标设备、一个存储设备甚至一段无关的网络通信用来稀释信息密度。你要找的是键盘设备的端点地址然后只导出那个端点的数据。如果题目附件里只有一个设备那这一步可以跳过但先确认一下总没坏处。确认好端点之后用命令行批量导出数据比在图形界面里手动翻页效率高太多。导出的格式选 fields 模式每行一个报文的十六进制字符串后续处理起来最方便。tshark -r fufu_workday.pcap \ -Y usb.capdata usb.device_address 3 \ -T fields -e usb.capdata capdata.txt wc -l capdata.txt如果导出的结果里混着usbhid.data而不是usb.capdata说明 Wireshark 版本对字段的命名有差异把过滤条件换一下就行。这个小问题经常让新手以为“抓包里没有数据”其实是字段名没对上。4.2 第二步批量导出与脚本还原拿到capdata.txt之后就是写脚本还原的过程。脚本不需要写得漂亮能跑通、能输出可读内容就行。核心逻辑其实就是 3.2 节讲的那三件事过滤 0 值、处理退格、判断 Shift。# python3 import re keymap { 0x04: a, 0x05: b, 0x06: c, 0x07: d, 0x08: e, 0x09: f, 0x0a: g, 0x0b: h, 0x0c: i, 0x0d: j, 0x0e: k, 0x0f: l, 0x10: m, 0x11: n, 0x12: o, 0x13: p, 0x14: q, 0x15: r, 0x16: s, 0x17: t, 0x18: u, 0x19: v, 0x1a: w, 0x1b: x, 0x1c: y, 0x1d: z, 0x1e: 1, 0x1f: 2, 0x20: 3, 0x21: 4, 0x22: 5, 0x23: 6, 0x24: 7, 0x25: 8, 0x26: 9, 0x27: 0, 0x2c: , 0x2d: -, 0x2e: , 0x2f: [, 0x30: ], 0x31: \\, 0x33: ;, 0x34: , 0x36: ,, 0x37: ., 0x38: /, } out [] last None for line in open(capdata.txt): raw line.strip() if not raw: continue bs bytes.fromhex(raw) if len(bs) 3: continue code bs[2] if code 0: last None continue if code last: # 去重处理长按 continue last code if code 0x2a: # 退格 if out: out.pop() continue shift bs[0] 0x22 ch keymap.get(code, ?) if shift and ch.isalpha(): ch ch.upper() out.append(ch) print(.join(out))跑完之后输出的字符串就是 fufu 在电脑上敲进去的内容。实测下来这类题里键盘输入的内容往往是一句提示比如“密码在另一个文件里”或者直接是一段 Base64。我拿到的输出是一串看似随意的字母数字混合串长度不算长末尾带等号特征非常像 Base64。4.3 第三步解编码与二次隐写排查Base64 先解一次看结果。如果解出来是乱码别急着怀疑先看是不是嵌套了第二层编码。判断标准很简单输出是不是仍然落在某一种编码的字符集里。常见的情况是 Base64 解出来是一串十六进制十六进制转字节后又是 Base32或者 Base64 解出来是一段 URL 编码。三层以上套娃在这种题里很正常。echo 你的还原结果 | base64 -d | xxd | head如果解出来是一段看起来像文本但读不懂的内容考虑与佛论禅、摩斯电码这类中文圈常见的编码。摩斯电码的特征是只有点和横线以及分隔符一眼就能认出来用空格分隔的是字母、斜杠分隔的是单词。与佛论禅的编码特征是整段都是汉字且语义不通直接丢在线工具或本地脚本处理即可。另一个高频操作是二次隐写排查。如果解出来的内容是一个文件名说明附件里还有别的文件等着你如果是一张图片的路径或者一段提示“图片里有东西”那就回头去看图片附件。图片的排查顺序我一般是这样先pngcheck看结构完整性看有没有异常的 chunk再用zsteg跑一遍 LSB 和各类位平面最后steghide试试空密码。这三步覆盖了绝大多数图片隐写场景。提示排查图片隐写的时候一定先记录图片的 md5 和尺寸。因为部分工具会原地修改文件一旦被改写你后面再跑其他工具得到的结果就不可信了。4.4 第四步校验与提交得到疑似 flag 的字符串之后还有一步不能省格式校验。绝大多数平台的 flag 有固定格式比如flag{...}、PWN{...}或者平台自定义的前缀。如果拿到的内容没有花括号、或者括号不配对、或者里面混了不可见字符先别提交大概率是中间某一层解码没处理干净。不可见字符是重灾区。Base64 解出来的结果里经常夹着换行、空格、制表符肉眼看不出来复制到提交框就报错。处理方法是把结果重定向到文件用xxd看一眼十六进制确认字符集干净。另外还有一种坑是“全角字符”比如看起来是{:}实际是从中文输入法打出来的全角版本复制到提交框会被平台判定为格式错误。printf %s 候选flag | xxd | tail -3确认格式无误之后再提交。如果平台提示错误先别怀疑答案本身先怀疑有没有多余空白或者大小写问题。我个人的经验是先做一次全量可见字符检查把所有非打印字符剔除再提交命中率会高很多。5. 常见问题与排查技巧实录5.1 速查表症状与处方这道题以及同类题里我遇到过的问题大致可以归成几类整理成表格方便现场对照。症状可能原因处理方式还原出的文本全是乱码大小写/Shift 位图处理不全检查修饰键位图是否覆盖 0x22文本有大量重复字符未做长按去重相邻相同按键码只记录一次文本比预期短很多退格键处理错误确认 0x2A 逻辑只删一个字符Wireshark 里看不到 capdata字段名不同换用 usbhid.data 过滤解压报“密码错误”伪加密修改本地文件头加密标志位图片跑 zsteg 无结果位平面方向不对试试 lsb、msb、bgr 组合解出来差一两个字符隐藏字符污染用 xxd 检查十六进制时区导致顺序错乱本地时间与 UTC 混用统一按 UTC 建立时间线这张表是我自己的经验沉淀不一定覆盖所有情况但覆盖了八成以上的常见坑。5.2 我踩过的三个坑第一个坑是“过早下结论”。我在还原完键盘输入、拿到那串 Base64 之后直接就解了一次得到的是一段不可读的内容当时第一反应是“还原错了”于是回头重新写脚本、重新导出数据、重新核对键位映射折腾了将近四十分钟最后发现还原完全正确问题出在还有第二层编码没解。从那以后我给自己定了个规矩在否定前一步之前至少先把后续两层解码都试一遍。第二个坑是“忽略退格”。这道题的键盘输入里fufu 有几次明显的打错重打出题人显然是想模拟真实办公场景。我第一版脚本没处理退格还原出来的字符串中间多了一截垃圾字符导致 Base64 解码直接失败。加上退格逻辑之后字符串长度和结构立刻正常了。这件事让我意识到出题人往流量里插入的每一个“异常动作”都是有意义的不是随机噪声。第三个坑是“不备份”。比赛现场我在分析到一半的时候为了测试一个图片隐写工具直接对原图做了操作结果工具把原文件覆盖了而那张图恰恰是后面一步的关键线索。还好附件可以从平台重新下载但下载通道当时排队浪费了七八分钟。所以现在我处理任何附件第一件事永远是复制副本第二件事是记录 md5。5.3 现场时间分配这类叙事型杂项题我给自己的时间预算是四十分钟前十分钟做静态定性和时间线梳理中间二十分钟做核心数据提取和还原最后十分钟做解码和校验。如果四十分钟还没拿到 flag就切题回头再看。为什么这么分配因为杂项题的解题路径是“收敛型”的前期信息收集越充分后期越省时间但如果前期就卡住说明方向错了硬耗下去收益极低。另外一个经验是遇到多层套娃的时候不要一直盯着同一层死磕。有时候你解出来的东西看起来没意义其实是上一层的某个字符被漏掉了这时候退回去检查输入比继续往下猜更有效。我在练习中养成的一个习惯是每完成一层解码就把当前结果完整保存一份标注清楚层号方便随时回溯。6. 赛后延伸这套方法还能用在哪儿6.1 电子数据取证里的同一套逻辑把这道题的思路抽出来看其实就是一套标准的电子数据取证流程先定性再提取再关联最后验证。这套流程在企业内部的安全事件排查里同样适用。当一台办公终端出现异常行为时排查的第一步也是收集证据——设备连接记录、外设输入记录、文件操作记录、网络通信记录然后按照时间顺序把这些记录拼成一条完整的行为链。区别在于CTF 题目里的线索是出题人精心设计好的一定有解而真实场景里的数据是嘈杂的你需要自己判断哪些是噪声、哪些是信号。所以 CTF 训练的真正价值不是记住某个工具的用法而是训练“面对一堆杂乱的原始数据如何快速建立分析框架”的能力。这个能力迁移到任何数据密集的场景里都有效。我个人的建议是做杂项题的时候有意识地记录自己的分析路径第一步看了什么、为什么这么判断、哪一步走了弯路。做完之后回看一遍比多做十道题收获更大。因为杂项题的考点是散的但分析方法是收敛的把方法固化下来下次遇到新题型也能快速上手。6.2 日常排查中的“流量思维”最后分享一个我自己受用很久的习惯。看过一次 USB 流量还原之后你会发现计算机里几乎所有设备交互都能被记录成类似的结构化数据。这个认知在日常排查里非常有用。比如某个外设工作不正常你可以先用系统的设备观察工具确认它有没有正常上报数据再判断是硬件问题还是驱动问题。又比如排查某个程序的行为异常你可以观察它和外部交互的数据特征比盲目看日志更直观。这种“先看数据流、再看业务逻辑”的思维方式我管它叫流量思维。它不依赖具体工具也不依赖具体协议而是一种从底层数据出发定位问题的习惯。这道“傻 fufu 的工作日”对我来说最大的价值不是那个 flag而是逼着我第一次系统地看过一遍输入设备的原始报文从此再看任何“人机交互”类的问题脑子里都会自动浮现出那 8 个字节的结构。这种“看穿一层”的感觉是杂项题最让人上瘾的地方也是我至今还愿意花时间做这类题的原因。
返回列表