The Problem

Operators need a low‑cost, Wi‑Fi‑controlled BadUSB that can be programmed on‑the‑fly without moving SD cards or reinstalling firmware. Existing BadUSB kits require manual flashing after each script change, which slows testing and deployment in red‑team exercises.

What This Does

WiFi Duck turns an ATmega32u4 or ESP8266 board into a wireless HID keyboard. The firmware (see atmega_duck/atmega_duck.ino and esp_duck/esp_duck.ino) runs a small web server (esp_duck/webserver.cpp) that serves the UI in web/. Scripts written in the DuckScript language are stored in the ESP’s SPIFFS (esp_duck/spiffs.cpp) and executed by the parser (esp_duck/duckscript.cpp). The same source tree also contains a desktop‑side converter (webconverter.py) that translates raw scripts into the binary format the device expects.

How It Is Wired

Entry points

  • atmega_duck/atmega_duck.ino – Arduino sketch for ATmega32u4 boards.
  • esp_duck/esp_duck.ino – Arduino sketch for ESP8266 boards.
  • web/index.html / web/index.js – browser UI loaded from the device.

Core flow (ESP variant, the most feature‑complete)

  1. Bootesp_duck.ino calls setup()WebServer::begin() (in esp_duck/webserver.cpp).
  2. Web UIwebserver.cpp registers HTTP handlers that serve files from web/ (e.g., index.html, script.js).
  3. Script upload – UI posts a script to /upload. The handler invokes Spiffs::writeFile() (esp_duck/spiffs.cpp) to persist the script in SPIFFS.
  4. Execution request – UI triggers /run. Handler calls DuckScript::run() (esp_duck/duckscript.cpp).
  5. ParsingDuckScript::run() creates a DuckParser (esp_duck/duckscript.cpp includes duckparser.cpp from the ATmega side) which tokenises the script and maps tokens to HID reports using tables in usb_hid_keys.h.
  6. Output – Parsed commands are sent to the USB HID stack via Keyboard::press() calls (implemented in atmega_duck/keyboard.cpp for ATmega and mirrored in ESP code).

Responsibility map

FilePrimary role
esp_duck/webserver.cppHTTP server, routes UI actions
esp_duck/spiffs.cppPersistent storage of scripts
esp_duck/duckscript.cppScript interpreter entry point
atmega_duck/duckparser.cppTokenisation and command mapping
atmega_duck/keyboard.cppLow‑level HID report generation
web/*.js & web/*.htmlFront‑end UI, JSON API calls
webconverter.pyOffline conversion of raw scripts to binary payload

The import graph shows no circular dependencies; each module is a leaf or a single‑direction consumer, so changes in webserver.cpp affect only the HTTP layer, while duckparser.cpp is the widest‑impact module (used by both ATmega and ESP builds).

How To Use It

# 1. Clone the repo
git clone https://github.com/moses-y/WiFiDuck.git
cd WiFiDuck

# 2. Open the appropriate sketch in the Arduino IDE
#    – ATmega32u4 board: atmega_duck/atmega_duck.ino
#    – ESP8266 board:    esp_duck/esp_duck.ino
#    Select the correct board type and upload.

# 3. After flashing, the device creates Wi‑Fi AP “wifiduck” (default password “wifiduck”).
#    Connect a client PC to that network.

# 4. Open a browser to http://192.168.4.1 .
#    The UI loads from web/*.html and web/*.js served by the firmware.

# 5. Write a DuckScript in the “Script” pane, click **Save** → it is stored via SPIFFS.
#    Click **Run** to execute the keystrokes on the host computer.

# Optional: Convert a raw script to binary with the supplied tool
python3 webconverter.py input.duck output.bin

Configuration files are hard‑coded in the sketches (esp_duck/config.h and atmega_duck/config.h). Changing SSID/password requires editing esp_duck/settings.cpp and re‑flashing, or using the UI’s Settings page which writes to SPIFFS and updates the runtime config.

Real‑World Use

A penetration tester deploys a DSTIKE ESP8266 board on a target’s USB hub. After a quick flash, they walk to the target site, connect a laptop to the AP, load a credential‑harvesting script via the browser UI, and trigger it when the victim plugs in the hub. The entire workflow takes minutes and leaves no visible storage media on the target.

Code Health & Issues

  • HIGH – No test suite – 20 source files, zero test files.
  • HIGH – No CI pipeline – No GitHub Actions or other build automation present.
  • LOW – Missing convention files – No .editorconfig, .gitattributes, or formatter config.
  • HIGH – Deep nestingesp_duck/com.cpp, atmega_duck/duckparser.cpp, esp_duck/cli.cpp each have indentation depth 8, making logic hard to follow.
  • HIGH – Duplicated code – Repeated 6‑line blocks across 25 files (e.g., Adafruit_DotStar.*, NeoPixel.*, com.*).
  • MEDIUM – Resource safetywebconverter.py opens files without a context manager, risking leaks on exception.
  • MEDIUM – High branching densityatmega_duck/Adafruit_DotStar.cpp, atmega_duck/duckparser.cpp, atmega_duck/parser.c contain >100 branch points in <400 lines.

The Bottom Line

WiFi Duck provides a functional, Wi‑Fi‑controlled BadUSB platform with a ready‑to‑use web UI and clear separation between firmware, storage, and parsing. The codebase suffers from maintainability problems (deep nesting, duplicated logic) and lacks basic engineering scaffolding (tests, CI, formatting rules). It is suitable for security researchers comfortable with Arduino tooling who can tolerate ad‑hoc code quality, but teams looking for a production‑grade, maintainable BadUSB framework will need to invest in refactoring and test infrastructure.