2022CISCN初赛-ez_usb-复盘WriteUp
2022CISCN初赛 ez_usb 复盘WriteUp
前言
本篇首发于看雪社区
发现看雪社区对带有图片markdown非常友好,开始有点想把我博客迁过去了
本篇所有图片都挂载在看雪社区的图床下
前期准备
USB协议规范
l USB UART
l USB HID
l USB Memory
最上面这个就是标准的串口实现,可以通过USB连接STM32或ESP8266这些MCU
中间这个可以做BadUSB(实现键盘模拟输入)
最下面这个应该是访问U盘
每一种USB设备,尤其是人机交互设备和存储设备,都有一串特殊的数字,这串数字被称为厂商ID和产品ID。这两个数字一般是成对出现的。厂商ID用于标明该产品是由哪一个制造商所生产的,而产品ID是产品的标识符。
有关协议的详细说明:USB初学(一)---USB-HID的初步认识【转】
HID设备的描述符除了5个USB的标准描述符(设备描述符、配置描述符、接口描述符、端点描述符、字符串描述符)外,还包括三个HID设备类特定的描述符:HID描述符、报告描述符、实体描述符。
除了HID的三个特定描述符组成对HID设备的解释外,5个标准描述符中与HID设备有关的部分有:
- 设备描述符中:bDeviceClass, bDeviceSubClass, bDeviceProtocol三个值必须为0
- 接口描述符中:bInterfaceClass的值必须时0x03, bInterfaceSubClass的值为0或1, 为1表示HID设备是一个启动设备(Boot Device, 一般对PC机有意义,意思是BIOS启动时能识别您使用的HID设备,切只有标准鼠标或者键盘才能称为Boot Device),为0表示HID设备是操作系统启动厚才能识别使用的设备。bInterfaceProtocol的取值含义如下:
bInterfaceProtocol的取值(十进制) | 含义 |
---|---|
0 | None |
1 | 键盘 |
2 | 鼠标 |
3-255 | 保留 |
buu的一道赛题
当时做到时候由于不熟悉usb流量分析,网上资料也看的云里物理,找了篇题解进行分析
buu usb 1分题
首先,修复rar的文件头
里面有一张233.png
233.png通过stegsolver可以提取出一张二维码出来
用草料二维码可以提取出一段字
ci{v3erf_0tygidv2_fc0}
另外还有一个fkm文件,发现503B这个zip文件头,另存为zip然后解压
提到了可以用tshark把指令提取出来
tshark -r key.pcap -T fields -e usb.capdata > usbdata.txt
但我实际用UsbKeyboardDataHacker出结果
维吉尼亚密码,密码为xinan
fa{i3eei_0llgvgn2_sc0}
离flag很近,具备栅栏密码的特征
还真是
切入正题
根据标识符,找到设备的情况:
2.4.0 未知设备
2.8.0 keyboard
2.10.0 logitech
2.3.0 Lite-on Technology
2.4.0 Intel Bluetooth
用usb.addr作为过滤条件筛出数据,然后分组导出
usb.addr== "2.8.1"
usb.addr== "2.10.1"
2.8.1 里藏了一个压缩包,有着rar文件头的标记
把
tips:010editior的hex粘贴需要从Edit进入
2.10.1 里藏了密码35c535765e50074a,应该是鼠标信号
解压即可得到flag
当时走过的弯路
当时十分莽撞的直接拿现成脚本去试,结果什么都没有
照猫画虎x2,用提取不少帖子用tshark提取capdata
tshark -r ez_usb.pcapng -T fields -e usb.capdata > usbdata.txt
然后很不幸的得到了一个空的txt
看了hid部分以后,想了一下抓的应该是usbhid.data
tshark -r ez_usb.pcapng -T fields -e usbhid.data > usbdata.txt
有结果了
找到一个处理冒号和空格的脚本(忘记从哪里找来的)
#!/usr/bin/env python
#-*- coding: utf-8 -*-
info = '''
kali下运行:
tshark -r usb.pcap -T fields -e usb.capdata > usbdata.txt
提取流量包信息
然后通过该脚本可以过滤掉空格和其他内容,并且添加冒号
'''
print(info)
f_data = input("请输入带处理txt文件的路径:")
shujian = int(input("鼠标流量信息请输入8,键盘流量请输入16:"))
f = open(f_data,'r')
# 整理到out.txt
with open('out.txt','w') as f_out:
for i in f.readlines():
s = i.strip()
# 鼠标流量长度为8 ,键盘流量长度为16
if len(s) == shujian:
# 鼠标流量长度为8 ,键盘流量长度为16
nsl = [s[j:j+2] for j in range(0,shujian,2)]
ns = ":".join(nsl)
f_out.write(ns)
f_out.write('\n')
数据整理完成,开始转换
//tran.py
normalKeys = {"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",
"28": "<RET>", "29": "<ESC>", "2a": "<DEL>", "2b": "\t", "2c": "<SPACE>", "2d": "-", "2e": "=", "2f": "[",
"30": "]", "31": "\\", "32": "<NON>", "33": ";", "34": "'", "35": "<GA>", "36": ",", "37": ".", "38": "/",
"39": "<CAP>", "3a": "<F1>", "3b": "<F2>", "3c": "<F3>", "3d": "<F4>", "3e": "<F5>", "3f": "<F6>",
"40": "<F7>", "41": "<F8>", "42": "<F9>", "43": "<F10>", "44": "<F11>", "45": "<F12>"}
shiftKeys = {"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": "!",
"1f": "@", "20": "#", "21": "$", "22": "%", "23": "^", "24": "&", "25": "*", "26": "(", "27": ")",
"28": "<RET>", "29": "<ESC>", "2a": "<DEL>", "2b": "\t", "2c": "<SPACE>", "2d": "_", "2e": "+", "2f": "{",
"30": "}", "31": "|", "32": "<NON>", "33": "\"", "34": ":", "35": "<GA>", "36": "<", "37": ">", "38": "?",
"39": "<CAP>", "3a": "<F1>", "3b": "<F2>", "3c": "<F3>", "3d": "<F4>", "3e": "<F5>", "3f": "<F6>",
"40": "<F7>", "41": "<F8>", "42": "<F9>", "43": "<F10>", "44": "<F11>", "45": "<F12>"}
output = []
keys = open('out.txt') #这里是加号冒号的数据
for line in keys:
try:
if line[0]!='0' or (line[1]!='0' and line[1]!='2') or line[3]!='0' or line[4]!='0' or line[9]!='0' or line[10]!='0' or line[12]!='0' or line[13]!='0' or line[15]!='0' or line[16]!='0' or line[18]!='0' or line[19]!='0' or line[21]!='0' or line[22]!='0' or line[6:8]=="00":
continue
if line[6:8] in normalKeys.keys():
output += [[normalKeys[line[6:8]]],[shiftKeys[line[6:8]]]][line[1]=='2']
else:
output += ['[unknown]']
except:
pass
keys.close()
flag=0
print("".join(output))
for i in range(len(output)):
try:
a=output.index('<DEL>')
del output[a]
del output[a-1]
except:
pass
for i in range(len(output)):
try:
if output[i]=="<CAP>":
flag+=1
output.pop(i)
if flag==2:
flag=0
if flag!=0:
output[i]=output[i].upper()
except:
pass
print ('output :' + "".join(output))
然后得到同样一个带有rar文件头的数据包
当时为了修复这个包,花了不少力气。但事后来看,只要所有数据混到一块就全部木大。
运气好的话,没准可以用binwalk分离出来。
尾声
这类题目的套路就是把数据藏到capdata,然后找脚本读取就OK。本次CISCN的题目还不涉及加密算法,某些程度上来说比Buu上那道还简单。
当时在场上有把zip包从USB中提取出来,但是没把2.8.1的信息单独分离出来,即使修复文件头依然无法解压文件。只能说准备确实还不够充分,留下了遗憾。
补充
用过的exp
https://github.com/WangYihang/UsbKeyboardDataHacker/blob/master/UsbKeyboardDataHacker.py
参考过的资料
当时现场做不出来,使足力气Bing+Google,赛后发现自己没少看资料,就整理了出来