Open-source project
dbisu/pico-ducky avatar
dbisu/pico-ducky

pico-ducky: a DuckyScript injector on a Raspberry Pi Pico

Create a USB Rubber Ducky like device using a Raspberry PI Pico

3,335 stars529 forksPythonGPL-2.0

At a glance

What is it?
pico-ducky turns a Raspberry Pi Pico or Pico W into a USB Rubber Ducky style keystroke injector running CircuitPython. It is a small, readable build for people who want to write and run DuckyScript payloads on hardware they already own, with a web editor on the Pico W.
Who is it for?
pico-ducky is for engineers who already own a Pico or Pico W and want a DuckyScript injector they can read and modify, and for anyone who needs the Pico W web editor at 192.168.4.1 to write payloads without unplugging the board. It is the wrong choice if your payload needs DuckyScript 3.0 features, since the README states only DuckyScript 1.0 and some of 3.0 are supported, or if you need a device that hides its USB mass storage by default on a plain Pico.
Can I use it commercially?
Yes, with conditions. GPL-2.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Activity is slowing. The repository last received commits 6 months ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What pico-ducky replaces, and who builds one

A USB Rubber Ducky is a small board that presents itself to a host as a keyboard and types a stored script. pico-ducky does the same job with a Raspberry Pi Pico or Pico W. The README describes it as a way to "Make a cheap but powerful USB Rubber Ducky with a Raspberry Pi Pico", and the workflow is the same: you write a script, the board enumerates as a HID keyboard, and the script runs when the board is plugged into the target.

The audience is narrow and practical. It is for people who already have a Pico on a desk, are comfortable copying files onto a CIRCUITPY drive, and want the payload logic in plain Python they can edit. It is also for anyone who wants the Pico W variant, where the board creates its own access point and serves a small web editor so payloads can be changed over Wi-Fi instead of by pulling the board out of the target machine.

What it is not is a finished product. There is no enclosure, no case, no vendor support line. You supply the board, the USB cable and the jumper wires. The payoff is that the whole stack is a handful of Python files in the repository root plus a CircuitPython runtime.

How the CircuitPython stack turns a Pico into a keyboard

The repository root holds the runtime pieces: boot.py, code.py, duckyinpython.py, pins.py, webapp.py and wsgiserver.py. The README's install steps copy those files to the root of the CIRCUITPY drive and copy library dependencies into a lib folder. CircuitPython loads code.py on boot, and that file pulls in the DuckyScript interpreter in duckyinpython.py.

Two jumpers change behaviour before the payload runs. Connecting pin 1 (GP0) to pin 3 (GND) puts the board in setup mode, which the README says stops the pico-ducky from injecting the payload into your own machine. That is the mode you use while editing. Separately, connecting pin 18 (GND) to pin 20 (GPIO15) disables the USB mass storage presentation, so the board does not show up as a drive on the target computer. The README notes an asymmetry here: on a plain Pico the default is USB mass storage enabled, while on the Pico W the default is disabled.

On the Pico W, webapp.py and wsgiserver.py expose a small HTTP service. The README gives the default AP address as 192.168.4.1 and lists the routes: /, /new, /ducky, /edit/<filename>, /write/<filename> and /run/<filename>, plus an API route /api/run/<filenumber>. The Wi-Fi credentials come from a secrets.py file you create yourself, with ssid and password keys.

Installing pico-ducky and running a first payload

The README claims the whole process takes under five minutes, which is optimistic the first time because of the library copying. Start by cloning the repository so you have boot.py and the Python files locally.

bash
git clone https://github.com/dbisu/pico-ducky.git

Hold the boot button while plugging the Pico into USB. It appears as a removable drive named RPI-RP2. Copy the matching CircuitPython UF2 for your board to that drive; the README names adafruit-circuitpython-raspberry_pi_pico-en_US-10.0.3.uf2, and separate files for pico_w, pico2 and pico2_w. After the reboot the drive is named CIRCUITPY.

Next, from the Adafruit CircuitPython Bundle 10.x mpy zip, copy adafruit_hid, adafruit_debouncer.mpy, adafruit_ticks.mpy, asyncio and adafruit_wsgi into the lib folder on the board. Then copy boot.py, duckyinpython.py, code.py, pins.py, webapp.py and wsgiserver.py to the root of CIRCUITPY. On a Pico W you also create secrets.py:

python
secrets = { 'ssid' : "BadAPName", 'password' : "badpassword" }

The README uses those exact placeholder values. Replace them before you use the board anywhere real, because an open or guessable access point is a separate problem from the payload itself.

Finally, write a DuckyScript file and save it as payload.dd in the root of the drive. The README points to the hak5 usbrubberducky-payloads repository for existing scripts and to the DuckyScript basics documentation for writing your own. The repository also ships examples/functions.dd and examples/while_loops.dd if you want something to compare against. With the setup jumper removed and the board plugged into the target, the README says the device reboots and the script runs after about half a second.

Where pico-ducky stops: DuckyScript coverage and the setup jumper

The clearest limitation is language coverage. The README states that pico-ducky "only supports DuckyScript 1.0, and some of 3.0". If your payload depends on DuckyScript 3.0 features outside that subset, this project is the wrong tool and no amount of editing the Python will fix it.

The setup jumper is a second constraint that is easy to forget. Injection is prevented only while GP0 is tied to GND. The README warns that if the device is not in setup mode, it reboots and runs the script after roughly half a second. That means the safe-editing state is a physical wire you have to remember to install, not a software setting, and the failure mode is your own machine receiving the payload.

USB mass storage visibility is a third trade-off, and it cuts both ways. Leaving the drive enabled makes reprogramming easy and also makes the device visible to the target as a storage device. The GND to GPIO15 jumper hides it, but the README says you then have to remove the jumper and reconnect to your PC to reprogram. On the Pico W the default is already hidden, so if you want the drive on that board you are working against the default rather than with it. The README does not document a rollback path if a payload misbehaves on the target, and it does not describe a way to abort a running script from the host.

pico-ducky against a purpose-built Rubber Ducky

The obvious alternative is the original Hak5 USB Rubber Ducky, whose payload repository and DuckyScript documentation this project points to. The difference in approach is where the work happens. A commercial Ducky is a finished device with its own toolchain and vendor support; you buy it, load a payload and use it. pico-ducky is a build: you flash CircuitPython, assemble the library dependencies by hand, copy six Python files, and manage two jumper states yourself.

That difference matters in both directions. The build gives you the full source in Python, so the interpreter in duckyinpython.py is something you can read and change, and the Pico W web service at 192.168.4.1 is a feature a stock Ducky does not offer in the same form. Against that, you inherit every dependency problem: the wrong UF2 for your board, a missing .mpy in lib, or a secrets.py you forgot to create on a Pico W all produce a board that simply does not do what you expect. The README does not describe a diagnostic mode for those cases, though DEBUG.md and RESET.md exist in the repository for troubleshooting and resetting the board.

Maintenance, licence and what an upgrade costs you

The last push to the repository was on 2026-03-18, and the most recent release is v4.0 from 2026-01-10, labelled CircuitPython 10.x Support. The two releases before it, v3.2 and v3.1, were both keyboard-layout fixes for non-US keyboards. That release history tells you where the maintenance effort has gone: keeping up with CircuitPython versions and correcting layout handling. If you depend on a non-US layout, check the v3.2 notes before assuming your keyboard is covered.

Upgrading is not a package manager operation. It means replacing the CircuitPython UF2 on the board and re-copying the library dependencies, then re-copying the project's Python files. The README pins the runtime at 10.0.3 across all four board variants, and v4.0 is the release that aligns the project with CircuitPython 10.x. A mismatch between the bundle version and the runtime is the kind of thing that shows up as an import error rather than a clear message.

The project is licensed GPL-2.0. That is a copyleft licence, and it applies to the code you copy onto the board and any modifications you distribute. If you plan to ship a product built on these files, read the licence text in the repository rather than relying on a summary; this article is not legal advice. The repository also carries a LICENSE file at the root, which is the authoritative copy.

Editorial conclusion

pico-ducky is for engineers who already own a Pico or Pico W and want a DuckyScript injector they can read and modify, and for anyone who needs the Pico W web editor at 192.168.4.1 to write payloads without unplugging the board. It is the wrong choice if your payload needs DuckyScript 3.0 features, since the README states only DuckyScript 1.0 and some of 3.0 are supported, or if you need a device that hides its USB mass storage by default on a plain Pico. Before you build, verify the CircuitPython 10.0.3 UF2 matches your exact board (pico, pico_w, pico2 or pico2_w), confirm you can copy adafruit_hid, adafruit_debouncer.mpy, adafruit_ticks.mpy, asyncio and adafruit_wsgi into lib, and check that your target keyboard layout is one of the layouts the v3.2 and v3.1 releases fixed.

Frequently asked questions

What is pico-ducky?

pico-ducky is a project that turns a Raspberry Pi Pico or Pico W into a USB Rubber Ducky style device, using CircuitPython and DuckyScript payloads. The README describes it as making a cheap but powerful USB Rubber Ducky with a Raspberry Pi Pico.

Which Pico boards does pico-ducky support?

The README gives install steps for the Pico, Pico W, Pico 2 and Pico 2W, each with its own CircuitPython 10.0.3 UF2 file. The Pico W and Pico 2W variants also add the web service and the secrets.py file for the access point.

How do I stop pico-ducky from running the payload on my own computer?

Connect pin 1 (GP0) to pin 3 (GND) with a jumper wire before plugging the board in. The README states that this setup mode stops the pico-ducky from injecting the payload into your own machine.

What DuckyScript versions does pico-ducky support?

The README states that pico-ducky currently only supports DuckyScript 1.0, and some of 3.0. Payloads that rely on DuckyScript 3.0 features outside that subset are not covered.

How do I reach the pico-ducky web service on a Pico W?

The README gives the default AP address as 192.168.4.1 and says the webservice is reachable at http://192.168.4.1:80. The available routes include /new, /ducky, /edit/<filename>, /write/<filename> and /run/<filename>, plus the API route /api/run/<filenumber>.

Official sources

  1. dbisu/pico-ducky on GitHub
  2. Issues
  3. License: GPL-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dbisu-pico-ducky.svg)](https://hysenlabs.com/projects/dbisu-pico-ducky)