Boston Key Party 2015 - Riverside

Filter and decode USB mouse position data from a packet capture to recover the flag.

Introduction

Riverside was an original forensic challenge at Boston Key Party 2015.

omg tha NSA hacked my super secret login, I caught them exfillin this pcap, am I t3h fuxxed?

We are given a pcap-ng file, after opening with Wireshark we observe it only contains USB traffic. The attacker probably used an HID to exfiltrate informations from the victim.

Filtering

The first step is to identify the USB devices, maybe it will help us find suspicious behaviours.
We apply the following filter to Wireshark to display descriptor response from devices.
usb.bDescriptorType && usb.urb_type == 0x43

It looks like there are seven different USB devices.

  • Logitech M-BJ58/M-BJ69 Optical Wheel Mouse
  • Linux Foundation 3.0 root hub
  • Intel Integrated Rate Matching Hub
  • Linux Foundation 2.0 root hub
  • Acer Integrated Camera
  • Intel 7260AC Bluetooth
  • Linux Foundation 2.0 root hub

After further investigation we found out that the only device that actually exchange a lot of data is the USB mouse.
You can use the following Wireshark filter to only display the interesting packets.
usb.data_flag == "present (0)" && usb.device_address == 12

Decoding mouse data

To decode the mouse data, we fist have to know how it is encoded. It is actually very simple to understand, even without any documentations.
The carried data is 4 bytes the first one indicates the mouse status, the second one the direction on the X axis, the second the direction on the Y axis and the last one is for the mouse wheel.

We will convert the pcap-ng to a pcap with Wireshark and create a Python script to read the data from the capture file.

#!/usr/bin/env python
import struct
import Image
import dpkt

def print_data(pcap, device):
    for ts, buf in pcap:
        device_id, = struct.unpack("b", buf[0x0B])

        if device_id != device:
            continue

        data = struct.unpack("bbbb", buf[-4:])
        print(data)

if __name__ == "__main__":
    f = open("usb.pcap", 'rb')
    pcap = dpkt.pcap.Reader(f)

    print_data(pcap, 12)
    f.close()

Output

(0, 0, 0, 0)
(0, -3, 2, 0)
(0, 0, 0, 0)
(0, -2, 2, 0)
(0, 0, 0, 0)
(0, -4, 2, 0)
(0, 0, 0, 0)
(0, -4, 1, 0)
…

Mapping mouse position

The next step was to map the mouse position, we create a blank image and put pixels on the mouse coordinates and black squares on mouse clicks.

#!/usr/bin/env python
import struct
import Image
import dpkt

INIT_X, INIT_Y = 100, 400

def print_map(pcap, device):
    picture = Image.new("RGB", (1200, 500), "white")
    pixels = picture.load() 

    x, y = INIT_X, INIT_Y

    for ts, buf in pcap:
        device_id, = struct.unpack("b", buf[0x0B])

        if device_id != device:
            continue

        data = struct.unpack("bbbb", buf[-4:])

        status = data[0]
        x = x + data[1]
        y = y + data[2]

        if (status == 1):
            for i in range(-5, 5):
                for j in range(-5, 5):
                    pixels[x + i , y + j] = (0, 0, 0, 0)
        else:
            pixels[x, y] = (255, 0, 0, 0)
    picture.save("riverside-map.png", "PNG")

if __name__ == "__main__":

    f = open("usb.pcap", "rb")
    pcap = dpkt.pcap.Reader(f)

    print_map(pcap, 12)
    f.close()

Result

USB mouse coordinates
This definitely looks like a keyboard, it is probably the windows on screen keyboard. From this point we could read the typed letters one by one but it would take too much time so let's create a script to automate the process.

Getting the flag

I created a template representing an English keyboard.
Riverside template

The script below will crop a 50px square on the position of each click and save it in a new file.

#!/usr/bin/env python
import struct
import Image
import dpkt

INIT_X, INIT_Y = 100, 400

def print_map(pcap, device):
    picture = Image.open("riverside-template.png")
    pixels = picture.load()

    x, y = INIT_X, INIT_Y

    for ts, buf in pcap:
        device_id, = struct.unpack("b", buf[0x0B])

        if device_id != device:
            continue

        data = struct.unpack("bbbb", buf[-4:])

        status = data[0]
        x = x + data[1]
        y = y + data[2]

        if (status == 1):
            picture.crop((x - 25, y - 25, x + 25, y + 25)).save("letter-" + str(ts) + ".png", "PNG")

if __name__ == "__main__":
    f = open("usb.pcap", "rb")
    pcap = dpkt.pcap.Reader(f)

    print_map(pcap, 12)
    f.close()

The last step is to concatenate all the pictures in the right order and you should get the flag.