Sensibiliser aux risques des clés USB avec un Lilygo T-Dongle-S3

5
(4)

Les clés USB sont pratiques pour le stockage et le transfert de données. Mais elles présentent également plusieurs risques potentiels pour la sécurité. Au delà des logiciels malveillants qu’elles peuvent contenir, certaines clés (comme la célèbre Rubber Ducky) peuvent être programmées pour se comporter comme un clavier. Permettant ainsi à un attaquant d’injecter des commandes malveillantes dans le système hôte.

Vous souhaitez sensibiliser vos équipes à ce risque ? Vous avez un budget de moins de 20€ ? Cela tombe bien car c’est l’objectif de cet article !

Pour rendre ce petit tutoriel accessible à tous, j’utilise une clé Lilygo T-Dongle-S3 que je programme avec l’IDE Arduino.

Installation du gestionnaire de cartes ESP32

Le dongle Lilygo étant basé sur le microcontrôleur ESP32-S3 il faut installer le support des cartes Espressif ESP32 dans l’IDE Arduino.

Dans le menu préférences de l’IDE Arduino, ajoutez l’URL suivante dans le gestionnaire de carte :

Plaintext
https://espressif.github.io/arduino-esp32/package_esp32_index.json

Voici une capture d’écran des étapes à suivre :

Installez le paquet de gestion de cartes esp32 by Espressif. Attention, à date (15/02/2025) il faut installer la version 2.0.14 car la librairie RFT_eSPI que nous allons utiliser ne fonctionne pas avec les versions ultérieures sur l’ESP32-S3 (voir TFT_eSPI/issue3329).

Une fois le gestionnaire de cartes installé, sélectionnez la carte ESP32S3 Dev Module et configurez la taille de la mémoire Flash à 16MB.

Installation des librairies pour l’écran LCD et la LED RGB

Installez ensuite la librairie FastLED pour la gestion de la LED RGB, et la librairie TFT_eSPI pour la gestion de l’écran LCD.

Il est indispensable de modifier le fichier User_Setup_Select.h dans le répertoire de la librairie TFT_eSPI pour que la librairie soit configurée pour notre dongle Lilygo-T-Dongle-S3.

Commenter la ligne 27 du fichier User_Setup_Select.h :

C++
//#include <User_Setup.h>           // Default setup is root library folder

Dé-commenter la ligne 135 du fichier User_Setup_Select.h :

C++
#include <User_Setups/Setup209_LilyGo_T_Dongle_S3.h>      // For the LilyGo T-Dongle S3 based ESP32 with ST7735 80 x 160 TFT

Voici à quoi doit ressembler ce fichier User_Setup_Select.h après ces 2 modifications :

Un premier croquis pour vérifier que tout fonctionne bien

Pour vérifier que votre installation et configuration est correcte, voici un croquis qui affiche un message à l’écran et fait clignoter la LED RGB.

C++
// LilyGo T-Dongle-S3 ESP32
// https://tutoduino.fr/
// Copyleft 2025

#include <FastLED.h>
#include <SPI.h>
#include <TFT_eSPI.h>

TFT_eSPI tft = TFT_eSPI();  // Invoke custom library

#define DATA_PIN 40
#define CLOCK_PIN 39

CRGB rgb_led[1];

void setup() {
  // Init RGB LED in BGR ordering
  FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, BGR>(rgb_led, 1);

  // Init TFT screen
  tft.init();
  // Set screen in landscape mode for right side laptop usb port (turn 270°)
  tft.setRotation(3); 
  // Set screen background to BLACK
  tft.fillScreen(TFT_BLACK); 
  // Set "cursor" at top left corner of display (0,0) and select font 4
  tft.setCursor(0, 0, 4);
  // Set text color to white
  tft.setTextColor(TFT_WHITE);
  tft.println("Welcome to");
  tft.println("Tutoduino");    
}

void loop() {
  // Set LED color to blue, then pause
  rgb_led[0] = CRGB::Blue;
  FastLED.show();
  delay(500);
  // Set LED color to white, then pause
  rgb_led[0] = CRGB::White;
  FastLED.show();
  delay(500);
  // Set LED color to red, then pause
  rgb_led[0] = CRGB::Red;
  FastLED.show();
  delay(500);    
  // Set LED color to black, then pause
  rgb_led[0] = CRGB::Black;
  FastLED.show();
  delay(500);
}

Si le téléversement se déroule bien, au démarrage la LED RGB devrait clignoter Bleu-Blanc-Rouge et le texte “Welcome to Tutoduino” devrait s’afficher sur l’écran.

Le croquis de sensibilisation aux risques liés aux clés USB

Ce programme s’exécute automatiquement dès que le dongle USB est inséré dans un PC. Il est prévu pour un PC fonctionnant sous Windows, mais il peut être adapté très facilement pour un PC sous Linux (“Windows+r” doit simplement être remplacé par “CTRL+ALT+t”).

Lorsque il est connecté à un PC sous Windows, le dongle USB émule pendant 20 secondes un stockage de masse USB contenant un fichier au format csv. Après 20 secondes, le stockage de masse USB est éjecté et le dongle émule alors un clavier USB. Ce clavier USB envoi alors la combinaison de touche “Windows + r” qui ouvre la boite de dialogue “exécuter” puis y entre la commande “cmd” (“c;d” pour un clavier azerty) afin d’ouvrir un invité de commande (Command Prompt). La commande “dir” est ensuite exécutée dans cet invité de commande, affichant le contenu du répertoire courant…

Un message est également affiché sur l’écran du dongle USB, avec pour objectif de rendre plus visuel ce type d’attaque et de renforcer l’efficacité de la sensibilisation.

C++
/*
 * Sensibilisation aux risques liés à l'utilisation des clés USB
 * -------------------------------------------------------------
 *
 * Ce programme configure un dongle USB à base de ESP32 pour se comporter 
 * d'abord comme une clé USB, puis comme un clavier qui ouvre automatiquement 
 * un terminal.
 *
 * ATTENTION : Ce programme est à des fins éducatives uniquement. Il démontre
 * comment un dispositif peut émuler une clé USB et un clavier, ce qui peut
 * potentiellement être utilisé à des fins malveillantes.
 *
 * Risques associés :
 * 1. Exécution de commandes non autorisées : En se comportant comme un clavier,
 *    le dispositif peut injecter des commandes dans le système hôte.
 * 2. Vol de données : Le dispositif peut copier des fichiers sensibles sur lui-même
 *    lorsqu'il est connecté en tant que clé USB.
 * 3. Installation de logiciels malveillants : Le dispositif peut installer des
 *    logiciels malveillants sur le système hôte.
 *
 * Précautions à prendre :
 * - Ne jamais connecter de clés USB non fiables à votre ordinateur.
 * - Utiliser des logiciels de sécurité pour analyser les clés USB avant de les utiliser.
 * - Désactiver l'exécution automatique des fichiers à partir des clés USB.
 *
 * L'auteur de ce programme décline toute responsabilité en cas d'utilisation
 * abusive ou de dommages causés par ce programme.
 *
 * https://tutoduino.fr/
 * Copyleft 2025
*/
#include "USB.h"
#include "USBMSC.h"
#include <FastLED.h>
#include <SPI.h>
#include <TFT_eSPI.h>
#include "USBHIDKeyboard.h"
// Keyboard
USBHIDKeyboard Keyboard;
// LCD screen
TFT_eSPI tft = TFT_eSPI();  // Invoke custom library
// RGB LED
#define RGB_DATA_PIN 40
#define RGB_CLOCK_PIN 39
CRGB rgb_led[1];
USBCDC USBSerial;
USBMSC MSC;
#define FAT_U8(v) ((v) & 0xFF)
#define FAT_U16(v) FAT_U8(v), FAT_U8((v) >> 8)
#define FAT_U32(v) FAT_U8(v), FAT_U8((v) >> 8), FAT_U8((v) >> 16), FAT_U8((v) >> 24)
#define FAT_MS2B(s,ms)    FAT_U8(((((s) & 0x1) * 1000) + (ms)) / 10)
#define FAT_HMS2B(h,m,s)  FAT_U8(((s) >> 1)|(((m) & 0x7) << 5)),      FAT_U8((((m) >> 3) & 0x7)|((h) << 3))
#define FAT_YMD2B(y,m,d)  FAT_U8(((d) & 0x1F)|(((m) & 0x7) << 5)),    FAT_U8((((m) >> 3) & 0x1)|((((y) - 1980) & 0x7F) << 1))
#define FAT_TBL2B(l,h)    FAT_U8(l), FAT_U8(((l >> 8) & 0xF) | ((h << 4) & 0xF0)), FAT_U8(h >> 4)
#define README_CONTENTS "Asset,Date,Time,Production\nPROD_LINE_A,05/02/25,14:28:00,180\nPROD_LINE_G,05/02/25,15:05:00,245\nPROD_LINE_U,05/02/25,14:28:00,180"
static const uint32_t DISK_SECTOR_COUNT = 2 * 8; // 8KB is the smallest size that windows allow to mount
static const uint16_t DISK_SECTOR_SIZE = 512;    // Should be 512
static const uint16_t DISC_SECTORS_PER_TABLE = 1; //each table sector can fit 170KB (340 sectors)
static uint8_t msc_disk[DISK_SECTOR_COUNT][DISK_SECTOR_SIZE] =
{
  //------------- Block0: Boot Sector -------------//
  {
    // Header (62 bytes)
    0xEB, 0x3C, 0x90, //jump_instruction
    'M' , 'S' , 'D' , 'O' , 'S' , '5' , '.' , '0' , //oem_name
    FAT_U16(DISK_SECTOR_SIZE), //bytes_per_sector
    FAT_U8(1),    //sectors_per_cluster
    FAT_U16(1),   //reserved_sectors_count
    FAT_U8(1),    //file_alloc_tables_num
    FAT_U16(16),  //max_root_dir_entries
    FAT_U16(DISK_SECTOR_COUNT), //fat12_sector_num
    0xF8,         //media_descriptor
    FAT_U16(DISC_SECTORS_PER_TABLE),   //sectors_per_alloc_table;//FAT12 and FAT16
    FAT_U16(1),   //sectors_per_track;//A value of 0 may indicate LBA-only access
    FAT_U16(1),   //num_heads
    FAT_U32(0),   //hidden_sectors_count
    FAT_U32(0),   //total_sectors_32
    0x00,         //physical_drive_number;0x00 for (first) removable media, 0x80 for (first) fixed disk
    0x00,         //reserved
    0x29,         //extended_boot_signature;//should be 0x29
    FAT_U32(0x1234), //serial_number: 0x1234 => 1234
    'T' , 'i' , 'n' , 'y' , 'U' , 'S' , 'B' , ' ' , 'M' , 'S' , 'C' , //volume_label padded with spaces (0x20)
    'F' , 'A' , 'T' , '1' , '2' , ' ' , ' ' , ' ' ,  //file_system_type padded with spaces (0x20)
    // Zero up to 2 last bytes of FAT magic code (448 bytes)
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     //boot signature (2 bytes)
    0x55, 0xAA
  },
  //------------- Block1: FAT12 Table -------------//
  {
    FAT_TBL2B(0xFF8, 0xFFF), FAT_TBL2B(0xFFF, 0x000) // first 2 entries must be 0xFF8 0xFFF, third entry is cluster end of readme file
  },
  //------------- Block2: Root Directory -------------//
  {
    // first entry is volume label
    'U' , 'S' , 'B' , ' ' , 'K' , 'E' , 'Y' , ' ' , 
    ' ' , ' ' , ' ' , 
    0x08, //FILE_ATTR_VOLUME_LABEL
    0x00, 
    FAT_MS2B(0,0), 
    FAT_HMS2B(0,0,0),
    FAT_YMD2B(0,0,0), 
    FAT_YMD2B(0,0,0), 
    FAT_U16(0), 
    FAT_HMS2B(13,42,30),  //last_modified_hms
    FAT_YMD2B(2018,11,5), //last_modified_ymd
    FAT_U16(0), 
    FAT_U32(0),
    
    // second entry is readme file
    'P' , 'R' , 'O' , 'D' , 'D' , 'A' , 'T' , 'A' ,//file_name[8]; padded with spaces (0x20)
    'C' , 'S' , 'V' ,     //file_extension[3]; padded with spaces (0x20)
    0x20,                 //file attributes: FILE_ATTR_ARCHIVE
    0x00,                 //ignore
    FAT_MS2B(1,980),      //creation_time_10_ms (max 199x10 = 1s 990ms)
    FAT_HMS2B(13,42,36),  //create_time_hms [5:6:5] => h:m:(s/2)
    FAT_YMD2B(2018,11,5), //create_time_ymd [7:4:5] => (y+1980):m:d
    FAT_YMD2B(2020,11,5), //last_access_ymd
    FAT_U16(0),           //extended_attributes
    FAT_HMS2B(13,44,16),  //last_modified_hms
    FAT_YMD2B(2019,11,5), //last_modified_ymd
    FAT_U16(2),           //start of file in cluster
    FAT_U32(sizeof(README_CONTENTS) - 1) //file size
  },
  //------------- Block3: Readme Content -------------//
  README_CONTENTS
};
unsigned long start_time = 0;
static int32_t onWrite(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize){
  memcpy(msc_disk[lba] + offset, buffer, bufsize);
  return bufsize;
}
static int32_t onRead(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize){
  memcpy(buffer, msc_disk[lba] + offset, bufsize);
  return bufsize;
}
static bool onStartStop(uint8_t power_condition, bool start, bool load_eject){
  return true;
}
static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
}
void awarness_on() {
  // Eject USB key
  MSC.end();
  // Init TFT screen and set it to black
  tft.init();
  tft.fillScreen(TFT_BLACK); 
  // Init RGB LED in BGR ordering and set it to black
  FastLED.addLeds<APA102, RGB_DATA_PIN, RGB_CLOCK_PIN, BGR>(rgb_led, 1);
  rgb_led[0] = CRGB::Black;
  FastLED.show();
  // Emulate keyboard and open a terminal
  Keyboard.begin();
  Keyboard.press(KEY_RIGHT_GUI);
  Keyboard.press('r');
  Keyboard.releaseAll();
  delay(500);
  Keyboard.println("c;d");
  delay(500);
  Keyboard.println("dir");
  Keyboard.println("echo Zhqt could go zrongM");

  // Display awarness message on LCD screen
  tft.setRotation(3); 
  tft.setCursor(0, 0, 2);
  // Set text color to white
  tft.setTextColor(TFT_RED);
  tft.println("USB keys:");
  tft.println("Small but sneaky.");
  tft.println("Be worry!");  
  // Set LED color to red
  rgb_led[0] = CRGB::Red;
  FastLED.show();
  // Infinite loop
  while (1);
}
void setup() {
  // Init USB key
  USB.onEvent(usbEventCallback);
  MSC.vendorID("ESP32");//max 8 chars
  MSC.productID("USB_MSC");//max 16 chars
  MSC.productRevision("1.0");//max 4 chars
  MSC.onStartStop(onStartStop);
  MSC.onRead(onRead);
  MSC.onWrite(onWrite);
  MSC.mediaPresent(true);
  MSC.begin(DISK_SECTOR_COUNT, DISK_SECTOR_SIZE);
  //USBSerial.begin();
  USB.begin();
  start_time = millis();
}
void loop() {
  // start awarness session after 20 seconds ;)
  if ((millis() - start_time) > 20000) {
    awarness_on();
  }
}

Il faut configurer le USB Mode sur USB-OTG avant de téléverser le croquis sur le dongle.

Note : si vous n’arrivez pas à téléverser le croquis sur le dongle, il faut le débrancher et le reconnecter tout en appuyant sur le bouton Boot (au dos de la clé, côté opposé à l’écran). Relâcher le bouton lorsque la clé est insérée dans le PC, puis téléverser le croquis.

Voici en vidéo ce que l’utilisateur verra sur son écran lorsqu’il va insérer la clé USB dans son PC, et l’affichage sur l’écran de la clé USB lorsque l’attaque sera terminée.

Suite à cette sensibilisation, les utilisateurs vont probablement vous demander “mais alors, comment éviter ce risque ?”. Je vous laisse y répondre, mais sachez qu’il y a de nombreuses solutions disponibles 😉.

J’espère que cet article vous aura intéressé. N’hésitez pas à donner votre avis en cliquant sur les étoiles ci-dessous. Je ne suis pas rémunéré au nombre d’étoiles mais c’est important pour moi afin de vérifier que mon travail est utile aux autres.

Cet article a été écrit purement à but pédagogique et son contenu ne doit pas être utilisé pour mener des activités illicites. L’auteur décline toute forme de responsabilité sur l’utilisation de son contenu.

Votre avis compte !

Note moyenne : 5 / 5. Nombre de votes : 4

Pas encore de vote pour ce tutoriel