Regenerate Message Typed as captured in a USB Keyboard Packet Capture

Introduction

I was recently presented with a packet capture file to perform some forensics on it as a challenge and see if I could find the hidden message. Naturally, it being a packet capture I fired up Wireshark only to be faced with a very bland single colour screen, quite different from the usual network captures most would be used to when using Wireshark (see Figure 1 below). There was none of the usual indication of different protocols broken down by colour. This was going to be a different type of challenge, and one I was going to learn a lot from, I knew I would enjoy it, and I sure did!

Figure 1: Bland, colour-free Wireshark window

Goal of this Post

The packet capture turned out to be from a USB keyboard. In order to process the packet capture I had to avail of a number of resources in order to figure out how. I’ll link out to the most useful resources throughout the article. In processing the packet capture I initially converted the key press events manually step by step, this was a lengthy procedure and if there had been a large amount of key presses captured it would have been futile to try to do by hand. So as I was going along, the Python part of my brain was figuring out how I could automate this process which brings me to the Goal of this Blog post. The main goal of this post is to provide the reader with a Jupyter Notebook that will take the extracted key press data from a USB packet capture and will build up the message typed out on the keyboard.

Caveats to the solution presented:

  1. The solution presented is specific to a US English keyboard, but my guess is the solution could be adapted for other keyboard languages (@s and #s may be incorrectly presented if your pcap is from a UK keyboard).
  2. Not all possible key presses are accounted for, you will see from the keys dictionary that I only accounted for the key type up to 0x73, again if other keys need to be accounted for in your solution it would be relatively easy to add them.
  3. Some of the keys from 0x00 to 0x73 aren’t accounted for either e.g. PAGE_UP and PAGE_DOWN, but again, if your solution required them they could be accommodated.
  4. Very little validation or error checking and handling (close to none) is provided in my code. This is more of a proof of concept and if used in any sort of production environment would pose a risk of exploitation.

How to Analyse a USB Keyboard Packet Capture

So, I’m not going to detail the steps I took, mainly because I found some great resources and I’m just going to link to those. As with most problems, I began with a simple search online to see how I could approach this packet capture. I first found this Medium post which I read through and compared the details against my packet capture to confirm that I was looking at a legitimate USB keyboard packet capture.

There wasn’t a chance that I was going to have the patience to manually extract each key press event so the next thing I searched for was a tshark command that I could just type into the command line and it would spit out each event. I found such a command on this page about CTF Forensics which had a nice section on USB Forensics in particular. The command was very simple:

tshark -r usb-keyboard-data.pcap -T fields -e usb.capdata

This command spits out a line per keyboard event that are split up into 8 hex encoded bytes per line. Taken from the above resource again, the different bytes represent:

Byte 0: Keyboard modifier bits (SHIFT, ALT, CTRL etc)

Byte 1: reserved

Byte 2-7: Up to six keyboard usage indexes representing the keys that are currently “pressed”. Order is not important, a key is either pressed (present in the buffer) or not pressed.

https://bitvijays.github.io/LFC-Forensics.html#usb-keyboard

The next problem was to figure out what each byte value represented in terms of key presses and the next invaluable resource found was a github gist that list all the USB HID Keyboard Scan Codes, very useful!

After that I was able to take my tshark generated key press events and reproduce the message that had been typed.

The Automation Part

What follows below is the Jupyter Notebook I used to reproduce the message generated by the key presses extracted from the capture (it looks a bit better if viewed over on Github directly). The first section of the notebook is the creation of a dictionary to specify how to interpret the keys pressed.

For my solution the input expected is a text file containing the output generated by the tshark command. The next section reads in this text file and echoes it to the Notebook as a sanity check. I added the keys pressed manually which appear on the right hand side of the printed text file so that someone coming fresh to this Notebook would be able to understand what message was typed and verify the ultimate output easily.

Finally the processing of the key events is conducted to reproduce the ultimate message. At the end two messages are printed out. The first message is the completed interpreted message, in which certain navigation key movements are accounted for e.g. left/right arrow, Home, End, Backspace, Delete. The next message is the raw typed keys, in which the press of left arrow is included as “LEFT_ARROW” for example.

The original capture file sent to me included a much longer message which this code was able to adequately deal with and produced the correct message. Aside from a lack of error handling or input validation in this code, as long as you can trust your input (which is a terrible idea) this solution should work quite well with most English language keyboard captures.

Alternative Solution

If you find my solution above long or messy (and I would understand if you did), allow me to offer you an alternative. Some further research led me to another Python solution that has the added benefit of reading directly from the packet capture itself. Both solutions could obviously be combined if I ever wanted to take this seriously, but it was just a fun challenge so I didn’t. This alternate solution will generate one message that is more akin to the raw typed message from the solution above.

Conclusion

If you enjoyed this Blog post or it helped you in anyway, please leave a comment below. If you would like anything clarified, ask a question below and I’ll do my best to help you out.

Leave a Reply

Your email address will not be published. Required fields are marked *