ECSC 18 Polish Junior Quals: keybd.py
All we get in this challenge is a file, keybd.pcapng. This is a packet dump, so let’s look at it in Wireshark. On a first glance, we can see that it’s a capture of the USB protocol. The first two packets reveal that the recorded communications are between the computer and a USB keyboard:
After the initial descriptor transfer, we have a bunch of
URB_INTERRUPT
type
transfers. Presumably, these contain the keypresses, but to decode them
manually would require understanding the USB HID (Human Interface
Device) protocol, which seems like a lot of work. At this point, I got
the idea of simply replaying the keypresses in a VM and looking at the
output, so I fired up a VM with a keyboard device and ran this tool. That didn’t work so well (or at
all), so I took to searching some more and found this lovely website which details how to
extract what Wireshark calls “capture data”, which contains the
currently pressed keys in the following format:
[modifier, reserved, Key1, Key2, Key3, Key4, Key6, Key7]
.
Using the keycode map here and some Python code to remove
duplication of keys which were pressed for more than one interrupt, we
can turn the keypresses into a readable stream:
Unfortunately there is no flag in here :( There is an especially
annoying stream of keys, which I thought looked less random than the
rest and spent a good few hours trying to decode:
8O73892934957430574309573490574905735097489329482
Having given up on that, I looked again at the key stream and
realised that the command executed at the beginning,
sudo python keybd.py
cannot
be the keylogger itself, because typing it in is already being logged.
So, it must be something else. Back to Wireshark:
Lo and behold, right after the command starts, we see a different
kind of USB transfer begin, using
URB_CONTROL
packets. What’s
more, these packets contain a different last byte every time:
Let’s look at these and see if they contain anything interesting. I
dumped the URB_CONTROL
packet contents into a plaintext file and then extracted the last bytes
using some Vim macros. The LSB in them is simply alternating between
0
and
1
, so it cannot be text and
should be discarded. However, the 2nd LSB
(0b000000X0
) changes in a
non-trivial way. What’s more, there are exactly 241 of these packets,
which is almost exactly 30 bytes. Cutting the first bit off to make it
even and decoding the values as a bitstream, I got the flag :D