Files
2026-03-17 21:39:58 +01:00

4.8 KiB

Digispark USB Rubber Ducky PoC

A proof-of-concept USB HID keystroke injection tool using a Digispark ATtiny85 development board and PlatformIO.

When plugged into a host machine the device emulates a USB keyboard and automatically types a predefined payload.

Prerequisites

  • PlatformIO CLI (or the PlatformIO IDE extension for VS Code)
  • A Digispark ATtiny85 board with the micronucleus bootloader

On Linux you may also need udev rules so the micronucleus uploader can access the USB device without root:

# /etc/udev/rules.d/49-micronucleus.rules
SUBSYSTEMS=="usb", ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="0753", MODE="0660", GROUP="plugdev"

Reload rules after creating the file:

sudo udevadm control --reload-rules && sudo udevadm trigger

Project structure

.
├── platformio.ini   # Board & framework configuration
├── src/
│   └── main.cpp     # Payload source code
├── include/         # Project header files
├── lib/             # Private libraries
└── test/            # Unit tests

Building

Compile the firmware:

pio run

PlatformIO will automatically download the required toolchain and Digistump framework on the first run.

Uploading to the device

pio run -t upload

The console will print Plug in device now.... You then have 60 seconds to connect the Digispark to a USB port. The micronucleus bootloader on the board will receive the firmware and reboot into the payload automatically.

Tip: If the upload times out, unplug the board, run the command again and re-plug when prompted.

Adapting the payload

All payload logic lives in the setup() function inside src/main.cpp, between the --- payload start --- and --- payload end --- comments.

Keyboard layout

The layout is selected with a #define before the DigiKeyboard.h include:

#define LAYOUT_FRENCH        // AZERTY
#include <DigiKeyboard.h>

Available layouts (define exactly one):

Define Layout
LAYOUT_US_ENGLISH US QWERTY (default if none specified)
LAYOUT_FRENCH French AZERTY
LAYOUT_FRENCH_BELGIAN Belgian AZERTY
LAYOUT_FRENCH_SWISS Swiss French
LAYOUT_CANADIAN_FRENCH Canadian French
LAYOUT_CANADIAN_MULTILINGUAL Canadian Multilingual
LAYOUT_GERMAN German QWERTZ
LAYOUT_GERMAN_SWISS Swiss German
LAYOUT_SPANISH Spanish
LAYOUT_SPANISH_LATIN_AMERICA Latin American Spanish
LAYOUT_ITALIAN Italian
LAYOUT_PORTUGUESE Portuguese
LAYOUT_PORTUGUESE_BRAZILIAN Brazilian Portuguese
LAYOUT_DANISH Danish
LAYOUT_FINNISH Finnish
LAYOUT_NORWEGIAN Norwegian
LAYOUT_SWEDISH Swedish
LAYOUT_TURKISH Turkish
LAYOUT_UNITED_KINGDOM UK
LAYOUT_ICELANDIC Icelandic
LAYOUT_IRISH Irish

Useful API

Function Description
DigiKeyboard.print("text") Type a string
DigiKeyboard.println("text") Type a string followed by Enter
DigiKeyboard.sendKeyStroke(key) Send a single key press
DigiKeyboard.sendKeyStroke(key, modifier) Send a key press with a modifier
DigiKeyboard.delay(ms) Wait (also keeps USB alive)

Common modifier constants: MOD_SHIFT_LEFT, MOD_CONTROL_LEFT, MOD_ALT_LEFT, MOD_GUI_LEFT (Windows/Super key).

Common key codes (USB HID usage IDs):

Code Key
0x04..0x1D a..z
0x1E..0x27 1..0
40 Enter
41 Escape
43 Tab
44 Space

The full list is in the USB HID usage tables specification (section 10, Keyboard/Keypad Page).

Example: open a terminal on Linux and run a command

void setup() {
  DigiKeyboard.delay(2000);

  // Ctrl+Alt+T opens a terminal on most Linux desktops
  DigiKeyboard.sendKeyStroke(0x17, MOD_CONTROL_LEFT | MOD_ALT_LEFT);  // 't'
  DigiKeyboard.delay(1000);

  DigiKeyboard.println("echo 'Hello from Digispark!'");
}

Hardware constraints

Resource Total Used by current payload
Flash 6 012 bytes ~2 860 bytes (48%)
RAM 512 bytes ~104 bytes (20%)

The micronucleus bootloader occupies roughly 2 KB of the 8 KB total flash, leaving ~6 KB for application code. Keep payloads concise, especially long strings.

Troubleshooting

Problem Solution
Upload failed: cannot access USB device Add udev rules (see Prerequisites) or run with sudo
Upload times out Unplug the board, run pio run -t upload again, plug in when prompted
Wrong characters appear on the host Change the LAYOUT_* define to match the host OS keyboard layout
Payload runs too fast / host misses keystrokes Increase DigiKeyboard.delay() values between actions