Image Buildroot transformant un Raspberry Pi Zero W en outil d'injection HID type "USB Rubber Ducky" — utilisable avec ou sans HAT écran/joystick.
v1.1.18 — Release stable, validée device end-to-end. ~95 % de la spec DuckyScript 3.0 supportée. Voir
docs/core/core-cheatsheet.mdpour la couverture exhaustive.
- ⚡ Boot < 5 secondes entre alimentation et clavier USB énuméré
- 🎯 DuckyScript 3.0 complet :
STRING,IF,WHILE,VAR,FUNCTION,ATTACKMODE, etc. - ⌨️ 10 layouts clavier livrés (us, gb, fr, de, es, it, pt, br, jp, be) + override custom
- 🪪 10 profils USB device émulables (Apple, IBM, Logitech, Dell, Microsoft, Cherry...)
- 💾 Mode arming : joystick UP au boot → édit la SD comme clé USB FAT32 sans démontage
- 🔒 Air-gapped total : zéro Wi-Fi, zéro SSH, zéro UART en runtime
- 📦 Distribution
.img.zstvia GitHub releases (~24 MB compressé)
Strictement nécessaire :
| Composant | Modèle testé | Notes |
|---|---|---|
| SBC | Raspberry Pi Zero W | Pi Zero (sans Wi-Fi) compatible. Pi Zero 2 W non testé. Le Pi 3/4/400 n'est pas supporté (USB OTG géré différemment). |
| Carte SD | ≥ 256 MB, classe 10 | L'image fait ~128 MB |
| Câble USB | USB-A → micro-USB data | Le port micro-USB du milieu (USB), pas celui d'alim (PWR) |
Optionnel — pour le menu LCD interactif :
| Composant | Modèle | Câblage |
|---|---|---|
| HAT écran/joystick | Waveshare 1.3" LCD HAT (240×240, ST7789, joystick 5 positions + 3 boutons KEY1/2/3) | Pose directe sur le GPIO 40-pin du Pi |
Sans le HAT, le projet reste 100% utilisable : le mode arming (joystick UP au boot, ou simplement démonter la SD physiquement) permet d'éditer boot.conf côté PC. Les codes erreur sont signalés via la LED ACT verte (cf. section "Patterns LED" plus bas).
Récupère la dernière raspducky-vX.Y.Z.img.zst depuis les releases.
Avec Raspberry Pi Imager 1.7+ (recommandé) :
- "Use custom" → sélectionner le
.img.zstdirectement (gère zstd nativement) - Choisir la SD cible → écrire
En CLI :
zstd -d raspducky-vX.Y.Z.img.zst # → sdcard.img (~128 MB)
sudo dd if=sdcard.img of=/dev/sdX bs=4M status=progress conv=fsync
sync- Insère la SD dans le Raspberry Pi Zero W
- Connecte le Pi au PC via le port USB du milieu (data, pas le port d'alim seul)
- Attends ~5 secondes
- Le PC voit un nouveau clavier USB
- Le payload par défaut (
demos/hello-notepad.ds3) ouvre Notepad et tapeHello, World!(layout FR par défaut)
Tout passe par un seul fichier sur la SD : /payloads/boot.conf
PAYLOAD=demos/hello-notepad.ds3
PROFILE=generic_keyboard
LAYOUT=fr
INTER_KEY=5
JITTER=0
OS=AUTOTrois moyens équivalents de modifier ce fichier :
Plus besoin de démonter la SD :
- Maintiens le joystick UP (ou le bouton équivalent) sur le HAT
- Branche le Pi → la LED ACT clignote vite (signal "arming actif")
- Le PC voit une clé USB
PAYLOADS(32 MB FAT32) - Édite
boot.conf(ou ajoute des.ds3,.kbd,.dev...) avec ton éditeur favori - Éjecte proprement côté PC
- Débranche le Pi, rebranche sans toucher le joystick → mode HID normal avec tes modifs
Sors la SD, mount sur ton PC (la partition PAYLOADS est en FAT32, lisible nativement Windows/macOS/Linux), édite, démonte, réinsère.
Navigue dans le menu pour sélectionner script / profil → écrit boot.conf → propose reboot.
Selon le layout configuré sur ton PC cible :
LAYOUT=fr # France (AZERTY)
LAYOUT=us # États-Unis (QWERTY) — défaut
LAYOUT=de # Allemagne (QWERTZ)
LAYOUT=es # Espagne
LAYOUT=it # Italie
LAYOUT=gb # Royaume-Uni
LAYOUT=pt # Portugal
LAYOUT=br # Brésil
LAYOUT=jp # Japon
LAYOUT=be # BelgiquePourquoi c'est important : le clavier USB envoie des keycodes physiques, pas des caractères. Si ton PC interprète A comme Q (cas FR vs US), le payload tapera n'importe quoi sans le bon LAYOUT.
Override par payload : un .ds3 peut commencer par LAYOUT us pour forcer un layout spécifique (utile si tu télécharges des scripts Hak5 conçus pour US sur un PC FR).
Permet de faire passer le Pi pour un clavier connu (cas d'usage : test compatibilité macOS, contournement de policies USB-whitelist, BIOS legacy, démo) :
PROFILE=apple_magic_keyboard # → apparaît comme Apple Magic Keyboard
PROFILE=ibm_model_m # → IBM Enhanced Keyboard (Model M)
PROFILE=logitech_k120 # → Logitech K120 Standard
PROFILE=lenovo_thinkpad_compact # → Lenovo ThinkPad Compact USB
PROFILE=dell_kb216 # → Dell KB216 Wired
PROFILE=microsoft_sculpt # → Microsoft Sculpt Comfort
PROFILE=cherry_g80_3000 # → Cherry G80-3000 Mechanical
PROFILE=generic_keyboard # → "raspducky HID" (défaut anonyme)Vérifier côté PC après reboot HID :
| OS | Commande |
|---|---|
| Linux/macOS | lsusb → cherche VID:PID |
| Windows | Win+X → Gestionnaire de périphériques → propriétés du clavier → onglet Détails → "ID matériels" |
| Tous (GUI) | USBDeview (NirSoft, portable) |
Deux scripts prêts à l'emploi dans br-external/board/raspducky/payloads/ — bons points de départ pour comprendre la syntaxe :
| Script | Effet | Layout |
|---|---|---|
demos/hello-notepad.ds3 |
Ouvre Notepad (Win+R) et tape Hello World | FR |
wifi/info-notepad.ds3 |
Dump des profils Wi-Fi Windows + clés en clair vers un fichier puis ouvre Notepad | FR |
- Crée un fichier
.ds3dans/payloads/(sur la SD côté arming, ou édit direct PC après démontage) - Mets-le dans
boot.conf:PAYLOAD=mon-script.ds3(sous-dossiers OK :attaques/recon.ds3) - Reboot le Pi → le script est exécuté
REM_BLOCK
TITLE: Hello world
END_REM
LAYOUT fr
DELAY 1000
STRING bonjour le monde !
ENTER
DELAY 1000
GUI r
DELAY 500
STRINGLN cmd
DELAY 500
VAR $i = 0
WHILE $i < 3
IF $i == 0 THEN
STRINGLN whoami
END_IF
IF $i == 1 THEN
STRINGLN ipconfig
END_IF
IF $i == 2 THEN
STRINGLN dir
END_IF
DELAY 300
VAR $i = $i + 1
END_WHILE
| Catégorie | Exemples |
|---|---|
| Texte | STRING bonjour, STRINGLN echo (avec $var interpolation) |
| Touches | ENTER, TAB, F1-F12, ESC, BACKSPACE, DELETE, UP/DOWN/LEFT/RIGHT, HOME, END |
| Combos | GUI r, CTRL c, CTRL-ALT t, ALT TAB, HOLD SHIFT / RELEASE |
| Délais | DELAY 1000, DEFAULT_DELAY 50, JITTER 20 |
| Variables | VAR $count = 0, VAR $sum = $a + $b * 2 |
| Conditions | IF $x == 5 THEN ... ELSE ... END_IF |
| Boucles | WHILE $i < 10 ... END_WHILE |
| Fonctions | FUNCTION my_func ... END_FUNCTION puis my_func |
| Configuration | LAYOUT fr, ATTACKMODE HID VID_05AC PID_024F, LED_ON |
| Commentaires | REM ..., REM_BLOCK ... END_REM |
Doc complète : docs/core/core-duckyscript.md
| LED ACT | Cadence | Signification |
|---|---|---|
mmc0 (irrégulier) |
activité SD | Boot HID nominal |
| timer 150ms on/off (rapide régulier) | 3 Hz | Mode arming actif — clé USB exposée |
| timer 100ms on/off (très rapide) | 5 Hz, agressif | Erreur fatale — boot.conf absent / corrompu / gadget HID KO |
| timer 300/700ms (asymétrique pulsé) | lent | Erreur soft — payload introuvable / parse error |
| timer 500ms on/off (régulier lent) | 1 Hz | Warning — fallback PROFILE automatique |
L'utilisateur reconnaît visuellement le problème par la cadence sans LCD.
| Couche | Choix | Pourquoi |
|---|---|---|
| OS de base | Buildroot 2025.02.13 LTS (submodule) | Boot < 5 s, pas de systemd, kernel custom |
| PID 1 | raspducky-init en C statique (~570 KB stripped) |
Boot path ~600 ms, zéro dépendance shared |
| Runtime payload | Python 3.10+ (ducky pipeline complet) | Lexer/Parser/Analyzer/Runtime, ~190 tests pytest. CLI raspducky-play. |
| UI LCD | Pillow + ST7789 SPI direct sur /dev/spidev0.0 |
7 écrans v1.1.0 : 5 config + 2 splash. Pas de driver kernel. |
| Inputs | /dev/gpiochip0 ioctl direct (C init) + RPi.GPIO polling 50ms (UI Python) |
Zéro dep externe pour init, polling robuste pour UI |
| Build | Buildroot + BR2_EXTERNAL (br-external/) |
Standard, reproductible |
| CI/CD | Gitea Actions (3 workflows) | tests / image / release |
3 partitions sur la SD :
/dev/mmcblk0
├── p1 FAT32 32 MB "boot" bootloader Pi + kernel + DTB
├── p2 ext4 ~64 MB (rootfs) Buildroot + Python + libs (read-only)
└── p3 FAT32 32 MB "PAYLOADS" boot.conf, *.ds3, layouts/, devices/ (RW)
raspducky/
├── br-external/ # BR2_EXTERNAL : configs, packages, board files
│ ├── configs/raspducky_defconfig
│ ├── package/ # raspducky-init (C) + raspducky (Python)
│ └── board/raspducky/ # genimage.cfg, post-build.sh, payloads/, devices/, keyboard-layouts/
├── src/
│ ├── init/ # raspducky-init (C statique, PID 1)
│ └── raspducky/ # raspducky (Python applicatif)
│ ├── raspducky/
│ │ ├── ducky/ # Lexer/Parser/Analyzer/Runtime DuckyScript 3.0
│ │ ├── lcd/ # Driver ST7789 SPI direct
│ │ ├── ui/ # Renderer + 7 écrans (v1.1.0)
│ │ ├── app/ # State machine + boucle d'événements
│ │ ├── inputs/ # InputReader joystick + KEY1/2/3
│ │ ├── bootconf.py # Parser/serializer boot.conf (atomique)
│ │ ├── loaders.py # Énumération layouts/profiles/payloads
│ │ ├── boot_mode.py # Lecture /run/raspducky/boot_state
│ │ └── ...
│ └── tests/ # ~307 tests pytest
├── scripts/ # gen-changelog, gen-manifest, gen-keyboard-layouts, ...
├── docs/ # Documentation atomique (atomes)
│ ├── core/ # Logique métier (DuckyScript, arming flow, boot.conf)
│ ├── archi/ # Architecture (USB gadget, image build, layouts, ...)
│ └── management/ # workflow.md, retex.md, TODO.md
├── buildroot/ # Submodule Buildroot 2025.02.13
└── .gitea/workflows/ # CI/CD
git clone --recurse-submodules https://github.com/NimpNaw/raspducky.git
cd raspducky/buildroot
make BR2_EXTERNAL=../br-external raspducky_defconfig
make -j$(nproc)
# → buildroot/output/images/sdcard.img (~128 MB)Build complet : ~30-40 min première fois (toolchain + kernel + Python). Rebuild incrémental d'init C : ~5 s.
cd src/raspducky
python3 -m venv venv
source venv/bin/activate
pip install -e ".[test]"
pytest -v --cov=raspducky
# → ~307 tests, ~3spython3 scripts/check-kernel-config.py# Télécharge depuis le repo Hak5 community + convertit en .kbd
python3 scripts/gen-keyboard-layouts.py --only ca-fr ch dkDocumentation atomique (un fichier par concept) dans docs/ :
- Métier :
core-boot-conf,core-arming-flow,core-duckyscript - Architecture :
archi-storage-layout,archi-usb-gadget,archi-keyboard-layouts,archi-device-profiles,archi-interpreter,archi-error-handling - Index :
docs/index.md
Ce projet produit un outil d'injection HID. Usage personnel / pédagogique / pentest perso / test compatibilité uniquement. Voir LICENSE GPLv3 §7 (clause de non-garantie).
L'utilisateur final est seul responsable de l'usage qu'il fait de l'image.