2022年全国大学生信息安全竞赛(CISCN)初赛中的"ez_usb"题目,是一道典型的USB流量分析取证题。这类题目在CTF比赛中属于中等难度,主要考察选手对USB协议的理解、数据包分析能力以及Python数据处理技巧。题目给出的是一个pcapng格式的网络抓包文件,但实际包含的是USB键盘流量数据。
从历年赛事经验来看,USB流量分析题通常会设置以下几个考察点:
推荐使用Kali Linux作为基础分析环境,预装了所需的大部分工具。关键工具链包括:
注意:Windows平台也可完成分析,但需要注意Wireshark版本差异可能导致过滤语法不同。建议使用Linux环境保持一致性。
以下Python库需要提前安装:
bash复制pip install pyshark scapy pandas
对于键盘按键码转换,建议准备HID Usage Tables文档(可从USB官网获取)或使用预制的键码映射表。比赛中常用的精简版映射表如下:
| 键码 | 字符 | 键码 | 字符 |
|---|---|---|---|
| 0x04 | a/A | 0x1d | z/Z |
| 0x05 | b/B | 0x1e | 1/! |
| 0x06 | c/C | 0x1f | 2/@ |
| ... | ... | ... | ... |
首先用Wireshark打开pcapng文件,观察到主要流量来自USB HID设备:
bash复制usb.transfer_type == 0x01 && usb.dst == "host"
关键字段说明:
使用tshark提取关键字段:
bash复制tshark -r ez_usb.pcapng -T fields -e usb.capdata \
-Y 'usb.transfer_type == 0x01 && usb.dst == "host"' > keydata.txt
典型数据处理脚本框架:
python复制import pandas as pd
mapping = {
0x04:['a','A'], 0x05:['b','B'], 0x06:['c','C'],
# 补充完整映射表
}
def parse_keystroke(data):
parts = data.split(':')
if len(parts) < 8: return ''
keycode = int(parts[2],16)
modifier = int(parts[0],16)
if keycode == 0: return ''
shift_pressed = modifier & 0x02 or modifier & 0x20
return mapping.get(keycode,[''])[1 if shift_pressed else 0]
with open('keydata.txt') as f:
print(''.join([parse_keystroke(line.strip()) for line in f]))
比赛中常见的坑点包括:
改进后的处理逻辑应包含:
python复制caps_lock = False
output = []
for data in packet_data:
modifier = data[0]
keycode = data[2]
# 处理Caps Lock
if keycode == 0x39:
caps_lock = not caps_lock
continue
shift = (modifier & 0x02) or (modifier & 0x20)
effective_shift = shift ^ caps_lock # XOR运算
# 特殊键处理
if keycode == 0x28: # Enter
output.append('\n')
elif keycode == 0x2a: # Backspace
if output: output.pop()
else:
char = mapping.get(keycode,['',''])[effective_shift]
output.append(char)
原始过滤条件可能遗漏关键数据包,改进方案:
bash复制(usb.transfer_type == 0x01 && usb.dst == "host") ||
(usb.transfer_type == 0x01 && frame.len == 72)
通过分析数据包时间戳还原输入节奏:
python复制from datetime import datetime
timestamps = []
with open('timing.txt') as f:
for line in f:
ts = float(line.strip())
timestamps.append(datetime.fromtimestamp(ts))
# 计算输入间隔
intervals = [(timestamps[i+1]-timestamps[i]).total_seconds()
for i in range(len(timestamps)-1)]
# 识别长停顿(可能为flag分段)
break_points = [i for i,x in enumerate(intervals) if x > 1.0]
处理残缺或异常数据包的策略:
python复制def sanitize_data(raw):
parts = raw.split(':')
if len(parts) < 8:
parts += ['00']*(8-len(parts))
return ':'.join(parts[:8])
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 输出乱码 | 键码映射表错误 | 核对HID Usage Tables |
| 缺失字符 | 过滤条件太严格 | 放宽wireshark过滤器 |
| 顺序错乱 | 未按时间排序 | 添加-t ad参数 |
| 重复输出 | 重复抓包 | 添加去重逻辑 |
分阶段验证:
python复制# 阶段1:原始数据检查
print(raw_data[:100])
# 阶段2:键码提取验证
print(extracted_codes[:50])
# 阶段3:最终输出检查
print(''.join(output)[:200])
边界案例测试:
可视化辅助:
python复制import matplotlib.pyplot as plt
plt.plot(intervals)
plt.show() # 识别异常时间间隔
类似题目可能考察USB鼠标数据:
python复制x, y = 0, 0
coords = []
for packet in mouse_data:
dx, dy = packet[1], packet[2]
x += dx if dx < 128 else dx-256
y += dy if dy < 128 else dy-256
coords.append((x,y))
键盘鼠标混合流量的处理方法:
进阶题目可能对USB数据加密:
python复制def decrypt_payload(data, key=0x55):
return bytes([b ^ key for b in data])
在实际比赛中,建议准备通用的USB分析工具链和脚本框架,遇到变种题型时能快速适配。对于这道"ez_usb"题目,掌握基础键盘流量分析技术即可在30分钟内完成解题。关键是要理解HID协议的数据组织方式,并能够编写健壮的数据处理脚本。