1. Overview

USB sniffing is the interception of communication from a USB device. In this tutorial, we’ll learn how to enable and then accomplish USB sniffing in the Linux operating system. Finally, we’ll learn how to understand the data we intercept from our USB device.

2. USB Sniffing in Linux

USB sniffing can be accomplished in Linux without the installation of additional software or tools. In this section, we’ll set up our system to allow for intercepting traffic from our USB device. Then, we’ll learn how to read the output traffic from a USB device (our computer’s keyboard).

2.1. Enabling USB Sniffing

Before we begin reading traffic from our USB, we’ll need to modify some things on our system.

To begin, we’ll first need to mount the debugfs filesystem. This is the filesystem we’ll use to retrieve communication from our USB device. Let’s mount debugfs using the mount command:

$ sudo mount -t debugfs device_debug /sys/kernel/debug

In this example, we use mount with the -t flag to specify the type of filesystem we want to mount — in this case, it’s debugfs. The next argument is device_debug, which is the device name. However, since we’re mounting the special filesystem debugfs, it doesn’t matter what device name we use. Finally, we specify the mount point. Typically, debugfs is mounted to /sys/kernel/debug.

After we have debugfs mounted, we’ll need to enable the usbmon module. This allows us to intercept USB traffic to our device. To enable a kernel module, we can use the modprobe command:

$ sudo modprobe usbmon

Here, we use modprobe with the first and only argument being the module we want to enable.

2.2. Sniffing USB Traffic

To begin intercepting USB traffic, we’ll need to identify a few pieces of information about the USB device we want to use. To list USB devices connected to our computer, we can use the lsusb command:

$ sudo lsusb
Bus 002 Device 002: ID 8087:8000 Intel Corp. Integrated Rate Matching Hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 006 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 005 Device 002: ID 1532:0226 Keyboard
Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 8087:8008 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 002: ID 0bc2:2344 Seagate RSS LLC Portable
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 007: ID 1b1c:1b3c Mouse
Bus 003 Device 006: ID 045b:0209 Hitachi, Ltd
Bus 003 Device 005: ID 045b:0209 Hitachi, Ltd
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

The output of lsusb will be a list of USB buses and device numbers along with their IDs and associated names. We’ll need the Bus and Device number of the USB device we want to modify. Since we are intercepting traffic from our keyboard, we’re going to want Bus 005 and Device 002.

Now that we have the information we need, we can output traffic from our USB keyboard.

To print the data from our USB, we use the cat command. The output of this command will be a stream of our data from the device(s) connected to the USB bus we are reading from. Since the device we want to read from is on Bus 5, the file we’ll want to read from is /sys/kernel/debug/usb/usbmon/5u:

$ sudo cat /sys/kernel/debug/usb/usbmon/5u
ffff9fa446351a80 1705656241 C Ii:5:003:1 0:8 8 = 00000000 00000000
ffff9fa446351a80 1705656263 S Ii:5:003:1 -115:8 8 <
ffff9fa446351a80 1708495718 C Ii:5:003:1 0:8 8 = 02000000 00000000
ffff9fa446351a80 1708495735 S Ii:5:003:1 -115:8 8 <
ffff9fa446351a80 1708647708 C Ii:5:003:1 0:8 8 = 02000b00 00000000
ffff9fa446351a80 1708647759 S Ii:5:003:1 -115:8 8 <
ffff9fa446351a80 1708711656 C Ii:5:003:1 0:8 8 = 00000b00 00000000
ffff9fa446351a80 1708711675 S Ii:5:003:1 -115:8 8 <
ffff9fa446351a80 1708743650 C Ii:5:003:1 0:8 8 = 00000000 00000000
ffff9fa446351a80 1708743669 S Ii:5:003:1 -115:8 8 <
...

2.3. Interpreting USB Traffic

Now that we know how to output the data from our USB device, let’s learn how to interpret this data into something we can understand.

Each line in the stream of data represents an event. The table below provides information about what each column means and an example event before and after being decoded:

URB Tag

Timestamp (In Microseconds)

Event Type

Address Word

URB Status word

Data Length (In Bytes)

Data Tag

Data Words (As Hexadecimal Bytes)

Example

ffff9fa446351a80

1708743650

C

Ii:5:003:1

0:8

8

=

00 00 2a 00 00 00 00 00

Decoded

ffff9fa446351a80

1708743650

Callback

Interrupt input and output:Bus 5:Device 003:Endpoint 1

URB Status (Success): URB Length

8

Data words are present.

While there’s a large amount of information output per event, we have to look at the hexadecimal bytes (data words) at the end to see what key was pressed on the keyboard. Specification for what number corresponds to what key is defined by the HID Usage Tables.

One key pressed on the keyboard is represented as one hexadecimal byte within the data words. Multiple non-zero hexadecimal bytes within the data words represent multiple keys being pressed simultaneously. When a key is released, another event will trigger from the keyboard, replacing that hexadecimal byte within the data words back to zero.

3. Conclusion

In this article, we learned how to enable USB sniffing in Linux through debugfs and usbmon. We then learned how to output the raw data from a USB keyboard. Finally, we learned how to interpret and decode the raw data output from our USB keyboard.