🐰 A working, fully-open port of the Hak5 Bash Bunny MK2 firmware (v1.7) onto a Raspberry Pi Zero 2 W + Waveshare 1.3" LCD HAT (ST7789 240x240, joystick + 3 keys) that acts as both the status "LED" and the control UI. Same Duckyscript engine, same
ATTACKMODEmodes, same VID/PID table — now with a nicer retro neon on-device menu and a write-your-own payload UI.
- Full Duckyscript 1.0 → 3.0 engine (
QUACK) —DEFINE,VAR/CONST,IF/ELIF/ELSE,WHILE,FUNCTION,STRINGLN,EXTENSION,WAIT_FOR_BUTTON_PRESS,STOP_PAYLOAD,HIDE_PAYLOAD,$_RANDOM_*, keyboard-lock-state helper functions, and more. - USB gadget re-implemented in configfs (
arm.py) on the Pi's DWC2 controller — HID keyboard, Mass Storage, RNDIS/ECM ethernet, CDC-ACM serial. - Seen as a plain USB keyboard on Linux / Windows / macOS — standard boot-protocol HID report descriptor (63 bytes), no drivers needed.
- Retro neon LCD UI with a scrollable payload library: joystick to navigate, K3 = OK (run), K2 = BACK — pick from 16 payloads without re-flashing.
- On-device screenshot-friendly status "LED" (the LCD renders
LEDstates). - 16 ready payloads: net recon, HID pwn, sys/cred/browser recon, wifi grab,
keylogger, hunter, ducky_runner, plus Hak5 ports — and a
hid_testpayload that proves keyboard enumeration on any OS in one go.
- Raspberry Pi Zero 2 W (also works on Zero W — anything with DWC2).
- Waveshare 1.3" LCD HAT (ST7735 128x128 / ST7789 240x240).
Reused 1:1 from upgrade/cherry.rootfs.tar:
- Duckyscript engine language files (
usr/local/bunny/lib/languages/*.json) - Payload extensions (
QUACKhelpers:GET,RUN,SETKB,DUCKY_LANG,WAIT*,DEBUG,requiretool, ...) - Built-in payloads,
config.txt, version, docs, Windows CDC-ACM inf - The
ATTACKMODEmode/PID semantics (same VID 0xF000 / PID table)
Rewritten for the Pi (DWC2 + configfs instead of the sunxi bunny_gadget.ko):
| Stock bunny | PiBunny |
|---|---|
bunny_gadget.ko (insmod) |
lib/gadget/arm.py (configfs composite gadget) |
| 3-position slide switch | Waveshare keys: KEY1/KEY2/KEY3 = switch1/2/3 |
| RGB status LED | LCD renders the LED command states |
| — | Joystick payload-select menu (new) |
/dev/nandf udisk |
loop image /opt/bunny/udisk.img |
| py2 QUACK / hid_reader | Python 3 ports |
- Flash Raspberry Pi OS Lite (32-bit / armhf) onto an SD card. (The UI drives the LCD directly from the kernel's SPI interface — no desktop needed and boot is fast.)
- Copy this
rpi-bunny/folder onto the Pi (e.g. via thebootpartition orscp), then run asroot:sudo ./install.sh sudo reboot
install.shinstallspython3-spidev,python3-libgpiod,dnsmasq, installs/usr/local/bunny, builds the udisk image, adds the systemd services and edits/boot*/config.txt+cmdline.txt(dwc2 peripheral gadget, SPI on, key/joystick pull-ups).
Nothing to wire by hand — the HAT stacks directly. Pin map used by the port:
| Function | BCM GPIO |
|---|---|
| LCD SPI (CLK/MOSI/CS) | 11 / 10 / 8 |
| LCD DC / RST / BL | 25 / 27 / 24 |
| KEY1 / KEY2 / KEY3 | 21 / 20 / 16 |
| Joystick UP/DOWN/LEFT/RIGHT/PRESS | 6 / 19 / 5 / 26 / 13 |
All keys/joystick are active-low with internal pull-ups.
Display driver: the port uses spidev + libgpiod from userspace, so no
kernel fbtft/DRM driver is required and it keeps full control of the panel
(it also lets us GPU-free render a status "LED" + menus).
On boot the screen shows the payload selector (retro neon list). The
selection is a single scrollable list driven from .menucfg — every payload
is a row, plus ARM (arming mode), LOOT, SYS, CAL and BACK tools:
- Joystick UP / DOWN — move the highlight
- K3 (OK) or joystick press — run / open the highlighted row
- K2 (BACK) — back out of a tool or running payload to the selector
Each row shows the actual payload name pulled from .menucfg, so what you
highlight is what runs — no more guessing which switch fires which attack.
Payloads live on the udisk (mounted at /root/udisk), exactly like stock:
payloads/switch1/payload.txt (your own ducky scripts per switch)
payloads/switch2/payload.txt
payloads/switch3/payload.txt (arming/setup fallback)
payloads/extensions/*.sh (QUACK helper functions)
payloads/library/*.txt (selectable from the on-screen library menu)
config.txt (e.g. DUCKY_LANG us)
loot/ (exfil directory)
tools/ (optional .deb / scripts run at boot in arming)
Payload files are mapped to the on-screen menu with .menucfg:
switch1 library/net_recon.txt
switch2 library/hid_pwn.txt
switch3 library/sys_recon.txt
...
switch16 library/hid_test.txt
Inside a payload it's stock-bunny syntax (a bash file that calls ATTACKMODE
and QUACK):
LED SETUP # screen goes magenta
ATTACKMODE HID STORAGE RNDIS_ETHERNET
QUACK GUI r
QUACK DELAY 300
QUACK STRING notepad
QUACK ENTER
QUACK DELAY 1000
QUACK STRING hello from pibunny
LED FINISHSupported ATTACKMODE modes and 1:1 same VIDs/PIDs:
HID STORAGE SERIAL RNDIS_ETHERNET ECM_ETHERNET AUTO_ETHERNET ARMING,
plus VID_0x… PID_0x… SN_ MAN_ RNDIS_SPEED_ ETHERNET_TIMEOUT_ overrides.
RNDIS/ECM get IP 192.168.1.1/24 on usb0 and a DHCP server hands the
target 192.168.1.x, so the usual GET TARGET_IP etc. work over
sock.sh/port forwarding like on stock.
rpi-bunny/
├── install.sh # Pi-side installer
├── build_udisk.sh # creates /opt/bunny/udisk.img (empty drive)
├── udisk/ # udisk skeleton: payloads, extensions, config...
├── src/usr/local/bunny/
│ ├── bin/ # ATTACKMODE, LED, QUACK, bunny_framework, udisk...
│ ├── lib/gadget/arm.py # configfs composite gadget (replaces the .ko)
│ ├── lib/languages/ # keyboard layouts (from stock firmware)
│ ├── ui/ # hardware.py, font.py, ui.py (LCD + input daemon)
│ └── hid_reader.py # HID LED/numlock report reader (py3)
└── src/etc/ # systemd units, dnsmasq/usb0 config
Do this once, in order, while the Pi is not plugged into a target yet:
- Boot + screen: after
reboot, the panel should show a splash then SELECT SWITCH (magenta). No screen?systemctl status bunny-ui— the common cause ispython3-spidev(install it) ordtoverlay=dwc2taking SPI (remove it);ls /dev/spidev0.0must succeed. - Keys / joystick on the picker: K1/K2/K3 should select that switch and
start running it (screen goes to RUN with a color), joystick press opens
the payload menu;
seling and committing runs the payload. Checkcat /run/bunny/switchand/run/bunny/led.state. - Gadget sanity before touching a host:
dmesg | grep -i -E 'dwc2|gadget'andls /sys/kernel/config/usb_gadget/pibunnyshould exist after anyATTACKMODEcall. RunATTACKMODE ARMING— the Pi should present itself as mass storage + serial (used forudisk/factory_reset_bunny). - HID: plug into a test machine. It must enumerate as keyboard
(VID_0xf000/PID_0xff01 after
ATTACKMODE HID). Type-capture:QUACK STRING helloshould type into notepad/editor. If nothing types, checkreport_length= 8 and that/dev/hidg0exists. - Altcode path (
ALTCODE/ numlock detection): run a payload usingALTCODE— the num-lock toggle only works whilehid_readeris running (systemctl status bunnyshows the framework; the bytes land in/tmp/hid_out). - Ethernet:
ATTACKMODE HID STORAGE RNDIS_ETHERNET, plug into Windows (installwin7-win8-cdc-acm.inffrom the udisk as needed) or macOS/Linux (ECM +ECM_ETHERNET). The Pi should get192.168.1.1onusb0and the target a lease in192.168.1.x;GET TARGET_IPthen works.
QUACKwrites to/dev/hidg0(f_hid). The HID report descriptor includes the keyboard LED output report soALTCODE/numlock detection still works.- The framework runs one attack per boot (like stock). Reboot (or
systemctl start bunny.service) to pick a different switch; the joystick menu persists payload assignments in/root/udisk/.menucfg. - The udisk image is both mounted at
/root/udiskand exposed as Mass Storage — same dual-use as the original/dev/nandfmount. Keep writes to the disk on one side at a time for clean sync. init_bt(Bluetooth serial module) is stubbed out — this is not the wireless bunny.- For lab testing that's authorized: this is a USB attack device. Use it only on systems you own or are explicitly permitted to test.