Skip to content

Physical Hacking: An introduction to Ducky Script

By Sebastian Günther

Image source: hak5

Physical hacking of a computer encompasses injection of commands with the target to grab files, install programs, create custom users or gain control. With the programmable Ducky Script USB stick, these exploits can be crafted to target any host system. When inserted, a preprogramed script is executed, written in the Ducky Script language.

This article is a concise introduction to the DuckyScript programming language. Based on the official DuckyScript documentation, it covers the essential commands and overall syntax, from keystrokes to host state management and function definition.

This article is for educational purposes only. Only use computers and devices that you own, and be mindful that they can be damaged.

The technical context for this article is CircuitPython v9.1.4 and Adafruit CircuitPython Bundle v9.x. The examples should work with newer releases too, but might require some code changes.

DuckyScript Program

A DuckyScript program is a line terminated sequence of commands that executes keystrokes on a target computer. The goal of these keystrokes is typically to infiltrate or compromise a system, for example by running commands that start a reverse shell, creating user accounts with root privileges, or downloading and installing malware. Therefore, a DuckyScript program can be thought of the vehicle, and the actual, os-specific exploit, as the transport.

The original DuckyScript programs are compiled into a binary using the Hak5 Payload Studio program. This binary is then uploaded to an USB stick, and executed when the USB stick is connected to a host device

The original hardware is not the only option to run DuckyScripts - several interpreters for different hardware are available in the open source community, including these:

  • pico ducky: A library for running DuckyScript on the Raspberry Pico microcontroller, using a CircuitPython interpreter.
  • Potato Parser: A library for running DuckyScript on ESP32 Devices. This Arduino project creates a custom binary with all included libraries.
  • Flipper BadUSB: A DuckyScript 2.x compatible language that runs on the flipper hacking device.
  • DucKey-Logger: A special PowerShell based exploit that completely logs all keystrokes and sends them to an online address.

Ducky Script Commands

The DuckyScript commands can be separated into these categories:

  • Keystrokes: Most commands initiate a single or sequence of keystrokes, including control keys.
  • Host State Management: Some commands read the systems keyboard state, and can be used as triggers to continue program flow, e.g. waiting for the user to start the screensaver, from which the exploit than resumes
  • Device State Management: The original rubber duck USB stick features an LED and a button. Commands for these features can turn the LED on or await the press of a button as a trigger
  • Program Structure & Control: DuckyScript allow the definition of variables and functions, lopping, branches and functions. For integer and boolean values, several operators exist. Finally, the global program state can be reflected and modified.

Keystrokes

Character Keys

STRING, STRINGLN

Execute character keypresses one after the other. The STRINGLN command automatically adds a newline character. Both commands can also be used in a block notation, and indented form.

To enter the string "hello" on a target system, both of the following commands can be used.

STRING
  h
  e
  l
  l
  o

STRING hello

cursor keys

UP DOWN LEFT RIGHT UPARROW DOWNARROW LEFTARROW RIGHTARROW PAGEUP PAGEDOWN HOME END INSERT DELETE DEL BACKSPACE TAB SPACE

These commands execute a cursor key press. As such, they are helpful to navigate inside a specific program, such as a text editor.

System Keys

ENTER ESCAPE PAUSE BREAK PRINTSCREEN MENU APP F1 F2 F3 F4 F5 F6 F7 F8 F9 F0 F11 F12

SHIFT ALT

CONTROL or CTRL

COMMAND

WINDOWS or GUI

CTRL SHIFT ALT SHIFT COMMAND CTRL COMMAND CTRL SHIFT COMMAND OPTION COMMAND OPTION SHIFT CONTROL ALT DELETE CAPSLOCK NUMLOCK SCROLLOCK

System keys and key combinations can be used to target OS-specific shortcuts, such as to quickly open a program.

INJECT_MOD

This special command needs to be used when a single modifier key should be pressed. See the following example.

INJECT_MOD GUI

Control Keystroke Duration

HOLD RELEASE

All keystroke commands are executed immediately, but with the help of these special commands, the duration can be controlled.

The following example shows how to keep two keys pressed for 4 seconds.

HOLD ALT F4
DELAY 4000
RELEASE ALT F4

Host State Management

With these commands, the hosts currently pressed keys can be determined.

WAIT_FOR_CAPS_ON WAIT_FOR_CAPS_OFF WAIT_FOR_CAPS_CHANGE WAIT_FOR_NUM_ON WAIT_FOR_NUM_OFF WAIT_FOR_NUM_CHANGE WAIT_FOR_SCROLL_ON WAIT_FOR_SCROLL_OFF WAIT_FOR_SCROLL_CHANGE

On a keyboard, the cap, num and scroll keys can be locked. Their absolute state is reflected by the ON and OFF variants, and any state change with the CHANGE suffix. These commands act as triggers - the program execution stops until the desired state of a key is detected.

$_CAPSLOCK_ON $_NUMLOCK_ON $_SCROLLLOCK_ON

These commands return boolean values depending on the current state of the lock keys.

SAVE_HOST_KEYBOARD_LOCK_STATE RESTORE_HOST_KEYBOARD_LOCK_STATE

This command saves the current state of all lock keys and then restores this state. It could be used to detect a certain user behavior, then switching to a desired state to execute a program, and then to return to the previous state without alarming the user.

Device State Management

Device Type

ATTACKMODE {HID STORAGE HID STORAGE OFF}

A command that puts the USB stick into a different device type that the host system discovers, such as HID, storage, or HID and storage. When set to off, the device will disconnect.

ATTACKMODE {VID_ PID_ MAN_ PROD_ SERIAL_}

Each USB device has several identifiers. These can be configured during setup. Here is a concrete example how to combine these two commands into one.

ATTACKMODE HID VID_033A PID_C3C4 MAN_DUCK PROD_DUCK SERIAL_42

ATTACKMODE {VID_RANDOM PID_RANDOM MAN_RANDOM PROD_RANDOM SERIAL_RANDOM}

Randomize the vendor and product information.

SAVE_ATTACKMODE RESTORE_ATTACKMODE

For dynamically changing the programs behavior, its attack mode configuration can be saved as a state. The state also stores the current ID values - enabling the device to mimic itself as a keyboard now, an USB stick later, and so on.

Device Files

HIDE_PAYLOAD, RESTORE_PAYLOAD

A DuckyScript program will be compiled to a binary file and placed on the device. This file is called inject.bin, and an accompanying seed.bin to generate random values. Both files are shown when the device is connected in HID mode to the host. Executing HIDE_PAYLOAD hides both files, and RESTORE_PAYLOAD shows them.

A third file is called loot.bin, which will be filled with content extracted from the host using the special command EXFIL.

Hardware Components

WAIT_FOR_BUTTON_PRESS

The Ducky Script USB device features a button which can be used for two purposes inside a program. With this command, program execution is blocked until the hardware button is pressed.

BUTTON_DEF & END_BUTTON, ENABLE_BUTTON, DISABLE_BUTTON

This command encapsulates a separate block of code that executes when the button is pressed. (a simple form of a function, but without parameter passing or other). The other two commands control whether such defined blocks will or will not be executed when a button is pressed.

Here is an example to print a message when the button is pressed.

BUTTON_DEF
  STRING Magic button was pressed
END_BUTTON

ENABLE_BUTTON

LED_OFF, LED_R LED_G

These commands control the onboard LED - it can be illuminated as red, green or turned off.

Program Structure & Control

Ducky Script programs are executed top-down. Some commands alter this control flow, including function definition, loops, and several commands delaying the execution until a trigger occurs.

Variables

DEFINE

Constants declared with #, they can contain any string. When called, their value will be inserted. The documentation is not clear about if you can also nest Ducky script commands inside a constant.

VAR

Variables are declared with a $, they can hold unsigned 8bit integer values in decimal and hex notation, as well as the boolean values (represented as strings) of TRUE and FALSE.

Assignments can be augmented, e.g. increasing numerical values as shown in the following example.

VAR $COUNTER = ( $COUNTER + 1 )

RANDOM_LOWERCASE_LETTER RANDOM_UPPERCASE_LETTER RANDOM_LETTER RANDOM_NUMBER RANDOM_SPECIAL RANDOM_CHAR

With these commands, random strings can be created, including letters, numbers, and special characters. See the following code for the full rule set.

RANDOM_LOWERCASE_LETTER = "abcdefghijklmnopqrstuvwxyz"
RANDOM_UPPERCASE_LETTER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
RANDOM_LETTER = """
  abcdefghijklmnopqrstuvwxyz
  ABCDEFGHIJKLMNOPQRSTUVWXYZ
  """
RANDOM_NUMBER = "0123456789"
RANDOM_SPECIAL = "!@#$%^&*()"
RANDOM_CHAR = """
  abcdefghijklmnopqrstuvwxyz
  ABCDEFGHIJKLMNOPQRSTUVWXYZ
  0123456789
  !@#$%^&*()
  """

$_RANDOM_INT $_RANDOM_MIN $_RANDOM_MAX

These commands can be used instead of concrete numerical values, resulting in a random integer with the default range of 0...65535, or within the defined min and max values.

Programming Operators

+, -, *, /, %, ^

Perform mathematic operations on numbers. The documentation does not detail how overflows of the integer value range are handled.

==, !=

These comparison operator work on pairs of integers and boolean values, for example checking the state of a certain key press.

>, <, >=, <=

These comparison operators only work on integer values.

&&, ||

These commands enable chaining multiple boolean expressions together.

&, |, >>, <<

Bitwise operators that work on numerical values.

Loops and Conditions

WHILE

The while statement continuously executes a code block until its termination definition evaluates to true. The following example prints and increases an integer value until it reaches the value of 10.

VAR $NUM = 0
WHILE ($NUM <= 10)
    STRING $NUM
    $NUM = ($NUM + 1)
END_WHILE

IF, ELSE

These commands enable branching of program logic. The given term is evaluated to a boolean value, and when evaluated to true, the given code is executed. From the documentation, it is unclear whether multiple ELSE blocks can be used, and also if other program structure commands can be nested, such as function definitions.

VAR $NUM = 42
IF ( $NUM == 42 ) THEN
  STRING Magic number
ELSE IF ( $NUM != 42 ) THEN
  STRING Just an ordinary number
END_IF

Function Definition

FUNCTION RETURN

Functions define blocks of code that are executed when called. They cannot receive parameters, but any variable referenced from within a function will be searched in the global scope. Functions can return numerical and boolean values that can used in other expression (for example a comparison operator).

Here is an example how to increment a numerical value by 10.

FUNCTION INCR()
  VAR $RES = $NUM +10
  RETURN $RES
END_FUNCTION

VAR $NUM = 10
VAR $NUM2 = INCR()

Global Program Flow

DELAY

Fixed time in milliseconds that needs to pass before running the next step.

RESTART_PAYLOAD, STOP_PAYLOAD

Stop and restart the programmed payload

RESET

Completely clears the keystroke buffers, including all LED states.

Global Program Configuration

$_JITTER_ENABLED, $_JITTER_MAX

Define a maximum value of millisecond delay to keystrokes, where each value will be computed individually.

Internal Variables

Ducky Script programs define several internal variables - the complete list encompassed around 40 entries. The variables provide access to the global device, program, and host state, including the hardware buttons and LED, the lock keys, and on the host, whether read/write activity to the USB device is detected or the OS type.

Conclusion

DuckyScript is a programming language to initiate keystrokes on a target computer. Essentially, these commands serve as a vehicle to transport an exploit onto a target system. This article provided a compact introduction to the DuckyScript language. You learned about all commands structured into four categories: a) keystrokes, a single or sequence of keys that are executed, b) host state management, commands that check the state of control keys, c) device management, to control the rubber ducks USB stick button and LED , d) program structure & control, defining variables, functions, and using various expressions to compare and modify numbers, strings and boolean values. Reflecting the language design, a striking feature is that the linear, continuous execution can be controlled by waiting for a specific condition on the target system. And with this, an exploit can be applied at the best moment to fulfill its goal.