USB Horsemen of the HID Apocalypse

Dec 12 2024

2024 was a year. What better way to close it off than by releasing some silly, but ultimately useful, tools. This article discusses and releases 4 USB HID fuzzing tools that get far, far, too much use. Meet the four horsemen of the HID apocalypse. Plug them in and watch the chaos.

The purpose of these tools is varied, and I’m sure you can find weird and wonderful uses for them. They’re mostly intended to find vulnerabilities in Human Input Device handling logic. The idea is to have a device that pretends to be a keyboard or a mouse, and then do various simple tasks. In this case? Key mashing, frantic clicking and even some wiggling.

I don’t want to buy a Flipper Zero or other such contraption for this. A $1.50 dev board that’s already on my desk will work just fine, thank you.

“It’s not stupid if it works”
Metlstorm - an Infosec Podcaster

While they seem simple, these silly USB HID horsemen just keep finding bugs in things we plug them into. You may remember the 2023 article from Michael Fincham talking about TPM bypasses by mashing enter (https://pulsesecurity.co.nz/advisories/tpm-luks-bypass). How? USB horsemen.

These projects have all been put together using the Arduino IDE. On purpose, even! They’re intended to run on whatever miscellaneous dev boards you happen to have in the drawer that support USBHost. That RP2040 smart-watch dev board you were going to do something with three years ago? The ESP32 controlled light switch you’ve got kicking around somewhere? That’s what I’m talking about. In this case, I’m using a spare RP2040 I had in the drawer and the associated Arduino core (https://github.com/earlephilhower/arduino-pico).

This code is mostly Michael’s work. I Just tidied up a few things, made LEDs blink and added the fourth horseman. Let’s meet them:

Horseman Description
ENTERCANNON Mash the enter key
PROGAMER Generate random mouse movements and clicks
RAGEQUITTER Generate random keyboard mashing and random input
BAPHOMET Wiggle the mouse

The source code for these is available on GitHub.

All of these projects include a 5 second delay before beginning, and then wiggle the LED while they’re running. This is so you have chance to unplug the device after flashing it and not have it wreck your dev machine. Ask me how I know.

ENTERCANNON

The simplest Horseman! Mash enter every 10 milliseconds. Straight forward, right?

 18 void loop( ) {
 19   digitalWrite(LED_BUILTIN, !led_state);
 20   led_state = !led_state;
 21 
 22   Keyboard.press(KEY_RETURN);
 23   delay(10);
 24   Keyboard.releaseAll();
 25   delay(10);
 26 }

Fantastic for attacking pre-boot environments. Here’s what it looks like plugged into a Linux machine:

PROGAMER

PROGAMER moves the mouse around and clicks randomly. Fun fact, while writing this article I plugged PROGAMER into a Windows 11 machine, unplugged it before it could cause too much chaos, and broke mouse clicking. My mouse would still work, but left/right/thumb/whatever clicks did nothing until after an arbitrary amount of frustrated button mashing on my part.

 30 void loop() {
 31   digitalWrite(LED_BUILTIN, led_state);
 32   led_state = !led_state;
 33 
 34   /* press buttons */
 35   for (uint8_t i = 0; i != 3; i++) {
 36     if (modifier & (1 << i)) {
 37       Mouse.press(modifiers[i]);
 38       delay(2);
 39     }
 40   }
 41 
 42   Mouse.move(random(-127, 127), random(-127, 127), random(-127, 127));
 43   /* release buttons */
 44   for (uint8_t i = 0; i != 3; i++) {
 45     if (modifier & (1 << i)) {
 46       Mouse.release(modifiers[i]);
 47       delay(2);
 48     }
 49   }
 50 
 51   loops++;
 52   modifier++;
 53   code++;
 54 
 55   if (loops == 65536) {
 56     for (int b = 0; b < 10000; b++)
 57       Mouse.move(-127, -127, 0);
 58   }
 59 }

Do not underestimate the horsemen. Here is PROGAMER attached to the same Linux host:

RAGEQUITTER

RAGEQUITTER mashes they keyboard far faster than any human could possibly mash a keyboard, including every possible shortcut key combination. This specific horseman found us various crashes and interesting edge cases in systems with exposed USB ports.

33 void loop() {
 34   digitalWrite(LED_BUILTIN, led_state);
 35   led_state = !led_state;
 36 
 37   if (mode == 0) {
 38     /* press modifiers */
 39     for (uint8_t i = 0; i != 8; i++) {
 40       if (modifier & (1 << i)) {
 41         Keyboard.press(modifiers[i]);
 42         delay(2);
 43       }
 44     }
 45 
 46     /* skip modifiers I guess */
 47     if (code == 128) {
 48       code = 136;
 49     }
 50 
 51     /* press and release key */
 52     Keyboard.press(code);
 53     delay(10 + random(0, 10));
 54     Keyboard.release(code);
 55 
 56     /* release modifiers */
 57     for (uint8_t i = 0; i != 8; i++) {
 58       if (modifier & (1 << i)) {
 59         Keyboard.release(modifiers[i]);
 60         delay(2);
 61       }
 62     }
 63   } else {
 64     Keyboard.press(random(0,255));
 65     delay(2);
 66     Keyboard.press(random(0,255));
 67     delay(2);
 68     Keyboard.press(random(0,255));
 69     delay(10 + random(0,10));
 70     Keyboard.releaseAll();
 71   }
 72   loops++;
 73   modifier++;
 74   code++;
 75 
 76   if (loops == 65536) {
 77     Keyboard.releaseAll();
 78     loops = 0;
 79     if (mode == 0) {
 80       mode = 1;
 81     } else {
 82       mode = 0;
 83     }
 84   }
 85 }

Lets take a look at RAGEQUITTER in action:

BAPHOMET

A simple mouse jiggler that draws pentagrams with the mouse. Need to stop something from going to sleep? This is the horseman for you!

The code below includes a random fast jump to a position after drawing the pentagram to introduce some randomness and move the mouse around the wider screen. It doesn’t do any clicking, so you need to open up paint or something similar and hold your mouse button if you’d like a trace of what it’s doing.

 39 typedef struct {
 40   int8_t x;
 41   int8_t y;
 42 } t_coord;
 43 
 44 // points referenced from an x,y 0,0 origin
 45 t_coord points[] = {
 46   {-42,-127},
 47   {-88,0},
 48   {18,-88},
 49   {-102,-88},
 50   {0,0},
 51 ...yoink... 
 61 void loop() {
 62   digitalWrite(LED_BUILTIN, led_state);
 63   led_state = !led_state;
 64 
 65   for(int i = 0; i < sizeof(points) / sizeof(t_coord); i++){
 66     int x = points[i].x;
 67     int y = points[i].y;
 68 
 69     if(i == 0){
 70       plot_line(0, 0, x, y);
 71     } else {
 72       plot_line(0, 0, x - points[i-1].x, y - points[i-1].y);
 73     }
 74     delay(10);
 75   }
 76 
 77   Mouse.move(random(-512, 512), random(-512, 512), 0);
 78 }

This one was particularly fun to put together, since it involved some Bresenham line algorithm work.

Summary

The purpose of this article was to provide a practical example of something silly, simple, and cobbled together. These sorts of tools get put together all the time for various weird tasks and, really, my point here is this:

A cobbled together tool that works NOW beats the perfect solution that never gets delivered. Got something you’d like to try? Go for it! No plan survives first contact with the enemy, no design survives implementation. Get your hands dirty, have some fun and you might just learn something along the way, too! All of the code here is public on GitHub.

Happy holidays, everyone. See you in 2025 🌈


Follow us on LinkedIn