Since the beginning of Keystroke Injection attacks, dating back to the origins of DuckyScript 1.0 over a decade ago, conventional wisdom has been to always begin a payload with the simple line: DELAY 3000.
TL;DR: If you've ever plugged in a keyboard and started pressing keys until you saw them appear - this is the nearly arbitrary delay we are trying to solve for.
Simply put, the USB Rubber Ducky is capable of injecting keystrokes faster than the target is ready to accept them in a usable fashion. While the USB Rubber Ducky boots, processes, and starts running its payload almost instantly, the target must enumerate and set up the new USB HID device as necessary.
On very old computers, USB device setup could be as much as 1 to 2 seconds. Therefore, a 3000 ms delay would in the majority of cases ensure that your payload would execute more reliably across varying targets.
But what about modern, fast computers? Why wait so long? Further complicating the problem; many systems will offer up the communication channel with the USB Rubber Ducky at the protocol level before the operating system itself starts accepting usable input.
While faster, omitting a "better safe than sorry" (better slow than broken) DELAY at the beginning of the your payload can often result in an "ello, World!" rather than a "Hello, World!". For a payload as simple as the above example it may not be the end of the world; but for your 5 second Mr Robot Style hack while on an engagement — it very well could be the difference between success and a very alarm bell ringing failure.
Even going as far as to custom tailor a finely tuned boot DELAY for your payload and the exact target system you'll find that the variance between USB Rubber Ducky "boots" (or payload runs) is enough to put you right back into the ballpark of +/- 1000ms. Considering many actual flash drives take significantly longer to enumerate on some systems, we're full circle back to "Just add a DELAY 3000
" for any hope of near 100% reliability and call it a day. Until...
Because DuckyScript 3.0 includes the logic necessary to make programmatic decisions, as well as awareness of the Lock Key states (that we abuse in our Keystroke Reflection attack) we can instruct our payload to DELAY
only for as long as is necessary before reliably injecting meaningful keystrokes. That's exactly what we've done with the EXTENSION DETECT_READY
.
Simply by being added to the top of your payload, this extension waits for the defined response delay until either the state of CAPSLOCK
has been confirmed on, or the defined iteration limit has been reached. The minimum response DELAY
is set to a default of 25 ms; a value we have found to be optimized. In our testing across a broad range of systems, this is the lowest value which produces the fastest results without additional unnecessary iteration.
This means one of three things has happened before the rest of your payload runs;
CAPSLOCK
turned on - $_CAPSLOCK_ON
== TRUE being a side effect of the operating system syncing the current CAPSLOCK
state to the newly connected keyboard: the USB Rubber Ducky. While this is not a full circle confirmation like the first possible outcome, it is sufficient enough indication that the host will accept further keystrokes from the USB Rubber Ducky.DELAY 3000
We put the DETECT_READY extension to the test by adding some debugging instrumentation. In this case, by importing the TRANSLATE extension, we are able to type out how long after connection and enumeration the target took to be ready for reliable keystroke injection.
This bare metal Dell XPS 13 machine is running on modern Intel i7 hardware.
Result: 25 milliseconds! 99.17% reduction
Similarly, our Linux example is also running on the same hardware in a VM
Result: 100ms; a 96.67% reduction
How about something ancient? For this test we dusted off an old ASUS Eee PC 701 — a 2007 era netbook with a Intel Celeron CPU underclocked to 630 MHz!
Result: ~400ms; a 86.67% reduction
Mobile targets such as iOS and Android need extra attention. While the DETECT_READY
extension will only DELAY
so long as the target is not prepared to accept keystrokes, in our experience mobile devices still require extra time to close their on-screen keyboards once a physical keyboard has been recognized by the OS.
Additionally MacOS traditionally does not sync or reflect lock key states; The iteration limit built into the extension by default ensures that the payload continues after a maximum DELAY
of 3000 ms, the previously held conventional wisdom, which in experience is sufficient for most targets. The value may be increased to achieve the highest possible compatibility in the case that the target requires a very slow driver download and installation — something that may be possible when brute forcing VID and PID values. That said, this is very unlikely for most known keyboard identifiers in our experience.
By using the extension architecture of DuckyScript 3.0, all payload authors benefit from the DETECT_READY
capabilities while having the flexibility to modify it to their specific requirements.
The DETECT_READY
extension can be found from the USB Rubber Ducky payload repository at https://github.com/hak5/usbrubberducky-payloads/tree/master/payloads/extensions
To implement DETECT_READY
in your own payload, simply begin typing EXTENSION DELAY_READY
and select the auto-complete suggestion from within PayloadStudio; it will automatically include the latest version.
Placing this at the top of your payload in lieu of the traditional DELAY 3000
will produce the results mentioned above. Get ready to enjoy both faster and more reliable payload execution!
Considering that Apple operating systems are the main flavor of target that won't take advantage of the enhanced functionality provided by DETECT_READY
, why not continue to improve within the scope where it is already applicable? If you've followed along so far you might have taken note that many operating systems that do reflect lock keys also (generally) sync lock states on connection of a new keyboard. This being part of what we can consider to be the setup process, it should be both faster and entirely passive to detect ready on these systems simply by detecting the initial update of the lock states. Thus PASSIVE_DETECT_READY
is born.
We're already detecting either the first usable input, or the end of the setup process...
Conveniently, the USB Rubber Ducky also happens to implement a novel, entirely passive, driver level, Windows detection - instantly during the setup process; not only can this process reliably become EVEN FASTER on Windows targets - we can also make a very simple yet powerful OS determination at the exact same time... PASSIVE_WINDOWS_DETECT
does exactly this for you.
Authors: Dallas Winger, Darren Kitchen
A two second HID attack against Windows and Mac that launches the website of your choosing. That's by far the most effective security awareness payload for the USB Rubber Ducky.