Bonjour,
Dans un précèdent article, je m'étais attelé à la réalisation d'un keylogger. Nous avions vu que, de par la méthode utilisée (hook global sur WH_GETMESSAGE), il s'agissait d'injecter une DLL dans tous les processus, du moins là où c'était possible. L'injection de DLL est donc en quelque sorte un effet secondaire de cette façon de procéder. J'avais mentionné que cela permettait plein de choses dans la mesure où l'on parvient à faire exécuter son propre code par un autre processus.
Ici, on va voir comment on peut injecter une DLL pour contourner un pare-feu. Le principe est très simple : on sélectionne une application qui est supposée avoir des droits suffisants dans le pare-feu et on y injecte notre DLL. A titre d'exemple, elle va contenir un serveur UDP, lequel va se mettre en attente de connexion extérieures.
Comme il s'agit de cibler un processus (et un seul) désigné à l'avance, un hook global n'est pas adapté pour ce faire. En conséquence, on va en profiter pour étudier une autre technique d'injection, celle par l'API Windows CreateRemoteThread().
Dans cet exemple, sous Windows 7, j'ai d'abord lancé le programme inj en ligne de commande (en haut à droite) pour injecter Firefox (visible en bas à droite). Ensuite, j'utilise la distribution GhostBSD (à gauche, en machine virtuelle) pour envoyer des données qui vont être lues par la DLL (donc Firefox) et finalement renvoyées à l'injecteur ("Coucou Windows !"). Le pare-feu de Windows n'y voit aucun inconvénient.
Ainsi que le montre netstat, c'est bien Firefox qui écoute sur UDP 53 (utilisé normalement pour le protocole DNS).
Concepts et principes
Compiler l'ensemble
Note : les commandes Set path font référence aux emplacements par défaut, avec un Windows 10 standard, des compilateurs 32 et 64 bits. A rectifier, selon la place où se trouvent vraiment les binaires de minGW-W64.
Git : https://git.hackademics.fr/Icarus/Injecteur
Conclusions
Alors, le crime est parfait ? Pas tout à fait, et loin s'en faut en vérité.
Premièrement, ouvrir un port dans un pare-feu, c'est bien, mais il faut encore pouvoir passer la box ou le routeur qui est en première ligne sur internet. Ceci dit, ce n'est pas impossible.
Deuxièmement, les applications qui s'octroient tous les droits dans le pare-feu de Windows se font rares. Firefox.exe, que j'ai pris comme exemple, fait telle chose lors de son installation. Cependant, ça ne fonctionnera pas du tout avec Chrome.exe dont les règles de paquets entrants semblent plutôt bien étudiées. Avec Edge, il n'y a aucun espoir car il est protégé contre ce genre d'amusement, comme le sont certains processus système de Windows (svchost.exe, ...). Maintenant, injecter un programme comme Explorer.exe ou OneDrive.exe va provoquer une demande d'autorisation du pare-feu ; cela peut éventuellement passer (mieux en tout cas que si c'est Inj.exe qui le demande ).
Troisièmement, ainsi que je le disais, l'injection par CreateRemoteThread() est bien connue de tout le monde, y-compris des antivirus. Certains d'entre eux vont placer Inj.exe en "bac à sable", constater la tentative d'injection de processus et décider que ce n'est pas bien , puis, pouf, quarantaine. A cela, il y a encore des parades mais d'autres interceptent certains appels systèmes (WriteProcessMemory() notamment dont on a besoin) et les rendent inopérants. Là, je ne vois pas comment faire pour s'en sortir, enfin simplement du moins.
Dans un précèdent article, je m'étais attelé à la réalisation d'un keylogger. Nous avions vu que, de par la méthode utilisée (hook global sur WH_GETMESSAGE), il s'agissait d'injecter une DLL dans tous les processus, du moins là où c'était possible. L'injection de DLL est donc en quelque sorte un effet secondaire de cette façon de procéder. J'avais mentionné que cela permettait plein de choses dans la mesure où l'on parvient à faire exécuter son propre code par un autre processus.
Ici, on va voir comment on peut injecter une DLL pour contourner un pare-feu. Le principe est très simple : on sélectionne une application qui est supposée avoir des droits suffisants dans le pare-feu et on y injecte notre DLL. A titre d'exemple, elle va contenir un serveur UDP, lequel va se mettre en attente de connexion extérieures.
Comme il s'agit de cibler un processus (et un seul) désigné à l'avance, un hook global n'est pas adapté pour ce faire. En conséquence, on va en profiter pour étudier une autre technique d'injection, celle par l'API Windows CreateRemoteThread().
Dans cet exemple, sous Windows 7, j'ai d'abord lancé le programme inj en ligne de commande (en haut à droite) pour injecter Firefox (visible en bas à droite). Ensuite, j'utilise la distribution GhostBSD (à gauche, en machine virtuelle) pour envoyer des données qui vont être lues par la DLL (donc Firefox) et finalement renvoyées à l'injecteur ("Coucou Windows !"). Le pare-feu de Windows n'y voit aucun inconvénient.
Ainsi que le montre netstat, c'est bien Firefox qui écoute sur UDP 53 (utilisé normalement pour le protocole DNS).
Code:
C:\Windows\system32>netstat -ab -p UDP Connexions actives Proto Adresse locale Adresse distante État UDP 0.0.0.0:53 *:* [firefox.exe]
Concepts et principes
- L'injection de DLL par CreateRemoteThread() est une technique ancienne que l'on trouve documentée (plus ou moins bien) un peu partout sur internet (1). Pour faire simple, cela permet de faire exécuter certaines API Windows par le processus cible. Ici, nous lui demandons simplement de charger notre DLL dans son espace mémoire (et de la décharger lorsqu'on en a fini).
- De la même façon que nous l'avions vu pour le Keylogger, on se retrouve confronté au problème suivant : un processus 32 bits ne peut charger qu'une DLL 32 bits, idem pour un processus 64 bits qui ne peut charger qu'une DLL 64 bits. De plus, l'injection elle-même ne peut être réalisée que par un programme de même nombre de bits que la cible (cela bloque à CreateRemoteThread() : accès refusé). Ce projet va donc demander la compilation de deux injecteurs et de deux DLL, les uns en 32 bits et les autres en 64 bits, bien que le code soit exactement le même.
- Ce dernier point amène à la réalisation d'un autre programme dit "frontal" qui va avoir en charge de trouver le processus cible et déterminer s'il s'agit d'un 32 bits ou d'un 64 bits. Sur la base de son résultat, il appelle la bonne version de l'injecteur qui lui-même fait charger la bonne version de la DLL.
Compiler l'ensemble
- Télécharger l'installateur de minGW-W64. Il faudra le lancer deux fois, pour rapatrier les deux versions 32 et 64 bits.
- Pour le compilateur 32 bits choisir les paramètres : Version = laisser le défaut, Architecture = i686, Thread = win32, Exception = dwarf, Buil Revison = laisser le défaut. Par défaut, il va s'installer dans C:\Program Files (x86)\mingw-w64\i686-7.2.0-win32-dwarf-rt_v5-rev1\.
- Relancer l'installateur. Pour le compilateur 64 bits choisir les paramètres : Version = laisser le défaut, Architecture = X86_64, Thread = win32, Exception = seh. Buil Revison = laisser le défaut. Par défaut, il va s'installer dans C:\Program Files\mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev1.
- Télécharger les sources de l'injecteur, placer les fichiers dans un répertoire.
- Ouvrir une invite de commande (cmd.exe), se placer là où se trouvent les sources et entrer :
- Set path=C:\Program Files (x86)\mingw-w64\i686-7.2.0-win32-dwarf-rt_v5-rev1\mingw32\bin
- g++ -static -s -o inj.exe inj-frontal.cpp
- g++ -shared -static -s -o inj32.dll Injecteur_dll.cpp -lws2_32
- g++ -static -s -o inj32.exe Injecteur.cpp
- Set path=C:\Program Files\mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev1\mingw64\bin
- g++ -shared -static -s -o inj64.dll Injecteur_dll.cpp -lws2_32
- g++ -static -s -o inj64.exe Injecteur.cpp
Note : les commandes Set path font référence aux emplacements par défaut, avec un Windows 10 standard, des compilateurs 32 et 64 bits. A rectifier, selon la place où se trouvent vraiment les binaires de minGW-W64.
Git : https://git.hackademics.fr/Icarus/Injecteur
Conclusions
Alors, le crime est parfait ? Pas tout à fait, et loin s'en faut en vérité.
Premièrement, ouvrir un port dans un pare-feu, c'est bien, mais il faut encore pouvoir passer la box ou le routeur qui est en première ligne sur internet. Ceci dit, ce n'est pas impossible.
Deuxièmement, les applications qui s'octroient tous les droits dans le pare-feu de Windows se font rares. Firefox.exe, que j'ai pris comme exemple, fait telle chose lors de son installation. Cependant, ça ne fonctionnera pas du tout avec Chrome.exe dont les règles de paquets entrants semblent plutôt bien étudiées. Avec Edge, il n'y a aucun espoir car il est protégé contre ce genre d'amusement, comme le sont certains processus système de Windows (svchost.exe, ...). Maintenant, injecter un programme comme Explorer.exe ou OneDrive.exe va provoquer une demande d'autorisation du pare-feu ; cela peut éventuellement passer (mieux en tout cas que si c'est Inj.exe qui le demande ).
Troisièmement, ainsi que je le disais, l'injection par CreateRemoteThread() est bien connue de tout le monde, y-compris des antivirus. Certains d'entre eux vont placer Inj.exe en "bac à sable", constater la tentative d'injection de processus et décider que ce n'est pas bien , puis, pouf, quarantaine. A cela, il y a encore des parades mais d'autres interceptent certains appels systèmes (WriteProcessMemory() notamment dont on a besoin) et les rendent inopérants. Là, je ne vois pas comment faire pour s'en sortir, enfin simplement du moins.
Commentaire