@agustfricke
Raspberry pi

Rubber Ducky

Create a USB Rubber Ducky-like device using a Raspberry Pi Pico

Create Project Folder

mkdir ~/rubber_ducky
cd ~/rubber_ducky

Download CircuitPython Firmware

wget https://github.com/agustfricke/raspberry-pi/raw/refs/heads/main/adafruit-circuitpython-raspberry_pi_pico-en_US-6.3.0-rc.0.uf2

Connect Raspberry Pi Pico

  1. Disconnect the Pico from USB.
  2. Hold down the BOOTSEL button (small button on the Pico).
  3. While holding BOOTSEL, connect the USB cable.
  4. Release the BOOTSEL button after connecting.

Monitor USB events in real time:

sudo dmesg -w

Check the connected device:

sudo fdisk -l

Look for a line like:

Disk model: RP2

The device is usually /dev/sda1 (not /dev/sda):

Device     Boot Start    End Sectors  Size Id Type
/dev/sda1           1 262143  262143  128M  e W95 FAT16 (LBA)

Mount the Pico

Create a mount point:

sudo mkdir -p /mnt/pico

Mount the Pico:

sudo mount /dev/sda1 /mnt/pico

Verify:

ls -la /mnt/pico/

You should see files like INDEX.HTM and INFO_UF2.TXT.

Copy CircuitPython .uf2 Firmware

Navigate to your project folder:

cd ~/rubber_ducky

Copy the firmware:

sudo cp adafruit-circuitpython-raspberry_pi_pico-en_US-6.3.0-rc.0.uf2 /mnt/pico/

The Pico will reboot, now running CircuitPython.

Install the Adafruit HID Library

  1. Download the latest CircuitPython bundle:
wget https://github.com/agustfricke/raspberry-pi/raw/refs/heads/main/adafruit-circuitpython-bundle-6.x-mpy-20210130.zip
  1. Unzip the bundle:
unzip adafruit-circuitpython-bundle-6.x-mpy-20210130.zip
  1. Copy the HID library to the Pico:
sudo cp -r adafruit-circuitpython-bundle-6.x-mpy-20210130/lib/adafruit_hid /mnt/pico/lib/

This library is essential for USB keyboard functionality.

Create code.py

Replace the code.py on the Pico with the following script:

code.py
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode
import time

# Define Ducky commands and corresponding keycodes
duckyCommands = ["WINDOWS", "GUI", "APP", "MENU", "SHIFT", "ALT", "CONTROL", "CTRL", "DOWNARROW", "DOWN",
"LEFTARROW", "LEFT", "RIGHTARROW", "RIGHT", "UPARROW", "UP", "BREAK", "PAUSE", "CAPSLOCK", "DELETE", "END",
"ESC", "ESCAPE", "HOME", "INSERT", "NUMLOCK", "PAGEUP", "PAGEDOWN", "PRINTSCREEN", "SCROLLLOCK", "SPACE",
"TAB", "ENTER", " a", " b", " c", " d", " e", " f", " g", " h", " i", " j", " k", " l", " m", " n", " o", " p", " q", " r", " s", " t",
" u", " v", " w", " x", " y", " z", " A", " B", " C", " D", " E", " F", " G", " H", " I", " J", " K", " L", " M", " N", " O", " P",
" Q", " R", " S", " T", " U", " V", " W", " X", " Y", " Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]

keycodeCommands = [Keycode.WINDOWS, Keycode.GUI, Keycode.APPLICATION, Keycode.APPLICATION, Keycode.SHIFT, Keycode.ALT, Keycode.CONTROL,
Keycode.CONTROL, Keycode.DOWN_ARROW, Keycode.DOWN_ARROW ,Keycode.LEFT_ARROW, Keycode.LEFT_ARROW, Keycode.RIGHT_ARROW, Keycode.RIGHT_ARROW,
Keycode.UP_ARROW, Keycode.UP_ARROW, Keycode.PAUSE, Keycode.PAUSE, Keycode.CAPS_LOCK, Keycode.DELETE, Keycode.END, Keycode.ESCAPE,
Keycode.ESCAPE, Keycode.HOME, Keycode.INSERT, Keycode.KEYPAD_NUMLOCK, Keycode.PAGE_UP, Keycode.PAGE_DOWN, Keycode.PRINT_SCREEN,
Keycode.SCROLL_LOCK, Keycode.SPACE, Keycode.TAB, Keycode.ENTER, Keycode.A, Keycode.B, Keycode.C, Keycode.D, Keycode.E, Keycode.F, Keycode.G,
Keycode.H, Keycode.I, Keycode.J, Keycode.K, Keycode.L, Keycode.M, Keycode.N, Keycode.O, Keycode.P, Keycode.Q, Keycode.R, Keycode.S, Keycode.T,
Keycode.U, Keycode.V, Keycode.W, Keycode.X, Keycode.Y, Keycode.Z, Keycode.A, Keycode.B, Keycode.C, Keycode.D, Keycode.E, Keycode.F,
Keycode.G, Keycode.H, Keycode.I, Keycode.J, Keycode.K, Keycode.L, Keycode.M, Keycode.N, Keycode.O, Keycode.P,
Keycode.Q, Keycode.R, Keycode.S, Keycode.T, Keycode.U, Keycode.V, Keycode.W, Keycode.X, Keycode.Y, Keycode.Z,
Keycode.ZERO, Keycode.ONE, Keycode.TWO, Keycode.THREE, Keycode.FOUR, Keycode.FIVE, Keycode.SIX, Keycode.SEVEN, Keycode.EIGHT, Keycode.NINE]

# Functions to parse and execute DuckyScript lines
def convertLine(line):
    newline = []
    for j in range(len(keycodeCommands)):
        if line.find(duckyCommands[j]) != -1:
            newline.append(keycodeCommands[j])
    return newline

def runScriptLine(line):
    for k in line:
        kbd.press(k)
    kbd.release_all()

def sendString(line):
    layout.write(line)

def parseLine(line):
    global defaultDelay
    if line.startswith("REM"):
        pass  # comment
    elif line.startswith("DELAY"):
        time.sleep(float(line.split()[1])/1000)
    elif line.startswith("STRING"):
        sendString(line[7:])
    elif line.startswith("DEFAULT_DELAY") or line.startswith("DEFAULTDELAY"):
        defaultDelay = int(line.split()[1]) * 10
    else:
        newScriptLine = convertLine(line)
        runScriptLine(newScriptLine)

# Initialize HID
kbd = Keyboard(usb_hid.devices)
layout = KeyboardLayoutUS(kbd)

time.sleep(0.5)  # Allow host to recognize device

defaultDelay = 0
duckyScriptPath = "payload.dd"
with open(duckyScriptPath, "r", encoding='utf-8') as f:
    previousLine = ""
    duckyScript = f.readlines()
    for line in duckyScript:
        line = line.rstrip()
        if line.startswith("REPEAT"):
            for i in range(int(line.split()[1])):
                parseLine(previousLine)
                time.sleep(defaultDelay / 1000)
        else:
            parseLine(line)
            previousLine = line
        time.sleep(defaultDelay / 1000)

print("Done...")

Create Example Payload

Create payload.dd with a sample script:

DELAY 2000

GUI 0
DELAY 1000

GUI d
DELAY 1000
STRING kitty
ENTER

DELAY 1000
STRING echo "hello friend, starting payload"
ENTER
DELAY 5000

STRING exit
ENTER

Copy payload.dd to /mnt/pico. Now, every time the Pico is plugged in, it will execute the instructions in payload.dd.