HackIT 2017: Foren100

Overview

For the Foren100 challenge we are given a file named task.pcap. The first thing to do is open it in something like Wireshark. A quick peek with Wireshark shows that they have captured USB packets. Our goal will likely be to extract the flag from the data being transferred.

Step 0: Analysis

At this point, we’re not quite sure what data is being passed around in these USB packets. Based on the nature of the protocol, it could be many things, and the method of transferring that information does not often allow you to simply run strings to discover what it is. Let’s open it up with gallimaufry:

In [1]: from Gallimaufry import USB

In [2]: pcap = USB("./task.pcap")

In [3]: pcap
Out[3]: <USB packets=835>

Now that we’ve loaded up the pcap, we should take a look at what’s inside it. With smaller pcap files, the easiest way is probably to use the summary property:

In [4]: print(pcap.summary)
PCAP: /home/user/opt/gallimaufry/examples/keyboard/HackIT2017/task.pcap
Total Packets: 835

Devices
-------

    Apple, Inc. - Aluminum Keyboard (ISO)
    -------------------------------------
    bus_id: 1
    device_address: 3
    device_version: 0.6.9
    bluetooth_version: 2.0.0
    packets: 514

    Configurations
    --------------

        Configuration 1
        ---------------
        bNumInterfaces = 2
        self_powered = False
        remote_wakeup = False

        Interfaces
        -----------

            Interface 0
            -----------
            Class: HID – Human Interface Device
            SubClass: Boot Interface Subclass
            Protocol: Keyboard

            Endpoints
            ---------

                Endpoint 1
                ----------
                direction: In
                transfer_type: Interrupt
                packets: 478

            Interface 1
            -----------
            Class: HID – Human Interface Device
            SubClass: No Subclass
            Protocol: None

            Endpoints
            ---------

                Endpoint 2
                ----------
                direction: In
                transfer_type: Interrupt
                packets: 0

This is a very clean usb pcap. There’s only one Device, with one Configuration, and two interfaces. Of those interfaces, only one interface and one endpoint have data. It’s a fair bet that we should look at what’s in that data.

Step 1: Extract the Keystrokes

Here we can drill down into the object to extract the keystrokes. Let’s take a look at the devices:

In [6]: pcap.devices
Out[6]: [<Apple, Inc. Aluminum Keyboard (ISO) v0.6.9 USB2.0.0 bus_id=1 address=3>]

Drill down next into the configurations:

In [7]: pcap.devices[0].configurations
Out[7]: [<Configuration bNumInterfaces=2 bConfigurationValue=1>]

There’s only one. Let’s look at the Interfaces:

In [8]: pcap.devices[0].configurations[0].interfaces
Out[8]:
[<Interface HID – Human Interface Device bInterfaceNumber=0>,
 <Interface HID – Human Interface Device bInterfaceNumber=1>]

From the summary, we know we want Interface 0. Finally, checkout the endpoints:

In [9]: pcap.devices[0].configurations[0].interfaces[0].endpoints
Out[9]: [<Endpoint number=1 direction=In transfer_type=Interrupt packets=478>]

There’s only one of them. At this point, we have an Endpoint object. The library has identified that this endpoint is a keyboard, and has added a Keyboard object to it. Let’s pull that out.:

In [10]: keyboard = pcap.devices[0].configurations[0].interfaces[0].endpoints[0].keyboard

In [11]: keyboard
Out[11]: <Keyboard keystrokes=208>

Notice that the Keyboard object has identified 208 keystrokes for this endpoint. Let’s extract them:

In [12]: print(keyboard.keystrokes)
w
k
f
b
3'[Up Arrow][[Up Arrow]l[Up Arrow]#[Up Arrow]{w$[Down Arrow]>b[Down Arrow]ag[Down Arrow][e[Down Arrow]ci.[[Up Arrow][f[Up Arrow]{k[Up Arrow]n$[Up Arrow]ju}[Down Arrow]:[Down Arrow]3[Down Arrow]u[Down Arrow]%=[Up Arrow]~[Up Arrow]y[Up Arrow]6[Up Arrow],'[Down Arrow]p[Down Arrow]b[Down Arrow]7[Down Arrow]%&[Up Arrow]d[Up Arrow]0[Up Arrow]j[Up Arrow]pt[Down Arrow]i[Down Arrow]a[Down Arrow][[Down Arrow]k([Up Arrow]=[Up Arrow]r[Up Arrow]m[Up Arrow]]=[Down Arrow]0[Down Arrow]d[Down Arrow]>[Down Arrow]lc[Up Arrow]*[Up Arrow]_[Up Arrow]{[Up Arrow]j%[Down Arrow]u[Down Arrow]s[Down Arrow]([Down Arrow]*2[Up Arrow]0[Up Arrow]n[Up Arrow]'[Up Arrow];9[Down Arrow]h[Down Arrow]4[Down Arrow]][Down Arrow]y4[Up Arrow]'[Up Arrow]k[Up Arrow];[Up Arrow]+p[Down Arrow]f[Down Arrow]e[Down Arrow]$[Down Arrow]!}[Up Arrow]1[Up Arrow]_[Up Arrow]k[Up Arrow]s&[Down Arrow]s[Down Arrow]2[Down Arrow]c[Down Arrow]%q[Up Arrow]$[Up Arrow].[Up Arrow]![Up Arrow]#,[Down Arrow]s[Down Arrow]0[Down Arrow]c[Down Arrow]z3[Up Arrow]e[Up Arrow]}[Up Arrow]-[Up Arrow]i

At this point you may notice there are a bunch of [Up Arrow] and [Down Arrow] in the output. This is gallimaufry’s way of telling you that arrow characters were pushed. Thus, simply printing out the output like this, while a good start, won’t get us all the way. gallimaufry has the ability to attempt to interpret keystrokes in different settings. As of writing, the only setting it is interpreting is a notepad like setting. The goal for this setting is to interpret characters (such as the arrows) and maintain state of a cursor object, thus allowing it to correctly reproduce what was being typed.

To utilize this, use the keystrokes_interpret property, like so:

In [13]: print(keyboard.keystrokes_interpret)
w{w$ju},'pt]=j%;9+ps&#,i
k#>bn$:6pjim0{u'h;fks!s-
flag{k3yb0ard_sn4ke_2.0}
b[[e[fu~7d[=>*(0]'$1c$ce
3'ci.[%=%&k(lc*2y4!}%qz3

We can see that the flag is in the middle of the other random looking keys.

Flag: flag{k3yb0ard_sn4ke_2.0}

Resources