Hunting for USB Rubber Ducky/ Bad USB with ATP
Alright, so we’re here again with a hunting query to help catching some bad people out there.
This hunting started as a discussion with a customer and we figured out we should be able to chain the queries to see what happens after an event to be able to decide if it’s malicious or not.
Just to clear things out:
A USB Rubber Ducky is not something your AV solution would pick up. It’s a keyboard, it’s a preprogrammable keyboard. It exactly the same thing as plugging in a USB keyboard and type, except you’ve already told the keyboard what to type.

The ducky language is very simple as shown in below example
DELAY 3000
gui r
DELAY 100
STRING powershel xxxxxxxx
ENTER
The example will wait for 3 seconds, press win key and “r” and wait for another 100 ms and then type powershell xxxxxxx and then enter

You encode the textfile to a binary and loads it to the flash memory inside (which is read by the rubber ducky, not by the device you connect it to) well you can make some changes to that, but in general and depending how you configure it.
Hunting USB devices
It’s easy to find the PnP event which could be headsets, mass storage devices, keyboards etc.
MiscEvents
| where ActionType == "PnpDeviceConnected"
| extend parsed=parse_json(AdditionalFields)
| sort by EventTime desc nulls last
| project
EventTime,
ComputerName,
DeviceDescription=tostring(parsed.DeviceDescription),
ClassName=tostring(parsed.ClassName),
DeviceId=tostring(parsed.VendorIds),
VendorIds=tostring(parsed.VendorIds), MachineId , ReportId
Mass storage devices
MiscEvents
| where ActionType == "PnpDeviceConnected"
| extend ParsedFields=parse_json(AdditionalFields)
| project ClassName=tostring(ParsedFields.ClassName), DeviceDescription=tostring(ParsedFields.DeviceDescription),
DeviceId=tostring(ParsedFields.DeviceId), VendorIds=tostring(ParsedFields.VendorIds), MachineId, ComputerName, EventTime
| where ClassName contains "drive" or ClassName contains "usb"
| where DeviceDescription contains "Mass Storage"
But idea to hunt for duckies is that we want to see what happens after the device load.
- Device connected
- Someone executes powershell or cmd within a certain amount of time (10 seconds)
To explain further
We gather all devices where action type is “PnpDeviceConnected” and where the device description is “HID Keyboard Device”
Then we gather process starts which contains powershell or cmd and then we compare the time for the event and only present the ones where the process start happened within 10 seconds after the device load event.
// Hunting for malicious HID Keyboard devices
// PNP Event and Powershell or CMD within 10 seconds after driver load
let MalPnPDevices =
MiscEvents
| where ActionType == "PnpDeviceConnected"
| extend parsed=parse_json(AdditionalFields)
| sort by EventTime desc nulls last
| where parsed.DeviceDescription == "HID Keyboard Device"
| project PluginTime=EventTime, ComputerName,parsed.ClassName, parsed.DeviceId, parsed.DeviceDescription, AdditionalFields;
ProcessCreationEvents
| where ProcessCommandLine contains "powershell" or
ProcessCommandLine startswith "cmd"
| where isnotempty(ProcessCommandLine)
| project ProcessCommandLine, ComputerName, EventTime, ReportId, MachineId
| join kind=inner MalPnPDevices on ComputerName
| where (EventTime-PluginTime) between (0min..10s)
| where ComputerName == ComputerName1
Of course, the 10 seconds is basically the Delay time. If an attacker sets 11 seconds, we would miss it. But this query would have to be trimmed for your environment.
There is also another thing, as an attacker, you would like to deliver the payload as quick as possible but still want the driver to be able to load.
I usually use between 3-6 seconds as initial payload for my duckies.

You could also chain this with other events, like networkevents to discover network request after a specific event.
Happy Hunting!
Sec-Labs R&D