Neutralisation d'EDR
Publié le 11 min de lecture
Mis à jour le
Dans cette série32 min de lecture au total
- Composants internes de Windows
- Composants Internes des EDR
- Neutralisation d'EDR
De la signature du driver à la preuve d’une primitive de terminaison non autorisée.
La technique Bring Your Own Vulnerable Driver (BYOVD) consiste à charger un driver légitime mais vulnérable afin d’obtenir du noyau une opération privilégiée. Dans le cas étudié, cette opération est la terminaison d’un processus. Le raisonnement ne part donc pas d’un nom de fonction pour remonter immédiatement à une exploitation ; il suit tout le protocole exposé par le device, depuis son ouverture jusqu’à l’effet final.
Trois observations doivent rester séparées :
- une signature établit une relation de confiance pour le chargement selon la politique active ;
- un IOCTL sélectionne une commande du protocole propre au driver ;
- un import de
ZwTerminateProcessindique que le binaire peut appeler cette routine.
Aucune de ces observations ne démontre, seule, qu’un processus peut terminer le PID de son choix.
De la confiance de chargement à la surface exposée
Une politique d’intégrité du code peut accepter la signature d’un driver sans connaître la qualité de chacune de ses vérifications d’autorisation. C’est précisément l’écart exploité par BYOVD : le code est admis dans le noyau, puis son interface de device fournit éventuellement une capacité trop large à un appelant qui peut l’atteindre.
Cette capacité peut rester étroite, par exemple terminer un processus, ou devenir plus générale, par exemple lire ou écrire en mémoire kernel. Une primitive de terminaison n’est ni une primitive d’écriture kernel, ni une exécution arbitraire de code kernel. Cette distinction détermine ce que le défaut permet réellement de neutraliser.
Dans ce scénario, l’effet final peut passer par ZwTerminateProcess. La routine est exécutée par ntoskrnl.exe, déclarée dans ntddk.h et résolue par un driver au moyen de NtosKrnl.lib. Elle reçoit un handle de processus et un statut de sortie, pas un Process Identifier (PID). Si l’entrée du protocole contient un PID, le driver doit donc le convertir en objet ou en handle, souvent par ZwOpenProcess, avant de demander la terminaison.
L’existence de ce chemin pose alors une question plus précise : qui peut ouvrir le device et faire accepter la commande ?
Rechercher un candidat sans confondre triage et preuve
LOLDB centralise des métadonnées sur des drivers Windows connus, dont les empreintes, certificats, imports et catégories d’usage. La base sert à réduire l’espace de recherche ; elle ne remplace ni l’analyse du binaire exact ni un test d’autorisation.
Le filtre Process Killers rapproche les entrées associées à une capacité de terminaison observée. Il ne signifie pas que tous les drivers listés exposent le même IOCTL, acceptent le même format de buffer ou sont exploitables par le même niveau de privilège.
Le triage établit donc quels binaires méritent une analyse. Il ne montre toujours pas comment une application atteint la branche sensible. Pour répondre à cette question, il faut reconstruire la routine de dispatch du driver.
Reconstruire le contrat IOCTL
Un IOCTL est une valeur de 32 bits structurée par la macro CTL_CODE(DeviceType, Function, Method, Access). Le champ Method décrit le transport des buffers ; le champ Access indique les droits d’accès au device que le gestionnaire d’entrées/sorties exige pour créer la requête. Même FILE_ANY_ACCESS ne rend pas automatiquement le device accessible : sa liste de contrôle d’accès, ou Access Control List (ACL), décide d’abord quels principaux peuvent obtenir un handle.
Dans un driver Windows Driver Model (WDM), DriverEntry est le point d’entrée compilé dans le fichier .sys du fournisseur. Il initialise notamment la structure DRIVER_OBJECT. L’entrée MajorFunction[IRP_MJ_DEVICE_CONTROL] peut contenir l’adresse d’une routine de dispatch fournie par ce même driver. IRP_MJ_DEVICE_CONTROL est un code majeur de requête, pas une fonction exportée par Windows.
Identifier le binaire et le device
Conserver l’empreinte, la version et la signature du fichier analysé. Repérer la création du device, son lien symbolique et les descripteurs de sécurité qui conditionnent son ouverture.
Retrouver la routine de dispatch
Suivre l’initialisation de
DRIVER_OBJECT.MajorFunction[]jusqu’à l’entréeIRP_MJ_DEVICE_CONTROL. L’adresse trouvée appartient à une fonction compilée dans le.sys, même si le code majeur est défini par le Windows Driver Kit (WDK).Décoder chaque commande
Pour chaque valeur comparée, décoder
DeviceType,Function,MethodetAccess, puis relever les tailles, offsets et directions des buffers. Une constante identifiée sans son format d’entrée ne suffit pas à reproduire le contrat.Suivre l'effet privilégié
Tracer les données contrôlées par l’appelant jusqu’à
ZwOpenProcess,ZwTerminateProcessou une autre routine sensible. Relever toutes les branches qui valident l’identité de l’appelant, la cible et le contexte d’exécution.Mesurer le gain de capacité
Comparer l’effet obtenu par le device aux droits ordinaires du même appelant. Une vulnérabilité existe lorsque le driver lui accorde une opération privilégiée qu’il ne peut pas accomplir directement.
Lire les captures de reverse engineering
La méthode précédente fournit le fil directeur. Les captures suivantes montrent comment ses différentes preuves apparaissent pendant une analyse statique. Elles documentent un échantillon et une session de lab déterminés ; elles ne décrivent pas tous les drivers portant un import similaire.
La table d’imports commence par établir quelles routines kernel le binaire peut résoudre. Elle ne dit pas encore quelle entrée utilisateur atteint ces appels.
La présence de ZwTerminateProcess réduit la recherche à ses références. La routine apparaît ici comme un symbole importé depuis le noyau Windows.
Les références croisées relient ensuite l’import aux fonctions du driver qui l’appellent. Ce lien établit l’atteignabilité interne, mais pas encore l’atteignabilité depuis un IOCTL.
Le graphe de contrôle permet d’observer l’appel et les branches voisines. Les boucles et tests visibles doivent être interprétés avec leurs buffers et leurs bornes.
Le pseudo-code rend plus lisible la relation entre le handle et l’appel. Il faut cependant remonter l’origine de cette valeur avant de conclure que la cible est contrôlée par l’appelant.
Les références vers cette fonction conduisent enfin vers la routine qui prépare les paramètres. Le chaînage inverse relie l’effet privilégié à sa commande.
La branche de dispatch visible sélectionne ici une constante IOCTL. Cette valeur identifie une commande pour cette version du binaire, pas un code Windows générique.
Ces écrans reconstituent un chemin de code. La qualification de vulnérabilité exige encore une preuve dynamique et comparative.
Quand un IOCTL de terminaison constitue réellement une vulnérabilité
Le parcours documenté sépare les contrôles du gestionnaire d’entrées/sorties, la logique propre au driver et l’effet fourni par le noyau :
DeviceIoControl est l’API user-mode qui envoie le code de contrôle et ses buffers. Son point d’entrée est exporté par Kernel32.dll, son prototype est déclaré dans ioapiset.h et l’application le lie avec Kernel32.lib. Le gestionnaire d’entrées/sorties transforme l’appel en un I/O Request Packet (IRP) portant le code majeur IRP_MJ_DEVICE_CONTROL, puis appelle la routine de dispatch enregistrée par le driver.
ZwOpenProcess et ZwTerminateProcess s’exécutent dans ntoskrnl.exe, sont déclarées dans ntddk.h et sont liées par le driver au moyen de NtosKrnl.lib. La première peut demander un handle disposant de PROCESS_TERMINATE; la seconde tente de terminer le processus représenté par ce handle. L’une ou l’autre peut échouer si l’identifiant n’est plus valide, si l’accès n’est pas accordé, si le handle est incorrect, si la cible bénéficie de protections applicables ou si le processus est déjà en cours de terminaison.
Pour qualifier le chemin de vulnérable, l’analyse doit établir toute la chaîne :
| Propriété | Preuve recherchée |
|---|---|
| Accessibilité | Un appelant situé dans le modèle de menace peut ouvrir l’objet device |
| Atteignabilité | L’IOCTL atteint la branche de terminaison par IRP_MJ_DEVICE_CONTROL |
| Contrôle de la cible | L’appelant fournit ou influence le PID ou le handle utilisé |
| Autorisation insuffisante | Le driver ne valide pas correctement l’identité, les droits ou la cible |
| Gain de capacité | Le driver termine un processus que l’appelant ne peut pas ouvrir directement avec PROCESS_TERMINATE |
| Impact | La cible peut être un processus système, un service de sécurité ou le processus d’un autre utilisateur |
| Reproductibilité | L’effet est confirmé sur les versions exactes du driver, du produit et de Windows |
La démonstration factuelle est donc comparative : un appelant peut ouvrir le device, envoyer la commande avec une cible qu’il contrôle et provoquer une terminaison qu’il ne pourrait pas obtenir directement avec ses propres droits. Si le device est réservé à SYSTEM ou à un service déterminé, si le handler authentifie l’appelant et si les cibles sont strictement limitées, le même appel à ZwTerminateProcess peut relever d’une fonction d’administration intentionnelle.
Cette frontière rejoint directement l’analyse technique des internals EDR, qui distingue l’inscription des capteurs, la livraison des événements, la corrélation et les mécanismes de réponse. Terminer un service ou retirer un signal ne fait pas disparaître les observations déjà collectées par les autres sources.
Réduire le risque BYOVD
L’analyse d’un cas individuel indique où corriger le protocole. La défense à l’échelle d’un parc doit aussi agir avant le chargement et après l’apparition du driver :
- appliquer les mises à jour du fournisseur et retirer les versions vulnérables ;
- activer et vérifier la Microsoft Vulnerable Driver Blocklist, puis compléter la politique avec Windows Defender Application Control ou App Control for Business selon l’environnement ;
- utiliser Hypervisor-protected Code Integrity (HVCI) pour renforcer l’intégrité du code kernel, sans la présenter comme une garantie contre tout driver signé vulnérable ;
- restreindre l’ACL du device, choisir un champ
Accessadapté et valider les tailles, l’identité de l’appelant ainsi que chaque cible dans le handler ; - surveiller l’installation des services de driver, le chargement d’images kernel, les ouvertures de devices sensibles et l’état de santé du produit de sécurité ;
- confirmer sur les endpoints que les politiques attendues sont effectivement actives, car la couverture dépend de la version de Windows, de la configuration et de l’état des listes.
Conclusion
La neutralisation d’un EDR par BYOVD n’est pas la conséquence automatique d’une signature, d’un IOCTL lisible ou d’un import de ZwTerminateProcess. Elle résulte d’une chaîne complète : le driver est admis, son device est accessible, une commande atteint une routine privilégiée, la cible est contrôlable et l’autorisation échoue à contenir l’effet.
Ce fil directeur transforme une recherche de noms de fonctions en analyse de sécurité : établir le contrat, suivre les données, comparer les droits et borner exactement la primitive obtenue.
Références
- Microsoft,
DeviceIoControl,IRP_MJ_DEVICE_CONTROLet définition des codes IOCTL. - Microsoft,
ZwOpenProcessetZwTerminateProcess. - Microsoft, Driver security checklist, SDDL for Device Objects et Microsoft recommended driver block rules.
- LOLDB, base consultée le 23 juillet 2026, interface alimentée par les données du projet LOLDrivers.
- Cisco Talos, Exploring malicious Windows drivers, part 2.
- Altered Security, Evasion Lab.








