USB流量分析

USB流量分析

USB流量指的是USB设备接口的流量,攻击者能够通过监听usb接口流量获取键盘敲击键、鼠标移动与点击、存储设备的铭文传输通信、USB无线网卡网络传输内容等等。在CTF中,USB流量分析主要以键盘和鼠标流量为主。

鼠标流量

基础知识
USB协议鼠标数据部分在Leftover Capture Data域中,数据长度为四个字节

第一个字节,代表按键

当取0×00时,代表没有按键
当取0×01时,代表按左键
当取0×02时,代表当前按键为右键

第二个字节,可看作为signed byte类型,其最高位为符号位,

当值为正时,代表鼠标右移像素位;
值为负时,代表鼠标左移像素位。

第三个字节,代表垂直上下移动的偏移。

当值为正时,代表鼠标上移像素位;
值为负时,代表鼠标下移像素位。

img

如图,数据信息为0x00002000,表示鼠标垂直向上移动20

flag隐藏在usb流量中,通过USB协议数据中的鼠标移动轨迹转换成flag

#!/usr/bin/env python
# coding:utf-8

import argparse
import struct
from enum import Enum

import matplotlib.pyplot as plt
import numpy as np
import pyshark


class Opcode(Enum):
LEFT_BUTTON_HOLDING = 0b00000001
RIGHT_BUTTON_HOLDING = 0b00000010


class MouseEmulator:
def __init__(self):
self.x = 0
self.y = 0
self.left_button_holding = False
self.left_button_holding = False

def move(self, x, y):
self.x += x
self.y -= y

def set_left_button(self, state):
self.left_button_holding = state

def set_right_button(self, state):
self.right_button_holding = state

def snapshot(self):
return (self.x, self.y, self.left_button_holding, self.right_button_holding)


class MouseTracer:
def __init__(self):
self.snapshots = []

def add(self, snapshot):
self.snapshots.append(snapshot)


def load_pcap(filepath):
cap = pyshark.FileCapture(filepath)
for packet in cap:
if hasattr(packet, 'usb') and hasattr(packet, 'DATA') and hasattr(packet.DATA, 'usb_capdata'):
yield packet.DATA.usb_capdata


def parse_packet(payload):
items = [struct.unpack('b', bytes.fromhex(i))[0]
for i in payload.split(":")]

state, movement_x, movement_y = 0, 0, 0

if len(items) == 4:
state, movement_x, movement_y, _ = items

if len(items) == 8:
state, _, movement_x, _, movement_y, _, _, _ = items

left_button_holding = state & Opcode.LEFT_BUTTON_HOLDING.value != 0
right_button_holding = state & Opcode.RIGHT_BUTTON_HOLDING.value != 0

return movement_x, movement_y, left_button_holding, right_button_holding


def snapshot_mouse(filepath):
mouse_emulator = MouseEmulator()
for i in load_pcap(filepath):
mx, my, lbh, rbh = parse_packet(i)
mouse_emulator.move(mx, my)
mouse_emulator.set_left_button(lbh)
mouse_emulator.set_right_button(rbh)
yield mouse_emulator.snapshot()


def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input-file", help="Path to the input pcap file.")
return parser.parse_args()


def main():
args = parse_args()
mt = MouseTracer()

xs = []
ys = []
colors = []
alphas = []
for snapshot in snapshot_mouse(args.input_file):
x, y, lbh, rbh = snapshot
color = "red" if lbh else "grey"
color = "blue" if rbh else color
alpha = 1 if lbh or rbh else 0.1
mt.add(snapshot)
xs.append(snapshot[0])
ys.append(snapshot[1])
colors.append(color)
alphas.append(alpha)

plt.scatter(xs, ys, c=colors, alpha=alphas)
plt.show()


if __name__ == "__main__":
main()

键盘流量

USB协议数据部分在Leftover Capture Data域中,数据长度为八个字节。

击键信息集中在第3个字节,每次击键都会产生一个数据包。

参考文档:USB keyboard映射表
在这里插入图片描述flag信息一般隐藏在flag隐藏在usb流量中,通过USB协议数据中的键盘键码转换成键位。

1.使用kali linux中的tshark 命令把cap data提取出来:

tshark -r usb.pcap -T fields -e usb.capdata > usbdata.txt
tshark -r usb.pcap -T fields -e usb.capdata | sed '/^\s*$/d' > usbdata.txt #提取并去除空行

2.根据《USB键盘协议中键码》中的HID Usage ID将数据还原成键位,可写一个Python脚本进行快速转换。

提取出来的数据可能会带冒号,也可能不带,但是一般的脚本都会按照有冒号的数据来识别。有冒号时提取数据的[6:8],无冒号时数据在[4:6]

f=open('usbdata.txt','r')
fi=open('out.txt','w')
while 1:
a=f.readline().strip()
if a:
if len(a)==16: # 键盘流量len=16,鼠标流量len=8
out=''
for i in range(0,len(a),2):
if i+2 != len(a):
out+=a[i]+a[i+1]+":"
else:
out+=a[i]+a[i+1]
fi.write(out)
fi.write('\n')
else:
break

fi.close()

image-20240204195045748

image-20240204195026090

再用脚本

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))

最后可能会将十六进制转换为字符串

m="666C61677B38663965643266393333656631346138643035323364303334396531323939637D"
s=bytes.fromhex(m)
print(s)