CSAW CTF 2012: dongle.pcap (net300)
We received a pcap file containing USB Request Blocks (URBs) with no other information. A quick look at the exchanged frames with Wireshark revealed that most of the data was sent to the host from a specific device (26.3, HID device from “bInterfaceClass”, keyboard from “bInterfaceProtocol” from the official documentation) on an interrupt endpoint.
The first idea was of course: is the key typed on the keyboard? Every interrupt
packet from the 26.3 device was carrying a keycode, and all these packets had
the same URB id: 0xffff88003b7d8fc0
. Exploring packets structure made it easy
to localize these keycodes: the offset 0x42
of these interrupt packets. We
just had to script keycodes extracting using a correspondance table, then!
We created a Python script using the dpkt
library to parse the pcap file and
extract the keycodes:
import binascii
import dpkt
import struct
import sys
# Start the pcap file parsing
f = open(sys.argv[1], 'rb')
pcap = dpkt.pcap.Reader(f)
# Create a partial mapping from keycodes to ASCII chars
keys = {}
keys.update({
i + 0x4: chr(i + ord('a'))
for i in range(26)
})
keys.update({
i + 0x1e: chr(i + ord('1'))
for i in range(9)
})
keys[0x27] = '0'
keys.update({
0x28: '\n',
0x2c: ' ',
0x2d: '-',
0x2e: '+',
0x2f: '[',
0x30: ']',
})
# Then iterate over each USB frame
for ts, buf in pcap:
# We are interested only in packets that has the expected URB id, and
# packets carrying keycodes embed exactly 8 bytes.
urb_id = ''.join(reversed(buf[:8]))
if binascii.hexlify(urb_id) != 'ffff88003b7d8fc0':
continue
data_length, = struct.unpack('<I', buf[0x24:0x28])
if data_length != 8:
continue
key_code = ord(buf[0x42])
if not key_code:
continue
sys.stdout.write(keys[key_code])
The output of this script was the following “keyboard stream”:
rxterm -geometry 12x1+0+0
echo k
rxterm -geometry 12x1+75+0
echo e
rxterm -geometry 12x1+150+0
echo y
rxterm -geometry 12x1+225+0
echo [
rxterm -geometry 12x1+300+0
echo c
rxterm -geometry 12x1+375+0
echo 4
rxterm -geometry 12x1+450+0
echo 8
rxterm -geometry 12x1+525+0
echo b
rxterm -geometry 12x1+600+0
echo a
rxterm -geometry 12x1+675+0
echo 9
rxterm -geometry 12x1+0+40
echo 9
rxterm -geometry 12x1+75+40
echo 3
rxterm -geometry 12x1+150+40
echo d
rxterm -geometry 12x1+225+40
echo 3
rxterm -geometry 12x1+300+40
echo 5
rxterm -geometry 12x1+450+40
echo c
rxterm -geometry 12x1+375+40
echo 3
rxterm -geometry 12x1+525+40
echo a
rxterm -geometry 12x1+600+40
echo ]
Alright, the indented result should be to display the key re-ordering first the characters with terminal positions. We then had just to format a script to actually open multiple terms in the same time at the right place and containing the associated character:
python2 extract_keyboard.py dongle.pcap |
sed 's/rxterm \(.*\)/xterm \1 -e "\\/g' |
sed 's/echo \(.*\)/echo -n \1; read" \&/g' > display_key.sh
And finally, running the display_key.sh
script gave us the key:
key[c48ba993d353ca]