Post

Tool - KBDscan

USB traffic scanner for keyboard detection.

Tool - KBDscan

Overview

Another day, another script! This time, I wrote a tool to analyze .pcap files containing USB protocol packets to determine whether a USB keyboard is present among the captured devices. This script acts as a discovery tool by scanning USB traffic in a .pcap file and identifying devices that match the characteristics of a USB keyboard.

I developed this tool after participating in irisCTF2025, where one challenge involved extracting keystrokes from USB captures to retrieve the flag. It was a fun challenge, and if you’re curious, you can check out my write-up.

The script relies on the USB Human Interface Device (HID) specifications for keyboards to identify relevant packets in a capture. Specifically, it searches for packets that match the following criterias :

  • Report Length: A USB keyboard typically sends 8-byte HID reports.
  • Reserved Byte: The second byte of each report must be 0x00, as this field is reserved in standard HID keyboard descriptors.
  • Empty Reports (Optional): Keyboards may send empty reports (all zeroes) when no keys are pressed, which can further confirm their presence.

You can find the source code on my Github.

Requirements
To use this script, make sure you have TShark installed, as it is required for processing the packet capture files.

Usage

To run the script, use the following command:

1
KBDscan.py [FILE]
  • [FILE] is the path to the .pcap file you want to analyze to check for the presence of USB keyboards.

By default, the script outputs a list of device addresses identified as potential keyboards in CSV format. Each row corresponds to a detected device, allowing for easy parsing and further analysis.

Options

Summary

Option nameUsageDescription
Not empty--not_emptyIncludes devices that have never sent an empty report..
Render format--r [RENDER]Specifies the output format.
Extract output--e [OUTFILE]Saves the results to the specified file.
Index--indexAdds an index when exporting results in CSV or JSON format.
No space--no_spaceRemoves leading and trailing spaces in the output.
Quiet--quietSuppresses the banner when running the script.

Not empty

1
KBDscan.py [FILE] --not_empty
  • Utility : By default, the script filters out devices that have never sent an empty report, as a standard keyboard used by a human will occasionally send empty reports when no keys are pressed. This behavior occurs because a user doesn’t type as fast as the computer reads input.
  • Default : The default value is False (i.e., the script expects keyboards to send empty reports).

I added this option to account for devices like Rubber Ducky-style keystroke injectors. Since these act as keyboards but type at machine speed, they may never send empty reports. While I haven’t had the opportunity to test this behavior yet, this option is included to avoid missing such devices if my assumption is correct.

Render

1
KBDscan.py [FILE] -r or --render [RENDER]
  • Utility : Changes the output format.
  • Choices : [RENDER] can be txt, csv, json or tab.
  • Default : The default output format is csv.

Save output

1
KBDscan.py [FILE] -e or --extract [OUTFILE]
  • Utility : Saves the results to the specified [OUTFILE]. The output format follows the one defined by the render option.
  • Default : If no output file is specified, results are printed to the standard output.

Other options

No space

1
KBDscan.py [FILE] --no_space
  • Utility : Removes leading and trailing spaces from the output. Useful when redirecting results to a file or script.
  • Default : By default, spaces are added for better readability in the standard output.

Add index

1
KBDscan.py [FILE] --index
  • Utility : Adds an index column when exporting results in CSV or JSON format.
  • Default : The index is not included by default.

Quiet

1
KBDscan.py [FILE] --quiet 
  • Utility : Suppresses the script’s banner when running.
  • Default : The banner is displayed by default.

Example

For this example, I’ll quickly walk through the irisCTF2025 challenge I mentioned earlier. If you’re looking for a more detailed explanation—especially about the USB HID protocol—you can check out my write-up.

Since this script is designed as a discovery tool, we use it right away when first analyzing the .pcap file to identify potential USB keyboards.

1
2
3
4
5
KBDscan.py deldeldel.pcapng -q 

# === Output ===

1.5.1

According to the script, the USB device with ID 1.5.1 appears to be a keyboard. We can now use this result to further analyze its transmitted packets and determine if any useful data—such as keystrokes—can be extracted.

Future Improvements

I’m considering expanding this script into a more complete workflow to:

  • Extract potential USB devices from the .pcap file.
  • Decode keystrokes from detected USB keyboards.
  • Save results to an output file for further analysis.

Additionally, I plan to test the script further and enhance its functionalities to make it more robust and reliable.

Conclusion

This is a simple yet effective script for quickly identifying USB keyboard devices in packet captures. The extended workflow I mentioned could be particularly useful for CTF players, as it would automate much of the analysis process.

In the future, I’d like to explore other USB device types, such as mice and storage devices, as I’ve come across some interesting scripts that analyze them.

Anyways, see you in the next one!

emree1

This post is licensed under CC BY 4.0 by the author.