DLL hijacking Windows et ordre de recherche des bibliothèques
Section intitulée « DLL hijacking Windows et ordre de recherche des bibliothèques »Le détournement de bibliothèque dynamique (DLL hijacking, ou DLL sideloading dans sa variante par cohabitation) fait exécuter du code arbitraire à une application de confiance sans la modifier. On place une bibliothèque malveillante portant le nom que l’application attend, dans un répertoire qu’elle consulte avant le bon. Cette page monte une application vulnérable en laboratoire, exécute le détournement, puis referme la faille sur l’ordre de recherche qui la rend possible.
Quelqu’un cherche toujours son trousseau de clés en fouillant ses poches dans le même ordre : d’abord la poche gauche, puis la droite. Un farceur glisse un faux trousseau dans la poche gauche. La personne tombe dessus en premier, l’attrape sans regarder et s’en sert, alors que le vrai trousseau attendait plus loin. C’est l’ordre de fouille, pas le vol, qui fait le tour.
→ Lire l’explication complète de la marmotte
Pourquoi l’ordre de recherche de Windows rend le détournement possible
Section intitulée « Pourquoi l’ordre de recherche de Windows rend le détournement possible »Une application Windows ne contient pas tout son code : elle s’appuie sur des bibliothèques dynamiques, les fichiers DLL (Dynamic-Link Library), chargées à l’exécution. Quand un programme demande une DLL par son nom seul, sans chemin absolu, Windows doit décider où la trouver. Il ne devine pas : il parcourt une liste de répertoires dans un ordre fixe et prévisible, et prend la première DLL du bon nom qu’il rencontre.
Cet ordre commence par le répertoire de l’application, puis (selon la configuration) le répertoire de travail courant, les répertoires système, et enfin les dossiers listés dans la variable d’environnement PATH. La faille naît de deux propriétés combinées : l’ordre est connu de tous, et un attaquant qui peut écrire dans un répertoire consulté avant l’emplacement légitime y dépose sa DLL. Windows la trouve en premier, la charge, et exécute le code qu’elle contient, sous l’identité et avec les privilèges de l’application de confiance.
Le point décisif est que l’application n’a aucun bug au sens habituel : elle demande une DLL par son nom, ce que fait tout programme. Le défaut est dans l’hypothèse implicite que le nom suffit à désigner le bon fichier. Vingt ans de packaging Windows apprennent que le répertoire de travail d’un processus est rarement celui qu’on croit : un fichier ouvert par double-clic, une archive décompressée dans le dossier Téléchargements, un raccourci mal câblé, et le répertoire de travail devient un endroit où un attaquant a pu écrire. Le sideloading pousse la logique plus loin : on livre un exécutable légitime et signé accompagné de sa DLL piégée dans le même dossier, et c’est la signature de l’exécutable qui sert de couverture au code non signé chargé à côté.
Monter l’ouvrage : une application qui charge une DLL par son nom
Section intitulée « Monter l’ouvrage : une application qui charge une DLL par son nom »Le décor tient en une petite application de laboratoire, montée pour l’occasion, qui charge une DLL par son nom seul, et en une DLL malveillante déposée dans le répertoire de travail. Tout se passe sur une machine de test jetable.
// MONTAGE DE LABORATOIRE, application volontairement vulnérable.// Elle charge "greffon.dll" par son NOM seul : Windows applique alors// son ordre de recherche et prend la première trouvée.#include <windows.h>
int main(void) { // Faille : nom sans chemin absolu. Le répertoire de travail courant // est consulté avant les dossiers systeme (hors recherche sécurisée). HMODULE h = LoadLibraryA("greffon.dll"); // ← chargement détournable if (h) { // L'application appelle une fonction légitime du greffon. FARPROC f = GetProcAddress(h, "Demarrer"); if (f) ((void(*)(void))f)(); } return 0;}La DLL malveillante exporte le même nom de fonction que la légitime, mais glisse son code dans DllMain, la fonction appelée automatiquement par Windows au chargement de toute DLL, avant même que l’application n’appelle quoi que ce soit.
// DLL malveillante de laboratoire : meme nom de fichier que la légitime.// DllMain s'exécute AU CHARGEMENT, avant tout appel de l'application.#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE h, DWORD raison, LPVOID reserve) { if (raison == DLL_PROCESS_ATTACH) { // Preuve d'exécution en laboratoire : dépose un fichier témoin. // Le code tourne sous l'identité de l'application de confiance. WinExec("cmd.exe /c echo preuve > %TEMP%\\preuve-labo.txt", SW_HIDE); } return TRUE;}En plaçant cette DLL dans le répertoire depuis lequel l’application est lancée, le fichier témoin apparaît dès le démarrage : le code non prévu s’est exécuté avant même la première ligne utile du programme. Aucun exploit mémoire, aucun dépassement de tampon, juste un fichier bien nommé au bon endroit. Linux, qui se croit volontiers au-dessus de ces mœurs de bureautique, rejoue la même farce avec son chargeur dynamique, décrite dans le détournement de bibliothèque partagée sous Linux.
Chemin absolu, recherche sécurisée et vérification de signature
Section intitulée « Chemin absolu, recherche sécurisée et vérification de signature »Trois contre-mesures, dans l’ordre où elles referment la faille.
Charger la DLL par chemin absolu supprime la recherche : LoadLibraryA("C:\\Program Files\\MonAppli\\greffon.dll") désigne exactement un fichier, et Windows ne parcourt plus aucune liste de répertoires. C’est la correction la plus directe quand on maîtrise le code, et elle ferme le vecteur à la racine. Pour les DLL système, LoadLibraryEx avec le drapeau LOAD_LIBRARY_SEARCH_SYSTEM32 restreint la recherche au seul dossier système.
Activer la recherche sécurisée durcit le comportement par défaut quand le nom seul reste employé. L’appel SetDllDirectory("") retire le répertoire de travail courant de l’ordre de recherche, ce qui neutralise le cas le plus courant du détournement. Le mode SafeDllSearchMode, activé par défaut sur Windows moderne, place déjà les répertoires système avant le répertoire de travail ; le vérifier plutôt que le supposer évite les mauvaises surprises sur les parcs hétérogènes.
// Durcissement : on retire le répertoire de travail de la recherche,// puis on impose la recherche dans les seuls dossiers de confiance.SetDllDirectoryA(""); // le répertoire de travail courant n'est plus consulté
// Chargement par chemin absolu : aucune recherche, aucun détournement.HMODULE h = LoadLibraryExA( "C:\\Program Files\\MonAppli\\greffon.dll", NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); // ordre restreint aux dossiers sursLa signature et la vérification d’intégrité des bibliothèques ferment la variante par sideloading, où l’attaquant livre sa DLL à côté d’un exécutable signé. Une application sensible vérifie la signature Authenticode des DLL qu’elle charge, et refuse celles qui ne portent pas une signature attendue. Le compromis central est là : la vérification de signature coûte en maintenance (chaque DLL légitime doit être signée, la chaîne de confiance tenue à jour) et ne dispense jamais du chemin absolu, puisqu’une DLL non signée chargée avant la vérification a déjà exécuté son DllMain. Trancher est net : chemin absolu d’abord, recherche sécurisée ensuite, signature en défense de dernier rang pour les binaires exposés. La logique de désigner sans ambiguïté ce qu’on charge se retrouve dans la section Failles et dans les principes d’architecture cybersécurité, où une ressource appelée par un nom que l’attaquant contrôle est toujours une ressource qu’il finit par fournir.
🐝 Ce qui arrive si on s’en passe
Section intitulée « 🐝 Ce qui arrive si on s’en passe »Sans chargement par chemin absolu ni recherche sécurisée, toute application qui appelle une bibliothèque par son nom seul devient un vecteur d’exécution de code dès qu’un attaquant peut écrire dans un répertoire consulté avant l’emplacement légitime, souvent le répertoire de travail hérité d’un double-clic ou d’une archive décompressée. Le code s’exécute sous l’identité et avec les privilèges de l’application de confiance, ce qui sert aussi bien la persistance que l’escalade quand l’application tourne avec des droits élevés. La détection est pauvre : le processus porte un nom légitime, souvent signé dans la variante par cohabitation, et la bibliothèque piégée n’apparaît suspecte qu’à qui compare le chemin chargé à l’emplacement attendu. Sans vérification de signature des bibliothèques chargées, rien ne distingue le fichier légitime du fichier substitué avant que son code de démarrage n’ait déjà tourné.
🦫 Explication de la marmotte 🍫
Section intitulée « 🦫 Explication de la marmotte 🍫 »Un programme Windows ne contient pas tout son code : il s’appuie sur des fichiers séparés, les bibliothèques, qu’il charge quand il en a besoin. Souvent, il demande une bibliothèque juste par son nom, sans dire où elle se trouve. Windows doit alors la chercher, et il le fait toujours dans le même ordre : d’abord le dossier du programme, puis le dossier depuis lequel il a été lancé, puis les dossiers du système. Il prend la première bibliothèque du bon nom qu’il rencontre.
Un attaquant exploite cet ordre connu de tous. S’il peut écrire un fichier dans un dossier consulté tôt, il y dépose une fausse bibliothèque portant exactement le nom attendu. Le programme, en cherchant, tombe sur la fausse avant la vraie, la charge et exécute le code qu’elle contient, sans se douter de rien. Pire, Windows lance automatiquement un bout de code au moment même du chargement, donc le piège se déclenche avant que le programme ait fait quoi que ce soit d’utile. Le code s’exécute avec les droits du programme de confiance.
Pour refermer la faille, la meilleure solution est de préciser le chemin complet de la bibliothèque, pour que Windows n’ait plus à chercher et ne puisse plus se tromper de fichier. On peut aussi retirer le dossier de lancement de la liste des endroits fouillés, ce qui supprime le cas le plus fréquent, et vérifier la signature de chaque bibliothèque avant de s’y fier.
À lire aussi
- Attaque par relais NTLM et signature SMB obligatoireMonter un relais NTLM en laboratoire, s'authentifier à la place d'une victime sans casser son mot de passe, puis refermer la faille avec la signature SMB et la liaison de canal.
- Jeton de compte de service Kubernetes monté par défautExtraire le jeton de compte de service injecté par défaut dans un pod compromis, interroger l'API Kubernetes pour pivoter dans le cluster, puis refermer la faille par montage désactivé et RBAC minimal.
- Élévation de privilège via un binaire SUID root mal configuréDémonstration en laboratoire de l'élévation de privilège locale par abus d'un exécutable SUID root trop permissif, avec le mécanisme du bit SUID et les contre-mesures d'audit et de capabilities.
- Désérialisation non sécurisée et chaîne de gadgetsFabriquer un objet sérialisé malveillant qui déclenche l'exécution de code à la désérialisation, le démontrer en laboratoire, puis refermer la faille par liste blanche de classes et formats de données inertes.
Pages voisines
La citent
- JPCERT/CC rôle, outils malware et usage opérationnel
- Registre Windows, GPO et services, fonctionnement et attaques
DLL hijacking Windows et ordre de recherche des bibliothèques
Elle cite
: les deux pages se citent l’une l’autre.
