Writeup 4 Bugku - USB 流量截取
面向 CTF 初学者,手把手讲解 USB 键盘流量的分析思路、提取方法与 Shift 键的解码原理。
一、题目信息
- 题目名称:USB 流量截取
- 题目类型:MISC(杂项)
- 题目附件 :
flag.pcap - 最终 Flag :
flag{pr355_0nwards_a2fee6e0}
二、解题思路概述
这是一道非常经典的 USB 键盘流量分析 题。
- 用 Wireshark 打开
flag.pcap,确认是 USB 协议。 - 判断数据类型(键盘流量通常是 8 字节,鼠标是 4 字节)。
- 用
tshark提取按键数据。 - 编写 Python 脚本,根据 USB HID 键盘映射表还原按键。
- 重点处理 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. 分析数据规律
观察提取出来的数据,可以发现两种不同类型的行:
0000xx0000000000:第 1 字节为00,第 3 字节为实际键码。2000xx0000000000:第 1 字节为20,表示按下了 Shift 键。
在 USB HID 协议中,修饰键的位掩码如下:
0x02:Left Shift0x20: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
分析:
flag{...}是标准的 Flag 格式。- 最后多出来的
c,可能是因为出题人在敲完}之后,不小心误触了c键然后敲了回车,这段数据被录制进了流量包里。提交时不包含大括号外面的c。 - 最终 Flag 是:
flag{pr355_0nwards_a2fee6e0}。
四、知识点总结
| 知识点 | 说明 |
|---|---|
| USB 键盘流量特征 | 每包 8 字节,第 3 字节是键码,第 1 字节是修饰键。 |
| Shift 键的处理 | 修饰键 0x02 或 0x20 表示 Shift,此时需要查 Shift 映射表转大写或特殊符号。 |
tshark 提数据 |
-e usb.capdata 提取击键数据,sed 删空行。 |
| 多余字符排除 | Flag 通常被 {} 包裹,外部字符是干扰或误触,提交时须去除。 |
五、小白避坑指南
- 分不清鼠标和键盘:如果提取的数据长度是 4 字节,那是鼠标;如果是 8 字节,就是键盘。
- 符号解错 :很多新手只写一张小写字母表,遇到
{或_就全乱了。一定要处理Shift键。 - 不小心删错数据 :有些脚本会直接
replace('00', ''),这会把数据破坏。必须根据字节位置(切片)来精准提取。
Rambo
2026年11月8日
🎉🎉🎉