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

152 lines
4.8 KiB
Markdown

# 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](https://docs.platformio.org/en/latest/core/installation/index.html) (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:
```bash
# /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:
```bash
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:
```bash
pio run
```
PlatformIO will automatically download the required toolchain and Digistump framework on the first run.
## Uploading to the device
```bash
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:
```cpp
#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](https://usb.org/document-library/hid-usage-tables-15) specification (section 10, Keyboard/Keypad Page).
### Example: open a terminal on Linux and run a command
```cpp
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 |