Writeup 4 Bugku - USB 流量截取

Writeup 4 Bugku - USB 流量截取

面向 CTF 初学者,手把手讲解 USB 键盘流量的分析思路、提取方法与 Shift 键的解码原理。

一、题目信息

  • 题目名称:USB 流量截取
  • 题目类型:MISC(杂项)
  • 题目附件 :flag.pcap
  • 最终 Flag :flag{pr355_0nwards_a2fee6e0}

二、解题思路概述

这是一道非常经典的 USB 键盘流量分析 题。

  1. 用 Wireshark 打开 flag.pcap,确认是 USB 协议。
  2. 判断数据类型(键盘流量通常是 8 字节,鼠标是 4 字节)。
  3. 用 tshark 提取按键数据。
  4. 编写 Python 脚本,根据 USB HID 键盘映射表还原按键。
  5. 重点处理 Shift 键(修饰键),还原大写字母和特殊符号。

三、详细解题步骤

1. 确认流量类型

用 Wireshark 打开 flag.pcap,点击任意一个 USB 数据包,观察下方详情面板:

  • 数据包长度(Length)为 35 字节 ,其中 Leftover Capture Data 的长度是 8 字节 (例如 0000000000000000)。
  • 协议为 USB,Info 显示 URB_INTERRUPT in,表示设备(键盘)发送数据给主机。

结论:这是标准 USB 键盘的击键数据包,每包 8 字节。这 8 个字节的含义是:

字节序号 含义
第 1 字节 (Index 0) 修饰键(如 Shift、Ctrl、Alt)
第 2 字节 (Index 1) 保留位
第 3 字节 (Index 2) 实际按下的键码
第 4-8 字节 (Index 3-7) 保留位 / 其他按键

(注意:在程序里字符串索引从 0 开始,第 3 个字节对应的是索引 [4:6])

2. 提取数据

在 Linux 终端中,使用 tshark 命令提取 usb.capdata 字段:

bash 复制代码
tshark -r flag.pcap -T fields -e usb.capdata | sed '/^\s*$/d' > usbdata.txt

命令解释:

  • -r flag.pcap:读取流量包。
  • -T fields -e usb.capdata:只提取 usb.capdata 字段(即 HID 数据)。
  • sed '/^\s*$/d':删除空行,避免解码脚本报错。
  • > usbdata.txt:将提取结果保存到文件。

提取后的 usbdata.txt 内容:

bash 复制代码
root@Rambo:/USB 流量截取# cat usbdata.txt
0000090000000000
0000000000000000
00000f0000000000
0000000000000000
0000040000000000
0000000000000000
00000a0000000000
0000000000000000
2000000000000000
20002f0000000000
2000000000000000
0000000000000000
0000130000000000
0000000000000000
0000150000000000
0000000000000000
0000200000000000
0000000000000000
0000220000000000
0000000000000000
0000220000000000
0000000000000000
2000000000000000
20002d0000000000
2000000000000000
0000000000000000
0000270000000000
0000000000000000
0000110000000000
0000000000000000
00001a0000000000
0000000000000000
0000040000000000
0000000000000000
0000150000000000
0000000000000000
0000070000000000
0000000000000000
0000160000000000
0000000000000000
2000000000000000
20002d0000000000
2000000000000000
0000000000000000
0000040000000000
0000000000000000
00001f0000000000
0000000000000000
0000090000000000
0000000000000000
0000080000000000
0000000000000000
0000080000000000
0000000000000000
0000230000000000
0000000000000000
0000080000000000
0000000000000000
0000270000000000
0000000000000000
2000000000000000
2000300000000000
2000000000000000
0000000000000000
0100000000000000
0100060000000000
root@Rambo:/USB 流量截取#

3. 分析数据规律

观察提取出来的数据,可以发现两种不同类型的行:

  1. 0000xx0000000000:第 1 字节为 00,第 3 字节为实际键码。
  2. 2000xx0000000000:第 1 字节为 20,表示按下了 Shift 键。

在 USB HID 协议中,修饰键的位掩码如下:

  • 0x02:Left Shift
  • 0x20:Right Shift
  • 所以第 1 字节为 20 时,代表右侧 Shift 键被按下。

4. Python 解码脚本

由于题目中包含了大量的 Shift 组合键(例如打出 {、}、_),我们的脚本需要同时查两张表:普通映射表 和 Shift 映射表。

新建 solve.py,写入以下代码:

python 复制代码
# 基础键盘映射表
mapping = {
    '04': 'a', '05': 'b', '06': 'c', '07': 'd', '08': 'e', '09': 'f', '0a': 'g', '0b': 'h',
    '0c': 'i', '0d': 'j', '0e': 'k', '0f': 'l', '10': 'm', '11': 'n', '12': 'o', '13': 'p',
    '14': 'q', '15': 'r', '16': 's', '17': 't', '18': 'u', '19': 'v', '1a': 'w', '1b': 'x',
    '1c': 'y', '1d': 'z', '1e': '1', '1f': '2', '20': '3', '21': '4', '22': '5', '23': '6',
    '24': '7', '25': '8', '26': '9', '27': '0', '2c': ' ', '2d': '-', '2e': '=', '2f': '[',
    '30': ']', '31': '\\', '33': ';', '34': "'", '36': ',', '37': '.', '38': '/'
}

# 按住 Shift 时的键盘映射表(大写和符号)
shift_mapping = {
    '1e': '!', '1f': '@', '20': '#', '21': '$', '22': '%', '23': '^', '24': '&', '25': '*',
    '26': '(', '27': ')', '2d': '_', '2e': '+', '2f': '{', '30': '}', '31': '|', '33': ':',
    '34': '"', '36': '<', '37': '>', '38': '?'
}

result = ""
with open('usbdata.txt', 'r') as f:
    for line in f:
        line = line.strip()
        if not line or len(line) < 16:
            continue

        # 1. 检查修饰键(第一个字节)
        modifier = line[0:2].lower()
        is_shift = False
        if modifier != '00':
            # 如果修饰键包含 20、02 或 22,说明按住了 Shift
            # 你数据里是 20
            if int(modifier, 16) & 0x22:
                is_shift = True

        # 2. 检查键码(第三个字节)
        key_code = line[4:6].lower()

        # 如果是 00(松开按键)或者是 01(Ctrl键等),跳过
        if key_code == '00' or key_code == '01':
            continue

        # 3. 查表逻辑
        if is_shift and key_code in shift_mapping:
            result += shift_mapping[key_code]
        elif is_shift and key_code in mapping:
            # 按着 Shift 打字母,转大写
            result += mapping[key_code].upper()
        elif key_code in mapping:
            result += mapping[key_code]

print("解码结果:")
print(result)

脚本运行结果:

bash 复制代码
"H:\USB 流量截取\.venv\Scripts\python.exe" \USB 流量截取\solve.py
解码结果:
flag{pr355_0nwards_a2fee6e0}c

进程已结束,退出代码为 0

分析:

  1. flag{...} 是标准的 Flag 格式。
  2. 最后多出来的 c,可能是因为出题人在敲完 } 之后,不小心误触了 c 键然后敲了回车,这段数据被录制进了流量包里。提交时不包含大括号外面的 c。
  3. 最终 Flag 是:flag{pr355_0nwards_a2fee6e0}。

四、知识点总结

知识点 说明
USB 键盘流量特征 每包 8 字节,第 3 字节是键码,第 1 字节是修饰键。
Shift 键的处理 修饰键 0x02 或 0x20 表示 Shift,此时需要查 Shift 映射表转大写或特殊符号。
tshark 提数据 -e usb.capdata 提取击键数据,sed 删空行。
多余字符排除 Flag 通常被 {} 包裹,外部字符是干扰或误触,提交时须去除。

五、小白避坑指南

  1. 分不清鼠标和键盘:如果提取的数据长度是 4 字节,那是鼠标;如果是 8 字节,就是键盘。
  2. 符号解错 :很多新手只写一张小写字母表,遇到 { 或 _ 就全乱了。一定要处理 Shift 键。
  3. 不小心删错数据 :有些脚本会直接 replace('00', ''),这会把数据破坏。必须根据字节位置(切片)来精准提取。

Rambo

2026年11月8日

🎉🎉🎉

相关推荐
hengdonghui9 小时前
Writeup 4 GFCTF 2021 Baby_Web
web·ctf·变量覆盖·路径穿越·php 代码审计
kingx311 小时前
Bugku 逆向 MatrixHunter:黑盒 oracle + GF(2) 消元出唯一解
网络安全·矩阵·逆向·ctf
kingx32 天前
Bugku 3122 谷歌小恐龙题解:纯离线逆向 Vault 的 AES-GCM 加密资源(附 Python 解密器)
java·逆向·ctf·bugku·aes-gcm
kingx32 天前
Bugku 326 Baby-heap1 题解:glibc 2.23 堆溢出到 FSOP 劫持 _IO_list_all(附完整 exp)
pwn·ctf·glibc·堆溢出·fsop
hengdonghui3 天前
Writeup 4 0xGame 2021 · command (WEB)
web·ctf·rce(远程代码执行)
0xBADCODE3 天前
2026 强网杯 Web 题 cwp · 全链路复盘
前端·安全·web安全·网络安全·系统安全·ctf
kingx36 天前
Bugku WEB 题解总集:文件包含 / SQLi / 反序列化 / SSTI / 反爬 / 组件漏洞
web安全·网络安全·ctf·bugku
kingx36 天前
Bugku Reverse 与 Android 题解总集:APK / native / PE / IPA
android·逆向·ctf·bugku
笔绘丹心7 天前
测试工程师的 BLE 抓包实战:从开关到 Wireshark 逐帧解读(红米/小米避坑版)
wireshark·抓包·ble