Introduction
Bonjour à tous, dans cet article nous allons voir comment il est possible de créer sa propre Rubber Ducky.
Une Rubber Ducky, pour ceux qui ne le savent pas, est une clé USB capable d'émuler le comportement de périphériques connectés à votre ordinateur tels qu'un clavier ou une souris.
Ici nous émulerons un clavier pour générer des séquences de touches et voir comment les hackers pratiquent les Keystroke Injections
Pré-Requis
Voici ce dont on va avoir besoin pour créer notre objet :
- Un IDE (Thonny de préférence)
- Des bases en Python
- Un gadget supportant Micropython (généralement ESP32, RP2040, RP2350, etc...)
- Un cable supportant le chargement de données
Pour ceux qui ne souhaitent pas aller chercher dans l'aspect technique des micro-firmwares, achetez un Raspberry Pi Pico 1 ou 2 ça fera parfaitement l'affaire et c'est ce qu'on va utiliser ici.
Ca coute 4 à 6€ c'est pas excessif.
Micropython & Config Gadget
Micropython est une version simplifée de Python faite pour travailler avec les micro-programmes ce langage permet de faire tout un tas de choses, pour l'utiliser avec votre gadget, allez chercher le firmware correspondant au gadget que vous avez acheté sur ce lien :
Pour utiliser Micropython sur votre Raspberry Pico, c'est très simple voici le procédé :
- Branchez votre appareil sur votre ordinateur en maintenant le bouton blanc BOOTSEL (Ca fera entrer votre Raspbery en mode File Storage)
- Vous devriez voir apparaître un nouveau disque dur sur votre ordinateur.
- Allez chercher le fichier .uf2 téléchargé sur le site de Micropython puis copiez-le dans le disque du Raspberry Pico
- Si tout a fonctionné correctement la page d'explorateur de fichier devrait se fermer ça veut dire que votre Pico redémarre
- Débranchez le puios rebranchez le sans appuyer sur le bouton cette fois-ci. Vous ne verrez pas apparaître de disque dur mais c'est tout a fait normal parceque maintenant il est branché en mode Serial
Config de l'éditeur
Pour développer sur des gadgets il faut pouvoir communiquer avec eux via leur port de série. A savoir que Thonny est vraiment beaucoup plus pratique que VScode pour ce type de programmation.
1. Thonny
Installez-le ici
- Allez dans le menu Outils > Options.
- Dans l'onglet Interpréteur, choisissez MicroPython (Raspberry Pi Pico) dans la liste déroulante des interpréteurs.
- Assurez-vous que le port série est correctement sélectionné. Si votre Raspberry Pi Pico est correctement connecté, Thonny devrait détecter automatiquement le port.
Si jamais Thonny ne trouve pas le port vous pouvez le trouver de la manière suivante, puis l'entrer manuellement :
Linux : dmesg | grep tty
Vous verrez un ttyACM0
ou ttyUSB0
, c'est le port de série de votre Pico.
Windows : Ouvrez un PowerShell puis tappez -> Get-WMIObject Win32_SerialPort
. Vous verrez pleins d'informations et à un endroit vous devriez voir une ligne comme celle-ci :
CreationClassName : Win32_SerialPort
Description : Périphérique série USB
DeviceID : COM3
COM3 c'est le port de série de votre Pico sur Windows.
Sur Mac, utilisez ls /dev/tty.usbmodem*
Et voilà vous êtes tout prêt. Vous devriez voir sur la barre latérale gauche votre système de fichier de l'ordinateur en haut et celui du pico (vide pour l'instant) en bas.
2. VSCode
Installation de VS Code et des extensions nécessaires
-
Télécharger et installer VS Code
- Rendez-vous sur code.visualstudio.com et installez la dernière version.
-
Installer l'extension MicroPico
- Ouvrez VS Code
- Allez dans l'onglet "Extensions" (Ctrl + Shift + X)
- Recherchez "MicroPico" et installez-le
Configuration de VS Code pour MicroPython
-
Configurer le port série
- Branchez le Pico et identifiez son port :
- Sur Windows, ouvrez le "Gestionnaire de périphériques" et cherchez le port sous "Ports (COM et LPT)" ou sinon dans PowerShell :
Get-WMIObject Win32_SerialPort
- Sur Linux, utilisez
dmesg | grep tty
- Sur Mac, utilisez
ls /dev/tty.usbmodem*
- Sur Windows, ouvrez le "Gestionnaire de périphériques" et cherchez le port sous "Ports (COM et LPT)" ou sinon dans PowerShell :
- Branchez le Pico et identifiez son port :
-
Configurer l'extension MicroPico
- Ouvrez la palette de commande (Ctrl + Shift + P)
- Tapez
MicroPico: Configure Global Settings
- Sélectionnez le port détecté
-
Téléverser et exécuter le script
- Ouvrez la palette de commande (Ctrl + Shift + P)
- Tapez
MicroPico: Upload Current File to Device
- Pour ouvrir une console REPL :
MicroPico: Open REPL
Votre Raspberry Pi Pico est maintenant configuré pour être utilisé avec VS Code et MicroPython ! 🚀
3. Tester la configuration
Informations
Par défault le firmware .uf2 va chercher a exécuter les fichiers dans un certain ordre sur Micropython :
- D'abord le boot.py si vous le créez assurez vous qu'il ne bloquera pas le reste du code sinon il faudra re-flasher le firmware et recommencer
- Ensuite le main.py, en général c'est par celui-ci qu'on va commencer
- Pour les modules le dossier /lib/ est reconnu automatiquement
Tester un script MicroPython
On va écrire un script qui va allumer et éteindre la LED intégrée au Pico en boucle pour voir si ça fonctionne correctement.
- Créer un fichier
main.py
from machine import Pin import time led = Pin(25, Pin.OUT) # LED intégrée sur Raspberry Pi Pico while True: # Boucle pour allumer et éteindre la LED led.toggle() time.sleep(1)
Si ce script fonctionne on peut passer à la suite
Transformer Notre Clé USB en Clavier
Installer les librairies
L'organisme qui a créé micropython à aussi créé des librairies parcequ'ils sont super cools et il y en a une qui va permettre à notre Raspberry d'être detecté comme un périphérique type ***HID (Human Interface Device)
**Voici comment les utiliser : **
- Cloner le dépôt Github :
git clone https://github.com/micropython/micropython-lib.git
- Un dossier micropython-lib ca être ajouté sur votre ordinateur.
- Allez sur votre IDE et dans le Raspberry créez un dossier /lib
- Allez dans le dossier micropython-lib/micropython vous y verrez un dossier usb. Copiez dans le dossier lib de votre Pico.
Maintenant nous avons la librairie USB c'est parfait on va pouvoir l'utiliser !
Keyboard Script
On va devoir utiliser les fonctionnalités de ces librairies dans notre main.py pour qu'au branchement notre Pico exécute notre séquence de touches,
Dans votre main commencez par importer les modules nécessaires :
import usb.device # Librairie Micropython
from usb.device.keyboard import KeyboardInterface # Module pour être detecté comme clavier
import time # Librairie pour mettre des délais necessaires
Ensuite il faudra initialiser le périphérique en tant que clavier grâce a la class KeyboardInterface pour qu'il soit reconnu comme clavier et puisse utiliser les touches :
class MyKeyboard(KeyboardInterface):
def on_led_update(self, led_mask):
pass
Maintenant que notre périphérique est initialisé comem clavier on va pouvoir passer au code qui nous interresse :
def BadUsb(): # Fonction qui va contenir tout le comportement de notre clavier
k = MyKeyboard() # On récupère le clavier dans une variable
usb.device.get().init(k, builtin_driver=True) # On charge ses modules
# Boucle de vérification, si le clavier n'est pas initialisé le pico ne fera rien.
while not k.is_open():
time.sleep(0.01)
Pour gagner un maximum de temps avant de passer à la suite de notre main.py
on va aller créer notre propre module dans /lib/utils.py (nommez le comme vous voulez pas obligé que ce soit utils)
Redaction du lib/utils.py
from usb.device.keyboard import KeyboardInterface, KeyCode
import time
import os
# Fonction pour lire la configuration utilisateur qu'on va faire après
def read_config(file_path):
config = {}
with open(file_path, 'r') as file:
for line in file:
# Ignorer les lignes vides ou les commentaires
if line.strip() and not line.strip().startswith("#"):
key, value = line.strip().split("=", 1)
config[key.strip()] = value.strip()
return config
# On va mapper les touches pour que les variables soit moins longues et pour pouvoir adapter le comportement du Pico selon la disposition clavier (Qwerty, Azerty)
#Utils
SPACE = KeyCode.SPACE
BACKSPACE = KeyCode.BACKSPACE
ENTER = KeyCode.ENTER
ESCAPE = KeyCode.ESCAPE
TAB = KeyCode.TAB
LSHIFT = KeyCode.LEFT_SHIFT
RSHIFT = KeyCode.RIGHT_SHIFT
LALT = KeyCode.LEFT_ALT
RALT = KeyCode.RIGHT_ALT
LCTRL = KeyCode.LEFT_CTRL
RCTRL = KeyCode.RIGHT_CTRL
RIGHT = KeyCode.RIGHT
LEFT = KeyCode.LEFT
DOWN = KeyCode.DOWN
UP = KeyCode.UP
#<> for Azerty FR
INFERIOR = 100
SUPERIOR = (KeyCode.LEFT_SHIFT, INFERIOR)
#Miscellanous
LINUX_TERMINAL = (LCTRL, LALT, KeyCode.T)
MAC_TERMINAL = (KeyCode.LEFT_UI, SPACE)
WINDOWS_POWERSHELL = (KeyCode.LEFT_UI)
#Debug
#Mappage
azertyFR = {
#Letters
'a': KeyCode.Q, 'b': KeyCode.B, 'c': KeyCode.C, 'd': KeyCode.D,
'e': KeyCode.E, 'f': KeyCode.F, 'g': KeyCode.G, 'h': KeyCode.H,
'i': KeyCode.I, 'j': KeyCode.J, 'k': KeyCode.K, 'l': KeyCode.L,
'm': KeyCode.SEMICOLON, 'n': KeyCode.N, 'o': KeyCode.O, 'p': KeyCode.P,
'q': KeyCode.A, 'r': KeyCode.R, 's': KeyCode.S, 't': KeyCode.T,
'u': KeyCode.U, 'v': KeyCode.V, 'w': KeyCode.Z, 'x': KeyCode.X,
'y': KeyCode.Y, 'z': KeyCode.W,
#Special Chars
'à': KeyCode.N0, '&': KeyCode.N1, 'é': KeyCode.N2, '"': KeyCode.N3,
"'": KeyCode.N4, '(': KeyCode.N5, '-': KeyCode.N6, 'è': KeyCode.N7,
'_': KeyCode.N8, 'ç': KeyCode.N9, ')': KeyCode.MINUS, '=': KeyCode.EQUAL, '+': (KeyCode.LEFT_SHIFT, KeyCode.EQUAL),
#Numbers
'1': (KeyCode.LEFT_SHIFT, KeyCode.N1), '2': (KeyCode.LEFT_SHIFT, KeyCode.N2), '3': (KeyCode.LEFT_SHIFT, KeyCode.N3),
'4': (KeyCode.LEFT_SHIFT, KeyCode.N4), '5': (KeyCode.LEFT_SHIFT, KeyCode.N5), '6': (KeyCode.LEFT_SHIFT, KeyCode.N6),
'7': (KeyCode.LEFT_SHIFT, KeyCode.N7), '8': (KeyCode.LEFT_SHIFT, KeyCode.N8), '9': (KeyCode.LEFT_SHIFT, KeyCode.N9),
'0': (KeyCode.LEFT_SHIFT, KeyCode.N0),
#Utils & Special Chars
'´': (KeyCode.RIGHT_ALT, KeyCode.N1), '~': (KeyCode.RIGHT_ALT, KeyCode.N2), '#': (KeyCode.RIGHT_ALT, KeyCode.N3),
'{': (KeyCode.RIGHT_ALT, KeyCode.N4), '[': (KeyCode.RIGHT_ALT, KeyCode.N5), '|': (KeyCode.RIGHT_ALT, KeyCode.N6),
'`': (KeyCode.RIGHT_ALT, KeyCode.N7), '\\': (KeyCode.RIGHT_ALT, KeyCode.N8), '^': (KeyCode.RIGHT_ALT, KeyCode.N9),
'@': (KeyCode.RIGHT_ALT, KeyCode.N0), ']': (KeyCode.RIGHT_ALT, KeyCode.MINUS), '}': (KeyCode.RIGHT_ALT, KeyCode.EQUAL),
'/': (KeyCode.LEFT_SHIFT, KeyCode.DOT),
' ': KeyCode.SPACE,
'.': (KeyCode.LEFT_SHIFT, KeyCode.COMMA),
',': KeyCode.M,
';': KeyCode.COMMA,
':': KeyCode.DOT,
'!': KeyCode.SLASH,
'?': (KeyCode.LEFT_SHIFT, KeyCode.M),
'$': KeyCode.CLOSE_BRACKET,
'>': SUPERIOR,
'<': INFERIOR,
'*': KeyCode.BACKSLASH,
}
qwertyUS = {
# Letters
'a': KeyCode.A, 'b': KeyCode.B, 'c': KeyCode.C, 'd': KeyCode.D,
'e': KeyCode.E, 'f': KeyCode.F, 'g': KeyCode.G, 'h': KeyCode.H,
'i': KeyCode.I, 'j': KeyCode.J, 'k': KeyCode.K, 'l': KeyCode.L,
'm': KeyCode.M, 'n': KeyCode.N, 'o': KeyCode.O, 'p': KeyCode.P,
'q': KeyCode.Q, 'r': KeyCode.R, 's': KeyCode.S, 't': KeyCode.T,
'u': KeyCode.U, 'v': KeyCode.V, 'w': KeyCode.W, 'x': KeyCode.X,
'y': KeyCode.Y, 'z': KeyCode.Z,
# Numbers (direct keys)
'1': KeyCode.N1, '2': KeyCode.N2, '3': KeyCode.N3, '4': KeyCode.N4,
'5': KeyCode.N5, '6': KeyCode.N6, '7': KeyCode.N7, '8': KeyCode.N8,
'9': KeyCode.N9, '0': KeyCode.N0,
# Shifted Numbers (special characters)
'!': (KeyCode.LEFT_SHIFT, KeyCode.N1), '@': (KeyCode.LEFT_SHIFT, KeyCode.N2),
'#': (KeyCode.LEFT_SHIFT, KeyCode.N3), '$': (KeyCode.LEFT_SHIFT, KeyCode.N4),
'%': (KeyCode.LEFT_SHIFT, KeyCode.N5), '^': (KeyCode.LEFT_SHIFT, KeyCode.N6),
'&': (KeyCode.LEFT_SHIFT, KeyCode.N7), '*': (KeyCode.LEFT_SHIFT, KeyCode.N8),
'(': (KeyCode.LEFT_SHIFT, KeyCode.N9), ')': (KeyCode.LEFT_SHIFT, KeyCode.N0),
# Special characters (direct keys)
'-': KeyCode.MINUS, '=': KeyCode.EQUAL, '[': KeyCode.OPEN_BRACKET, ']': KeyCode.CLOSE_BRACKET,
'\\': KeyCode.BACKSLASH, ';': KeyCode.SEMICOLON, "'": KeyCode.QUOTE,
'`': KeyCode.GRAVE, ',': KeyCode.COMMA, '.': KeyCode.DOT, '/': KeyCode.SLASH,
# Shifted special characters
'_': (KeyCode.LEFT_SHIFT, KeyCode.MINUS), '+': (KeyCode.LEFT_SHIFT, KeyCode.EQUAL),
'{': (KeyCode.LEFT_SHIFT, KeyCode.OPEN_BRACKET), '}': (KeyCode.LEFT_SHIFT, KeyCode.CLOSE_BRACKET),
'|': (KeyCode.LEFT_SHIFT, KeyCode.BACKSLASH), ':': (KeyCode.LEFT_SHIFT, KeyCode.SEMICOLON),
'"': (KeyCode.LEFT_SHIFT, KeyCode.QUOTE), '~': (KeyCode.LEFT_SHIFT, KeyCode.GRAVE),
'<': (KeyCode.LEFT_SHIFT, KeyCode.COMMA), '>': (KeyCode.LEFT_SHIFT, KeyCode.DOT),
'?': (KeyCode.LEFT_SHIFT, KeyCode.SLASH),
# Miscellaneous
' ': KeyCode.SPACE,
}
### Ensuite on va faire 2 fonctions
# La premiere stringSeq pour pouvoir écrire des chaines de caractères qui soit comprises et retransmises ça évite de faire des tableaux à rallonge comme [KeyCode.H, KeyCode.E, etc.......]
# La deuxième sendKeys pour automatiser l'envoi des touches
def stringSeq(string):
config = read_config("config.txt")
language = config["lang"]
keycodes = []
if language == "FR":
for char in string:
if char.isupper():
keycodes.append((KeyCode.LEFT_SHIFT, azertyFR[char.lower()]))
elif char in azertyFR:
key = azertyFR[char]
if isinstance(key, tuple): # Cas des combinaisons (SHIFT, etc.)
keycodes.append(key)
else:
keycodes.append((key,))
else:
raise ValueError(f"Caractère Incompatible :")
elif language == "US":
for char in string:
if char.isupper():
keycodes.append((KeyCode.LEFT_SHIFT, qwertyUS[char.lower()]))
elif char in qwertyUS:
key = qwertyUS[char]
if isinstance(key, tuple): # Cas des combinaisons (SHIFT, etc.)
keycodes.append(key)
else:
keycodes.append((key,))
else:
raise ValueError(f"Caractère Incompatible :")
return keycodes
#Function to Send Keys Properly
def sendKeys(seq, parameter, timesleep):
for key in seq:
if isinstance(key, tuple):
parameter.send_keys(list(key))
elif isinstance(key, str): # Si la clé est une chaîne (caractère Unicode)
parameter.send_keys([key])
else:
parameter.send_keys([key])
time.sleep(timesleep)
parameter.send_keys([])
time.sleep(timesleep)
Création de la config.txt
A la racine de votre appareil créez un fichier config.txt (qui sera lu grâce a la fonction de read_config) elle va nous permettre de préciser si le clavier est azerty ou qwerty ça évitera que les Q devienne des A et inversement.
lang=FR ou lang=US
Une fois que c'est fait on peut repasser à notre cher main et commencer a rédiger des fonctions qui nous interresse. Revenons sur notre main.py:
from utils import * # Import de notre module personnalisé
Puis créons une fonction par exemple pour entrer un username et un mot de passe pour pouvoir se connecter comme un flemmard au démarrage de linux ou windows:
def OnlyKeyDump(vlue, username, password):
# Initialisation de la séquence
sequence = []
# Pour chaque lettres dans username on la tappe à une vitesse de 0.01s par touches puis on appuie sur TAB
for letters in stringSeq(username):
sequence.append(letters)
sequence.append(TAB)
# Meme chose pour password et on appuie sur entrée en ajoutant tout a la séquence
for letters in stringSeq(password):
sequence.append(letters)
sequence.append(ENTER)
# Envoi des touches
sendKeys(sequence, vlue, 0.1)
Et voilà vous êtes fin prêt voici le résultat final du main.py
:
import usb.device # Librairie Micropython
from usb.device.keyboard import KeyboardInterface # Module pour être detecté comme clavier
import time # Librairie pour mettre des délais necessaires
from utils import *
class MyKeyboard(KeyboardInterface):
def on_led_update(self, led_mask):
pass
# Fonction precedemment créée
def OnlyKeyDump(vlue, username, password):
# Rentrez vos valeurs en paramètres
username = "DonaldDumb"
password = "Don4ldDumB!"
# Initialisation de la séquence
sequence = []
# Pour chaque lettres dans username on la tappe à une vitesse de 0.01s par touches puis on appuie sur TAB
for letters in stringSeq(username):
sequence.append(letters)
sequence.append(TAB)
# Meme chose pour password et on appuie sur entrée en ajoutant tout a la séquence
for letters in stringSeq(password):
sequence.append(letters)
sequence.append(ENTER)
# Envoi des touches
sendKeys(sequence, vlue, 0.1)
# Fonction principale
def BadUsb(): # Fonction qui va contenir tout le comportement de notre clavier
k = MyKeyboard() # On récupère le clavier dans une variable
usb.device.get().init(k, builtin_driver=True) # On charge ses modules
# Boucle de vérification, si le clavier n'est pas initialisé le pico ne fera rien.
while not k.is_open():
time.sleep(0.01)
OnleyKeyDump(k, "DonaldDumb", "Don4ldDumb!")
# On lance la fonction main.
BadUsb()
Vous pouvez débranchez votre Pico lorsque vous le rebrancherez il tappera votre username et votre mot de passe.
Conclusion
Voilà maintenant vous savez comment les hackers crée des Bad USB j'espère que ça vous aura plus. Si jamais vous souhaitez m'aider vous pouvez M'acheter un Café ?