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
- Disconnect the Pico from USB.
- Hold down the BOOTSEL button (small button on the Pico).
- While holding BOOTSEL, connect the USB cable.
- 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
- Download the latest CircuitPython bundle:
wget https://github.com/agustfricke/raspberry-pi/raw/refs/heads/main/adafruit-circuitpython-bundle-6.x-mpy-20210130.zip
- Unzip the bundle:
unzip adafruit-circuitpython-bundle-6.x-mpy-20210130.zip
- 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:
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
.