
EDRSandblast-GodFault
Par Gabriel Landau chez Elastic Security. Modification de EDRSandblast – voir le README original ci-dessous.
Intègre GodFault dans EDR Sandblast, obtenant le même résultat sans utiliser de pilote vulnérable.
C:\Users\user\Desktop\Offsets>EDRSandblast.exe --kernelmode cmd
| | __ | __ \ / | | | | | | | |
| | | | | | |) | ( __ _ _ __ | | | | | __ _ | |
| | | | | | _ / _ \ / | '_ \ / _ | ' | |/ ` / __| __|
| || || | | \ \ ) | (| | | | | (| | |) | | (| _ | |
||_____/|| _|/ _,|| ||_,|_./||_,|__/__|
D3FC0N 30 Edition | Thomas DIOT (@_Qazeer) & Maxime MEIGNAN (@th3m4ks)
[!] If kernel mode bypass is enabled, it is recommended to enable usermode bypass as well (e.g. to unhook the NtLoadDriver API call)
[===== KERNEL MODE =====]
[+] Setting up prerequisites for the kernel read/write primitives... [+] Loading kernel related offsets from the CSV file [*] System's ntoskrnl.exe file version is: ntoskrnl_22621-1702.exe [+] Offsets are available for this version of ntoskrnl.exe (ntoskrnl_22621-1702.exe)! [+] Checking if any EDR kernel notify rountines are set for image loading, process and thread creations... [+] [NotifyRountines] Enumerating process creation callbacks [+] Running command: GodFault.exe -t 2684 [?] Server does not appear to be running. Attempting to install it... [+] CSRSS PID is 748 [+] Testing initial ability to acquire PROCESS_ALL_ACCESS to System: Failure [+] Ready. Spawning WinTcb. [+] SpawnPPL: Waiting for child process to finish. [+] Thread 2684 (KTHREAD FFFF910961E4C080) has been blessed by GodFault [+] [NotifyRountines] fffff8034df25500 [cng.sys + 0x5500] [+] [NotifyRountines] fffff8034e9efdc0 [WdFilter.sys + 0x4fdc0] [+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys [+] [NotifyRountines] fffff803487bc460 [ksecdd.sys + 0x1c460] [+] [NotifyRountines] fffff8034eff3fd0 [tcpip.sys + 0x13fd0] [+] [NotifyRountines] fffff8034f5ed980 [iorate.sys + 0xd980] [+] [NotifyRountines] fffff8034dea8890 [CI.dll + 0x88890] [+] [NotifyRountines] fffff803525079f0 [dxgkrnl.sys + 0x179f0] [+] [NotifyRountines] fffff80352be0a70 [vm3dmp.sys + 0x10a70] [+] [NotifyRountines] fffff8036ebccd00 [peauth.sys + 0x3cd00] [+] [NotifyRountines] fffff8036eda1550 [wtd.sys + 0x1550] [+] [NotifyRountines] Found a total of 1 EDR / security products driver(s) [+] [NotifyRountines] Enumerating thread creation callbacks [+] [NotifyRountines] fffff8034e9f15c0 [WdFilter.sys + 0x515c0] [+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys [+] [NotifyRountines] fffff8034e9f1350 [WdFilter.sys + 0x51350] [+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys [+] [NotifyRountines] fffff8036eb71010 [mmcss.sys + 0x1010] [+] [NotifyRountines] Found a total of 2 EDR / security products driver(s) [+] [NotifyRountines] Enumerating image loading callbacks [+] [NotifyRountines] fffff8034e9f0820 [WdFilter.sys + 0x50820] [+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys [+] [NotifyRountines] fffff80352ab5710 [ahcache.sys + 0x25710] [+] [NotifyRountines] Found a total of 1 EDR / security products driver(s)
[+] Checking if EDR callbacks are registered on processes and threads handle creation/duplication... [+] [ObjectCallblacks] Enumerating Process object callbacks : [+] [ObjectCallblacks] Callback at FFFF800C5E2F2940 for handle creations & duplications: [+] [ObjectCallblacks] Status: Enabled [+] [ObjectCallblacks] Preoperation at 0xfffff8034e9eda30 [WdFilter.sys + 0x4da30] [+] [ObjectCallblacks] Callback belongs to an EDR and is enabled! [+] [ObjectCallblacks] Enumerating Thread object callbacks : [+] [ObjectCallblacks] Object callbacks are present !
[+] [ETWTI] Checking the ETW Threat Intelligence Provider state... [+] [ETWTI] ETW Threat Intelligence Provider is ENABLED!
[+] Process is NOT "safe" to launch our payload, removing monitoring and starting another process...
[+] [ETWTI] Disabling the ETW Threat Intel provider by patching ProviderEnableInfo at 0xffff91095ce8c430 with 0x00. [+] [ETWTI] The ETW Threat Intel provider was successfully disabled!
[+] Removing kernel callbacks registered by EDR for process creation, thread creation and image loading... [+] [NotifyRountines] Removing process creation callbacks [+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c2a8 | callback struct: 0xffff91095dbf3a5f | callback function: 0xfffff8034e9efdc0] [+] [NotifyRountines] Removing thread creation callbacks [+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c4a0 | callback struct: 0xffff91095dbf3b1f | callback function: 0xfffff8034e9f15c0] [+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c4a8 | callback struct: 0xffff91095dbf3b4f | callback function: 0xfffff8034e9f1350] [+] [NotifyRountines] Removing image loading callbacks [+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c6a0 | callback struct: 0xffff91095dbf3e4f | callback function: 0xfffff8034e9f0820]
[+] Disabling kernel callbacks registered by EDR for process and thread opening or handle duplication... [+] [ObjectCallblacks] Disabling WdFilter.sys callback...
[+] All EDR drivers were successfully removed from Kernel callbacks!
[!] If kernel mode bypass is enabled, it is recommended to enable usermode bypass as well (e.g. to unhook the NtLoadDriver API call)
[===== KERNEL MODE =====]
[+] Setting up prerequisites for the kernel read/write primitives... [+] Loading kernel related offsets from the CSV file [*] System's ntoskrnl.exe file version is: ntoskrnl_22621-1702.exe [+] Offsets are available for this version of ntoskrnl.exe (ntoskrnl_22621-1702.exe)! [+] Checking if any EDR kernel notify rountines are set for image loading, process and thread creations... [+] [NotifyRountines] Enumerating process creation callbacks [+] Running command: GodFault.exe -t 8344 [+] Thread 8344 (KTHREAD FFFF91096169F080) has been blessed by GodFault [+] Initial blessing successful [+] [NotifyRountines] fffff8034df25500 [cng.sys + 0x5500] [+] [NotifyRountines] fffff803487bc460 [ksecdd.sys + 0x1c460] [+] [NotifyRountines] fffff8034eff3fd0 [tcpip.sys + 0x13fd0] [+] [NotifyRountines] fffff8034f5ed980 [iorate.sys + 0xd980] [+] [NotifyRountines] fffff8034dea8890 [CI.dll + 0x88890] [+] [NotifyRountines] fffff803525079f0 [dxgkrnl.sys + 0x179f0] [+] [NotifyRountines] fffff80352be0a70 [vm3dmp.sys + 0x10a70] [+] [NotifyRountines] fffff8036ebccd00 [peauth.sys + 0x3cd00] [+] [NotifyRountines] fffff8036eda1550 [wtd.sys + 0x1550] [+] [NotifyRountines] No EDR driver(s) found! [+] [NotifyRountines] Enumerating thread creation callbacks [+] [NotifyRountines] fffff8036eb71010 [mmcss.sys + 0x1010] [+] [NotifyRountines] No EDR driver(s) found! [+] [NotifyRountines] Enumerating image loading callbacks [+] [NotifyRountines] fffff80352ab5710 [ahcache.sys + 0x25710] [+] [NotifyRountines] No EDR driver(s) found!
[+] Checking if EDR callbacks are registered on processes and threads handle creation/duplication... [+] [ObjectCallblacks] Enumerating Process object callbacks : [+] [ObjectCallblacks] Callback at FFFF800C5E2F2940 for handle creations & duplications: [+] [ObjectCallblacks] Status: Disabled [+] [ObjectCallblacks] Preoperation at 0xfffff8034e9eda30 [WdFilter.sys + 0x4da30] [+] [ObjectCallblacks] Callback belongs to an EDR but is disabled. [+] [ObjectCallblacks] Enumerating Thread object callbacks : [+] [ObjectCallblacks] Object callbacks are not found !
[+] [ETWTI] Checking the ETW Threat Intelligence Provider state... [+] [ETWTI] ETW Threat Intelligence Provider is DISABLED!
[+] Process is "safe" to launch our payload
[+] Kernel callbacks have normally been removed, starting cmd.exe WARNING: EDR kernel callbacks will be restored after exiting the cmd prompt (by typing exit) WARNING: While unlikely, the longer the callbacks are removed, the higher the chance of being detected / causing a BSoD upon restore is!
Microsoft Windows [Version 10.0.22621.1702] (c) Microsoft Corporation. All rights reserved.
C:\Users\user\Desktop\Offsets>
# EDRSandBlast
`EDRSandBlast` est un outil écrit en `C` qui exploite un pilote signé vulnérable
pour contourner les détections EDR (callbacks de routines de notification, callbacks
d'objet et fournisseur `ETW TI`) et les protections de `LSASS`. Plusieurs techniques
de désunhook en espace utilisateur sont également implémentées pour échapper à la
surveillance en espace utilisateur.
À la date de publication, la combinaison des techniques en espace utilisateur
(`--usermode`) et en espace noyau (`--kernelmode`) a été utilisée pour extraire
la mémoire de `LSASS` sous la surveillance d'un EDR, sans être bloqué ni générer
d'événements liés à "OS Credential Dumping" dans la console (cloud) du produit.
Les tests ont été effectués sur 3 produits EDR distincts et ont été réussis dans
chaque cas.
## Description
### Contournement EDR via la suppression des routines de notification du noyau
Les produits EDR utilisent les callbacks "Notify Routines" du noyau sous Windows pour
être notifiés par le noyau de l'activité système, telle que la création de processus
et de threads et le chargement d'images (`exe` / `DLL`).
Ces callbacks noyau sont définis depuis l'espace noyau, généralement par le pilote
implémentant les callbacks, en utilisant un certain nombre d'API documentées
(`nt!PsSetCreateProcessNotifyRoutine`, `nt!PsSetCreateThreadNotifyRoutine`,
etc.). Ces API ajoutent des routines de callback fournies par le pilote à des
tableaux non documentés de routines dans l'espace noyau :
- `PspCreateProcessNotifyRoutine` pour la création de processus
- `PspCreateThreadNotifyRoutine` pour la création de threads
- `PspLoadImageNotifyRoutine` pour le chargement d'images
`EDRSandBlast` énumère les routines définies dans ces tableaux et supprime toute
routine de callback liée à une liste prédéfinie de pilotes EDR (plus de 1000
pilotes de produits de sécurité pris en charge, voir la [section de détection des pilotes EDR](#edr-drivers-and-processes-detection).
L'énumération et la suppression sont rendues possibles grâce à l'exploitation d'une
primitive de lecture/écriture mémoire arbitraire du noyau fournie par l'exploitation
d'un pilote vulnérable (voir la [section des pilotes vulnérables](#vulnerable-drivers-detection)).
Les offsets des tableaux susmentionnés sont récupérés à l'aide de plusieurs techniques,
veuillez vous référer à la [section des offsets](#ntoskrnl-and-wdigest-offsets).
### Contournement EDR via la suppression des callbacks d'objet
Les produits EDR (et même EPP) enregistrent souvent des "Object callbacks" via l'API
noyau `nt!ObRegisterCallbacks`. Ces callbacks permettent au produit de sécurité
d'être notifié à chaque génération de handle sur des types d'objets spécifiques
(les callbacks d'objet liés aux processus, threads et bureaux sont désormais pris
en charge par Windows). Une génération de handle peut se produire lors de l'ouverture
d'un objet (appel à `OpenProcess`, `OpenThread`, etc.) ainsi que lors de la
duplication de handle (appel à `DuplicateHandle`, etc.).
En étant notifié par le noyau pour chacune de ces opérations, un produit de sécurité
peut analyser la légitimité de la création du handle (par exemple, un processus
inconnu tente d'ouvrir LSASS), et même la bloquer si une menace est détectée.
À chaque enregistrement de callback via `ObRegisterCallbacks`, un nouvel élément
est ajouté à la liste doublement chaînée `CallbackList` présente dans l'objet
`_OBJECT_TYPE` décrivant le type d'objet affecté par le callback (soit un processus,
un thread ou un bureau). Malheureusement, ces éléments sont décrits par une structure
qui n'est ni documentée ni publiée dans les fichiers de symboles par Microsoft.
Cependant, son étude à partir de différentes versions de `ntoskrnl.exe` semble
indiquer que la structure n'a pas changé entre (au moins) les builds Windows 10
10240 et 22000 (de 2015 à 2022).
La structure mentionnée, représentant un enregistrement de callback d'objet, est la
suivante :```C
typedef struct OB_CALLBACK_ENTRY_t {
LIST_ENTRY CallbackList; // linked element tied to _OBJECT_TYPE.CallbackList
OB_OPERATION Operations; // bitfield : 1 for Creations, 2 for Duplications
BOOL Enabled; // self-explanatory
OB_CALLBACK* Entry; // points to the structure in which it is included
POBJECT_TYPE ObjectType; // points to the object type affected by the callback
POB_PRE_OPERATION_CALLBACK PreOperation; // callback function called before each handle operation
POB_POST_OPERATION_CALLBACK PostOperation; // callback function called after each handle operation
KSPIN_LOCK Lock; // lock object used for synchronization
} OB_CALLBACK_ENTRY;
La structure OB_CALLBACK mentionnée ci-dessus est également non documentée, et est définie par ce qui suit :```C
typedef struct OB_CALLBACK_t {
USHORT Version; // usually 0x100
USHORT OperationRegistrationCount; // number of registered callbacks
PVOID RegistrationContext; // arbitrary data passed at registration time
UNICODE_STRING AltitudeString; // used to determine callbacks order
struct OB_CALLBACK_ENTRY_t EntryItems[1]; // array of OperationRegistrationCount items
WCHAR AltitudeBuffer[1]; // is AltitudeString.MaximumLength bytes long, and pointed by AltitudeString.Buffer
} OB_CALLBACK;
Afin de désactiver les callbacks d'objets enregistrés par l'EDR, trois techniques sont implémentées dans
`EDRSandblast` ; cependant une seule est activée pour le moment.
#### Utilisation du champ `Enabled` de `OB_CALLBACK_ENTRY`
Il s'agit de la technique activée par défaut dans `EDRSandblast`. Afin de détecter et de désactiver
les callbacks d'objets liés à l'EDR, la liste `CallbackList` située dans les objets `_OBJECT_TYPE`
associés aux types *Process* et *Thread* est parcourue. Les deux `_OBJECT_TYPE` sont
pointés par des symboles globaux publics dans le noyau, `PsProcessType` et `PsThreadType`.
Chaque élément de la liste est supposé correspondre à la structure `OB_CALLBACK_ENTRY` décrite ci-dessus
(hypothèse qui semble tenir au moins dans toutes les versions de Windows 10 au moment de la rédaction).
Les fonctions définies dans les champs `PreOperation` et `PostOperation` sont localisées pour vérifier
si elles appartiennent à un pilote EDR, et si c'est le cas, les callbacks sont simplement désactivés en basculant le drapeau
`Enabled`.
Bien que cette technique soit assez sûre, elle a l'inconvénient de reposer sur une structure
non documentée ; pour réduire le risque de manipulation non sécurisée de cette structure, des vérifications de base sont
effectuées pour valider que certains champs ont les valeurs attendues :
* `Enabled` est soit `TRUE` soit `FALSE` (*ne riez pas, un `BOOL` est un `int`, donc il pourrait s'agir de n'importe quoi d'autre que `1` ou `0`*);
* `Operations` est `OB_OPERATION_HANDLE_CREATE`, `OB_OPERATION_HANDLE_DUPLICATE` ou les deux;
* `ObjectType` pointe vers `PsProcessType` ou `PsThreadType`.
#### Désolidarisation de la `CallbackList` des threads et processus
Une autre stratégie qui ne repose pas sur une structure non documentée (et est donc théoriquement
plus robuste face aux changements du noyau NT) est la désolidarisation de l'ensemble de la `CallbackList`
pour les processus et les threads. L'objet `_OBJECT_TYPE` est le suivant :```C
struct _OBJECT_TYPE {
LIST_ENTRY TypeList;
UNICODE_STRING Name;
[...]
_OBJECT_TYPE_INITIALIZER TypeInfo;
[...]
LIST_ENTRY CallbackList;
}
Faire pointer les pointeurs Flink et Blink de la CallbackList LIST_ENTRY vers la LIST_ENTRY elle-même rend effectivement la liste vide. Étant donné que la structure _OBJECT_TYPE est publiée dans les symboles du noyau, la technique ne repose pas sur des décalages/structures codés en dur. Cependant, elle présente quelques inconvénients.
Le premier est de ne pas pouvoir désactiver uniquement les callbacks de l'EDR ; en effet, la technique affecte tous les callbacks d'objets qui auraient pu être enregistrés par des logiciels « légitimes ». Il convient néanmoins de noter que les callbacks d'objets ne sont utilisés par aucun composant préinstallé sur Windows 10 (au moment de la rédaction), donc leur désactivation ne devrait pas affecter la stabilité de la machine (encore moins si la désactivation n'est que temporaire).
Le deuxième inconvénient est que les opérations de handles de processus ou de threads sont vraiment fréquentes (presque continues) dans le fonctionnement normal du système d'exploitation. Ainsi, si la primitive d'écriture dans le noyau utilisée ne peut pas effectuer une écriture QWORD « atomiquement », il y a de fortes chances que le pointeur _OBJECT_TYPE.CallbackList.Flink soit accédé par le noyau au milieu de sa réécriture. Par exemple, le pilote vulnérable MSI RTCore64.sys ne peut effectuer qu'une écriture DWORD à la fois, donc 2 IOCTL distincts seront nécessaires pour réécrire le pointeur, entre lesquels le noyau a une forte probabilité de l'utiliser (provoquant un crash). En revanche, le pilote vulnérable DELL DBUtil_2_3.sys peut effectuer des écritures de tailles arbitraires en un seul IOCTL, donc utiliser cette méthode avec lui ne risque pas de provoquer un crash.
Une dernière technique que nous avons trouvée consiste à désactiver entièrement le support des callbacks d'objets pour les threads et les processus. À l'intérieur de la structure _OBJECT_TYPE correspondant aux types processus et thread se trouve un champ TypeInfo, suivant la structure documentée _OBJECT_TYPE_INITIALIZER. Cette dernière contient un champ de bits ObjectTypeFlags, dont le flag SupportsObjectCallbacks détermine si le type d'objet décrit (Processus, Thread, Bureau, Jeton, Fichier, etc.) supporte ou non l'enregistrement de callbacks d'objets. Comme indiqué précédemment, seuls les types d'objets Processus, Thread et Bureau supportent ces callbacks sur une installation Windows au moment de la rédaction.
Étant donné que le bit SupportsObjectCallbacks est vérifié par ObpCreateHandle ou ObDuplicateObject avant même de lire la CallbackList (et avant d'exécuter les callbacks, bien sûr), inverser le bit à l'exécution du noyau désactive effectivement toute exécution de callbacks d'objets.
Le principal inconvénient de cette méthode est simplement que KPP (« PatchGuard ») surveille l'intégrité de certaines (toutes ?) structures _OBJECT_TYPE, et déclenche un 0x109 Bug Check avec le paramètre 4 égal à 0x8, ce qui signifie qu'une structure de type d'objet a été modifiée.
Cependant, effectuer la désactivation / réactivation (et l'action « malveillante » entre les deux) assez rapidement devrait suffire pour « distancer » PatchGuard (sauf si vous n'avez pas de chance et qu'une vérification périodique est effectuée au mauvais moment).
Le fournisseur ETW Microsoft-Windows-Threat-Intelligence enregistre les données concernant les utilisations de certaines API Windows couramment utilisées de manière malveillante. Cela inclut l'API nt!MiReadWriteVirtualMemory, appelée par nt!NtReadVirtualMemory (qui est utilisée pour vider la mémoire de LSASS) et surveillée par la fonction nt!EtwTiLogReadWriteVm.
Les produits EDR peuvent consommer les journaux produits par le fournisseur ETW TI via des services ou des processus s'exécutant respectivement sous SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT ou PS_PROTECTED_ANTIMALWARE_LIGHT, et associés à un pilote Early Launch Anti Malware (ELAM).
Comme publié par slaeryan dans un article de blog CNO Development Labs, le fournisseur ETW TI peut être désactivé complètement en corrigeant, dans la mémoire du noyau, son attribut ProviderEnableInfo à 0x0. Référez-vous à l'excellent article de blog susmentionné pour plus d'informations sur la technique.
De la même manière que pour la suppression des callbacks du noyau, les décalages nécessaires de ntoskrnl.exe (nt!EtwThreatIntProvRegHandleOffset, GuidEntry de _ETW_REG_ENTRY, et ProviderEnableInfo de _ETW_GUID_ENTRY) sont calculés dans le fichier NtoskrnlOffsets.csv pour un certain nombre de versions du noyau Windows.
Afin de surveiller facilement les actions effectuées par les processus, les produits EDR déploient souvent un mécanisme appelé hooking en espace utilisateur. Tout d'abord, les produits EDR enregistrent un callback du noyau (généralement des callbacks de chargement d'image ou de création de processus, voir ci-dessus) qui leur permet d'être notifiés à chaque démarrage de processus.
Lorsqu'un processus est chargé par Windows, et avant qu'il ne démarre réellement, l'EDR est capable d'injecter une DLL personnalisée dans l'espace d'adressage du processus, qui contient sa logique de surveillance. Pendant le chargement, cette DLL injecte des « hooks » au début de chaque fonction qui doit être surveillée par l'EDR. Au moment de l'exécution, lorsque les fonctions surveillées sont appelées par le processus sous surveillance, ces hooks redirigent le flux de contrôle vers un code de supervision présent dans la DLL de l'EDR, ce qui lui permet d'inspecter les arguments et les valeurs de retour de ces appels.
La plupart du temps, les fonctions surveillées sont des appels système (tels que NtReadVirtualMemory, NtOpenProcess, etc.), dont les implémentations résident dans ntdll.dll. Intercepter les appels aux fonctions Nt* permet aux produits d'être aussi proches que possible de la frontière espace utilisateur / espace noyau (tout en restant en espace utilisateur), mais les fonctions de certaines DLL de plus haut niveau peuvent également être surveillées.
Voici des exemples de la même fonction, avant et après avoir été hookée par le produit EDR :```assembly NtProtectVirtualMemory proc near mov r10, rcx mov eax, 50h test byte ptr ds:7FFE0308h, 1 jnz short loc_18009D1E5 syscall retn loc_18009D1E5: int 2Eh retn NtProtectVirtualMemory endp
messages de débogage en fournissant une sortie informative, ce qui peut aider au dépannage ou à la surveillance du comportement de l'application.
## Configuration
L'outil lit sa configuration à partir des variables d'environnement au démarrage. Les variables suivantes sont prises en charge :```assembly
NtProtectVirtualMemory proc near
jmp sub_7FFC74490298 ; --> "hook", jump to EDR analysis function
int 3 ; overwritten instructions
int 3 ; overwritten instructions
int 3 ; overwritten instructions
test byte_7FFE0308, 1 ; <-- execution resumes here after analysis
jnz short loc_7FFCB44AD1E5
syscall
retn
loc_7FFCB44AD1E5:
int 2Eh
retn
NtProtectVirtualMemory endp
Les hooks en espace utilisateur ont la "faiblesse" d'être situés dans la mémoire utilisateur, ce qui signifie qu'ils sont directement observables et modifiables par le processus examiné. Pour détecter automatiquement les hooks dans l'espace d'adressage du processus, l'idée principale est de comparer les différences entre la DLL originale sur le disque et la bibliothèque résidant en mémoire, qui a été potentiellement modifiée par un EDR. Pour effectuer cette comparaison, les étapes suivantes sont suivies par EDRSandblast :
InLoadOrderModuleList située
dans le PEB (pour éviter d'appeler une API qui pourrait être surveillée et suspecte)Note : Le processus peut être généralisé pour trouver des différences n'importe où dans les sections non inscriptibles
et pas seulement au début des fonctions exportées, par exemple si les produits EDR commencent à
appliquer des hooks au milieu d'une fonction :) Ainsi, non utilisé par l'outil, cela a été
implémenté dans findDiffsInNonWritableSections.
Pour contourner la surveillance effectuée par ces hooks, plusieurs techniques sont possibles, chacune avec ses avantages et inconvénients.
La méthode la plus intuitive pour contourner la surveillance basée sur les hooks est de supprimer les hooks. Comme les hooks sont présents dans une mémoire accessible par le processus lui-même, pour supprimer un hook, le processus peut simplement :
Cette approche est assez simple et peut être utilisée pour supprimer tous les hooks détectés d'un seul coup. Effectuée par un outil offensif au début de son exécution, cela permet au reste du code de rester complètement ignorant du mécanisme de hooking et de fonctionner normalement sans être surveillé.
Cependant, elle présente deux inconvénients principaux. L'EDR surveille probablement l'utilisation de
NtProtectVirtualMemory, donc l'utiliser pour modifier les permissions de la page où les
hooks ont été installés est (au moins conceptuellement) une mauvaise idée. De plus, si un thread est
exécuté par l'EDR et vérifie périodiquement l'intégrité des hooks, cela pourrait aussi
déclencher une détection.
Pour les détails d'implémentation, consultez le chemin de code de la fonction unhook() lorsque unhook_method est
UNHOOK_WITH_NTPROTECTVIRTUALMEMORY.
Note importante : par souci de simplicité, cette technique est implémentée dans EDRSandblast comme la
technique de base utilisée pour montrer les autres techniques de contournement ; chacune d'elles démontre
comment obtenir une version non surveillée de NtProtectVirtualMemory, mais effectue la même
opération ensuite (désaccrocher un hook spécifique).
Pour contourner un hook spécifique, il est possible de simplement "sauter par-dessus" et exécuter le reste de la fonction telle quelle. D'abord, les octets d'origine de la fonction surveillée, qui ont été écrasés par l'EDR pour installer le hook, doivent être récupérés depuis le fichier DLL. Dans notre exemple de code précédent, ce seraient les octets correspondant aux instructions suivantes :```assembly mov r10, rcx mov eax, 50h
Identifier ces octets est une tâche simple puisque nous sommes capables d'effectuer un *diff* propre des versions mémoire et disque de la bibliothèque, comme décrit précédemment. Ensuite, nous assemblons une instruction de saut conçue pour rediriger le flux de contrôle vers le code suivant immédiatement le hook, à l'adresse `NtProtectVirtualMemory + sizeof(overwritten_instructions)````assembly
jmp NtProtectVirtualMemory+8
Enfin, nous concaténons ces opcodes, les stockons dans une mémoire (nouvellement) exécutable et conservons un pointeur vers eux. Cet objet est appelé une "trampoline" et peut alors être utilisé comme un pointeur de fonction, strictement équivalent à la fonction NtProtectVirtualMemory originale.
Le principal avantage de cette technique, comme pour toutes les techniques ci-dessous, est que le hook n'est jamais effacé, donc toute vérification d'intégrité effectuée sur les hooks par l'EDR devrait réussir. Cependant, cela nécessite d'allouer de la mémoire d'abord accessible en écriture puis en exécution, ce qui est typique d'une allocation de shellcode, attirant ainsi l'attention de l'EDR.
Pour les détails d'implémentation, consultez le chemin de code de la fonction unhook() lorsque unhook_method est UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE. Veuillez vous rappeler que la technique n'est présentée que dans notre implémentation et est, en fin de compte, utilisée pour supprimer les hooks de la mémoire, comme chaque technique ci-dessous.
Le produit EDR, pour que son hook fonctionne, doit sauvegarder quelque part en mémoire les opcodes qu'il a supprimés. Pire (ou « mieux », du point de vue de l'attaquant), pour utiliser efficacement les instructions originales, l'EDR s'est probablement alloué une trampoline quelque part pour exécuter la fonction originale après avoir intercepté l'appel.
Cette trampoline peut être recherchée et utilisée comme remplacement de la fonction hookée, sans avoir besoin d'allouer de mémoire exécutable, ni d'appeler une API autre que VirtualQuery, qui n'est très probablement pas surveillée car c'est une fonction inoffensive.
Pour trouver la trampoline en mémoire, nous parcourons tout l'espace d'adressage en utilisant VirtualQuery à la recherche de mémoire engagée et exécutable. Pour chaque région de mémoire de ce type, nous la scannons pour chercher une instruction de saut qui cible l'adresse suivant les instructions écrasées (NtProtectVirtualMemory+8 dans notre exemple précédent). La trampoline peut alors être utilisée pour appeler la fonction hookée sans déclencher le hook.
Cette technique fonctionne étonnamment bien car elle récupère presque toutes les trampolines sur l'EDR testé. Pour les détails d'implémentation, consultez le chemin de code de la fonction unhook() lorsque unhook_method est UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE.
Une autre méthode simple pour accéder à une version non surveillée de la fonction NtProtectVirtualMemory est de charger une version en double de la bibliothèque ntdll.dll dans l'espace d'adressage du processus. Puisque deux DLL identiques peuvent être chargées dans le même processus, à condition qu'elles aient des noms différents, nous pouvons simplement copier le fichier ntdll.dll légitime vers un autre emplacement, le charger en utilisant LoadLibrary (ou réimplémenter le processus de chargement), et accéder à la fonction en utilisant GetProcAddress par exemple.
Cette technique est très simple à comprendre et à implémenter, et a une chance décente de succès, car la plupart des produits EDR ne réinstallent pas les hooks sur les DLL nouvellement chargées une fois le processus en cours d'exécution. Cependant, l'inconvénient majeur est que copier des binaires signés par Microsoft sous un nom différent est souvent considéré comme suspect par les produits EDR eux-mêmes.
Cette technique est néanmoins implémentée dans EDRSandblast. Pour les détails d'implémentation, consultez le chemin de code de la fonction unhook() lorsque unhook_method est UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY.
Afin d'utiliser des fonctions liées aux appels système, un programme peut réimplémenter les appels système (en assembleur) pour appeler les fonctionnalités OS correspondantes sans toucher au code dans ntdll.dll, qui pourrait être surveillé par l'EDR. Cela contourne complètement tout hook en espace utilisateur effectué sur les fonctions d'appel système dans ntdll.dll.
Cela présente néanmoins certains inconvénients. Premièrement, cela implique de pouvoir connaître la liste des numéros d'appels système des fonctions dont le programme a besoin, qui changent pour chaque version de Windows. Ceci est néanmoins atténué en implémentant plusieurs heuristiques qui sont connues pour fonctionner dans toutes les versions passées de Windows NT (tri des exports Zw* de ntdll, recherche de l'instruction mov rax, #numéro_appel_système dans la fonction ntdll associée, etc.), et en vérifiant qu'elles renvoient toutes le même résultat (voir Syscalls.c pour plus de détails).
De plus, les fonctions qui ne sont pas techniquement des appels système (par ex. LoadLibraryX/LdrLoadDLL) pourraient également être surveillées, et ne peuvent pas simplement être réimplémentées en utilisant un appel système.
La technique des appels système directs est implémentée dans EDRSandblast. Comme indiqué précédemment, elle est uniquement utilisée pour exécuter NtProtectVirtualMemory en toute sécurité et supprimer tous les hooks détectés.
Pour les détails d'implémentation, consultez le chemin de code de la fonction unhook() lorsque unhook_method est UNHOOK_WITH_DIRECT_SYSCALL.
Comme indiqué précédemment, chaque action nécessitant une lecture ou écriture de mémoire noyau repose sur un pilote vulnérable pour fournir cette primitive. Dans EDRSandblast, ajouter le support d'un nouveau pilote fournissant la primitive de lecture/écriture peut se faire « facilement », seules trois fonctions doivent être implémentées :
ReadMemoryPrimitive_NOMDUPILOTE(SIZE_T Size, DWORD64 Address, PVOID Buffer), qui copie Size octets de l'adresse noyau Address vers le tampon utilisateur Buffer;WriteMemoryPrimitive_NOMDUPILOTE(SIZE_T Size, DWORD64 Address, PVOID Buffer), qui copie Size octets du tampon utilisateur Buffer vers l'adresse noyau Address;CloseDriverHandle_NOMDUPILOTE() qui garantit que tous les handles vers le pilote sont fermés (nécessaire avant l'opération de désinstallation qui est indépendante du pilote, pour le moment).À titre d'exemple, deux pilotes sont actuellement supportés par EDRSandblast, RTCore64.sys (SHA256 : 01AA278B07B58DC46C84BD0B1B5C8E9EE4E62EA0BF7A695862444AF32E87F1FD) et DBUtils_2_3.sys (SHA256 : 0296e2ce999e67c76352613a718e11516fe1b0efc3ffdb8918fc999dd76a73a5). Le code suivant dans KernelMemoryPrimitives.h doit être mis à jour si le pilote vulnérable utilisé doit être changé, ou si un nouveau est implémenté.```C
#define RTCore 0
#define DBUtil 1
// Select the driver to use with the following #define
#define VULN_DRIVER RTCore
#if VULN_DRIVER == RTCore #define DEFAULT_DRIVER_FILE TEXT("RTCore64.sys") #define CloseDriverHandle CloseDriverHandle_RTCore #define ReadMemoryPrimitive ReadMemoryPrimitive_RTCore #define WriteMemoryPrimitive WriteMemoryPrimitive_RTCore #elif VULN_DRIVER == DBUtil #define DEFAULT_DRIVER_FILE TEXT("DBUtil_2_3.sys") #define CloseDriverHandle CloseDriverHandle_DBUtil #define ReadMemoryPrimitive ReadMemoryPrimitive_DBUtil #define WriteMemoryPrimitive WriteMemoryPrimitive_DBUtil #endif
### Détection des pilotes et processus EDR
Plusieurs techniques sont actuellement utilisées pour déterminer si un pilote ou un processus spécifique appartient ou non à un produit EDR.
Tout d'abord, le nom du pilote peut simplement être utilisé à cette fin. En effet, Microsoft alloue des numéros spécifiques appelés « Altitudes » à tous les pilotes qui doivent insérer des rappels dans le noyau. Cela permet un ordre déterministe dans l'exécution des rappels, indépendant de l'ordre d'enregistrement, mais basé uniquement sur l'utilisation du pilote. Une liste de (fournisseurs de) pilotes ayant réservé une *altitude* spécifique peut être trouvée [sur MSDN](https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/allocated-altitudes). Par conséquent, une liste quasi exhaustive des noms de pilotes de sécurité liés aux produits de sécurité est fournie par Microsoft, principalement dans les listes « FSFilter Anti-Virus » et « FSFilter Activity Monitor ». Ces listes de noms de pilotes sont intégrées dans EDRSandblast, ainsi que des contributions supplémentaires.
De plus, les exécutables et DLL des EDR sont bien souvent signés numériquement à l'aide du certificat de signature du fournisseur. Ainsi, la vérification du signataire d'un exécutable ou d'une DLL associé à un processus peut permettre d'identifier rapidement les produits EDR.
Aussi, les pilotes doivent être directement signés par Microsoft pour être autorisés à être chargés dans l'espace noyau. Bien que le fournisseur du pilote ne soit pas directement le signataire du pilote lui-même, il semblerait que le nom du fournisseur soit toujours inclus dans un attribut de la signature ; cette technique de détection reste néanmoins à étudier et à implémenter.
Enfin, face à un EDR inconnu d'EDRSandblast, la meilleure approche est d'exécuter l'outil en mode « audit », et de vérifier la liste des pilotes ayant enregistré des rappels noyau ; le nom du pilote peut alors être ajouté à la liste, l'outil recompilé et ré-exécuté.
### Contournement de RunAsPPL
Le mécanisme de « Protection de l'autorité de sécurité locale (LSA) », introduit pour la première fois dans Windows 8.1 et Windows Server 2012 R2, utilise la technologie `Protected Process Light (PPL)` pour restreindre l'accès au processus `LSASS`. La protection `PPL` régule et restreint les opérations, telles que l'injection mémoire ou le dump mémoire des processus protégés, même depuis un processus disposant du privilège `SeDebugPrivilege`. Dans le modèle de protection des processus, seuls les processus s'exécutant avec des niveaux de protection supérieurs peuvent effectuer des opérations sur les processus protégés.
La structure `_EPROCESS`, utilisée par le noyau Windows pour représenter un processus dans la mémoire noyau, inclut un champ `_PS_PROTECTION` définissant le niveau de protection d'un processus via ses attributs `Type` (`_PS_PROTECTED_TYPE`) et `Signer` (`_PS_PROTECTED_SIGNER`).
En écrivant dans la mémoire noyau, le processus EDRSandblast est capable d'élever son propre niveau de protection à `PsProtectedSignerWinTcb-Light`. Ce niveau est suffisant pour dumper la mémoire du processus `LSASS`, car il « domine » `PsProtectedSignerLsa-Light`, le niveau de protection du processus `LSASS` s'exécutant avec le mécanisme `RunAsPPL`.
`EDRSandBlast` implémente l'auto-protection comme suit :
- ouvrir un handle sur le processus courant
- fuiter tous les handles système en utilisant `NtQuerySystemInformation` pour trouver le handle ouvert sur le processus courant, et l'adresse de la structure `EPROCESS` du processus courant dans la mémoire noyau.
- utiliser la vulnérabilité de lecture/écriture arbitraire du pilote `Micro-Star MSI Afterburner` pour écraser le champ `_PS_PROTECTION` du processus courant dans la mémoire noyau. Les décalages du champ `_PS_PROTECTION` relatifs à la structure `EPROCESS` (définis par la version de `ntoskrnl` utilisée) sont calculés dans le fichier `NtoskrnlOffsets.csv`.
### Contournement de Credential Guard
Microsoft `Credential Guard` est une technologie d'isolation basée sur la virtualisation, introduite dans Microsoft `Windows 10 (édition Entreprise)` qui empêche l'accès direct aux informations d'identification stockées dans le processus `LSASS`.
Lorsque `Credential Guard` est activé, un processus `LSAIso` (*LSA Isolated*) est créé dans `Virtual Secure Mode`, une fonctionnalité qui exploite les extensions de virtualisation du CPU pour offrir une sécurité accrue des données en mémoire. L'accès au processus `LSAIso` est restreint même pour un accès avec le contexte de sécurité `NT AUTHORITY\SYSTEM`. Lors du traitement d'un hachage, le processus `LSA` effectue un appel `RPC` vers le processus `LSAIso`, et attend le résultat de `LSAIso` pour continuer. Ainsi, le processus `LSASS` ne contiendra aucun secret et stockera à la place des « données isolées LSA ».
Comme indiqué dans la recherche originale menée par `N4kedTurtle` : « `Wdigest` peut être activé sur un système avec Credential Guard en corrigeant les valeurs de `g_fParameter_useLogonCredential` et `g_IsCredGuardEnabled` en mémoire ». L'activation de `Wdigest` entraînera le stockage d'identifiants en clair dans la mémoire de `LSASS` pour toute nouvelle connexion interactive (sans nécessiter de redémarrage du système). Référez-vous au [billet de blog de recherche original](https://teamhydra.blog/2020/08/25/bypassing-credential-guard/) pour plus de détails sur cette technique.
`EDRSandBlast` rend simplement le PoC original un peu plus compatible OPSEC et offre un support pour un certain nombre de versions de `wdigest.dll` (via des décalages calculés pour `g_fParameter_useLogonCredential` et `g_IsCredGuardEnabled`).
### Récupération des décalages
Afin d'effectuer de manière fiable les opérations de contournement de la surveillance noyau, EDRSandblast doit savoir exactement où lire et écrire dans la mémoire noyau. Cela est fait en utilisant des décalages de variables globales dans l'image ciblée (ntoskrnl.exe, wdigest.dll), ainsi que le décalage de champs spécifiques dans des structures dont les définitions sont publiées par Microsoft dans des fichiers de symboles. Ces décalages sont spécifiques à chaque build des images ciblées et doivent être collectés au moins une fois pour une version de plateforme spécifique.
Le choix d'utiliser des décalages « codés en dur » plutôt que des recherches de motifs pour localiser les structures et variables utilisées par EDRSandblast est justifié par le fait que les API non documentées responsables de l'ajout/suppression des rappels noyau sont susceptibles de changer et que toute tentative de lecture ou d'écriture de la mémoire noyau à la mauvaise adresse peut (et souvent entraînera) un `Bug Check` (`Blue Screen of Death`). Un crash machine n'est pas acceptable dans les scénarios de red-teaming et de tests d'intrusion normaux, car une machine qui plante est hautement visible par les défenseurs et perdra toutes les informations d'identification encore en mémoire au moment de l'attaque.
Pour récupérer les décalages pour chaque version spécifique de Windows, deux approches sont implémentées.
#### Récupération manuelle des décalages
Les décalages requis pour `ntoskrnl.exe` et `wdigest.dll` peuvent être extraits à l'aide du script Python `ExtractOffsets.py` fourni, qui s'appuie sur `radare2` et `r2pipe` pour télécharger et analyser les symboles à partir des fichiers PDB, et en extrait les décalages nécessaires. Les décalages sont ensuite stockés dans des fichiers CSV pour une utilisation ultérieure par EDRSandblast.
Afin de supporter prêts à l'emploi une large gamme de builds Windows, de nombreuses versions des binaires `ntoskrnl.exe` et `wdigest.dll` sont référencées par [Winbindex](https://winbindex.m417z.com/) et peuvent être automatiquement téléchargées (et leurs décalages extraits) par `ExtractOffsets.py`. Cela permet d'extraire les décalages de presque tous les fichiers jamais publiés dans les mises à jour Windows (à ce jour, plus de 450 versions de `ntoskrnl.exe` et plus de 30 versions de `wdigest.dll` sont disponibles et pré-calculées).
#### Récupération et mise à jour automatiques des décalages
Une option supplémentaire a été implémentée dans `EDRSandBlast` pour permettre au programme de télécharger lui-même les fichiers `.pdb` nécessaires depuis le serveur de symboles Microsoft, d'extraire les décalages requis, et même de mettre à jour les fichiers `.csv` correspondants s'ils sont présents.
L'utilisation de l'option `--internet` rend l'exécution de l'outil beaucoup plus simple, tout en introduisant un risque OPSEC supplémentaire, car un fichier `.pdb` est téléchargé et déposé sur le disque pendant le processus. Ceci est requis par les fonctions `dbghelp.dll` utilisées pour analyser la base de données de symboles ; cependant, une analyse complète du PDB en mémoire pourrait être implémentée à l'avenir pour lever cette exigence et réduire l'empreinte de l'outil.
## Utilisation
Le pilote vulnérable `RTCore64.sys` peut être récupéré à l'adresse :```
http://download-eu2.guru3d.com/afterburner/%5BGuru3D.com%5D-MSIAfterburnerSetup462Beta2.zip
Usage: EDRSandblast.exe [-h | --help] [-v | --verbose] <audit | dump | cmd | credguard> [--usermode [--unhook-method ]] [--kernelmode] [--dont-unload-driver] [--dont-restore-callbacks] [--driver <RTCore64.sys>] [--service <SERVICE_NAME>] [--nt-offsets <NtoskrnlOffsets.csv>] [--wdigest-offsets <WdigestOffsets.csv>] [--add-dll ]* [-o | --dump-output <DUMP_FILE>]
### Options```
-h | --help Show this help message and exit.
-v | --verbose Enable a more verbose output.
Actions mode:
audit Display the user-land hooks and / or Kernel callbacks without taking actions.
dump Dump the LSASS process, by default as 'lsass' in the current directory or at the
specified file using -o | --output <DUMP_FILE>.
cmd Open a cmd.exe prompt.
credguard Patch the LSASS process' memory to enable Wdigest cleartext passwords caching even if
Credential Guard is enabled on the host. No kernel-land actions required.
--usermode Perform user-land operations (DLL unhooking).
--kernelmode Perform kernel-land operations (Kernel callbacks removal and ETW TI disabling).
--unhook-method <N>
Choose the userland un-hooking technique, from the following:
1 (Default) Uses the (probably monitored) NtProtectVirtualMemory function in ntdll to remove all
present userland hooks.
2 Constructs a 'unhooked' (i.e. unmonitored) version of NtProtectVirtualMemory, by
allocating an executable trampoline jumping over the hook, and remove all present
userland hooks.
3 Searches for an existing trampoline allocated by the EDR itself, to get an 'unhooked'
(i.e. unmonitored) version of NtProtectVirtualMemory, and remove all present userland
hooks.
4 Loads an additional version of ntdll library into memory, and use the (hopefully
unmonitored) version of NtProtectVirtualMemory present in this library to remove all
present userland hooks.
5 Allocates a shellcode that uses a direct syscall to call NtProtectVirtualMemory,
and uses it to remove all detected hooks
Other options:
--dont-unload-driver Keep the vulnerable driver installed on the host
Default to automatically unsinstall the driver.
--dont-restore-callbacks Do not restore the EDR drivers' Kernel Callbacks that were removed.
Default to restore the callbacks.
--driver <RTCore64.sys> Path to the vulnerable driver file.
Default to 'RTCore64.sys' in the current directory.
--service <SERVICE_NAME> Name of the vulnerable service to intall / start.
--nt-offsets <NtoskrnlOffsets.csv> Path to the CSV file containing the required ntoskrnl.exe's offsets.
Default to 'NtoskrnlOffsets.csv' in the current directory.
--wdigest-offsets <WdigestOffsets.csv> Path to the CSV file containing the required wdigest.dll's offsets
(only for the 'credguard' mode).
Default to 'WdigestOffsets.csv' in the current directory.
--add-dll <dll name or path> Loads arbitrary libraries into the process' address space, before starting
anything. This can be useful to audit userland hooking for DLL that are not
loaded by default by this program. Use this option multiple times to load
multiple DLLs all at once.
Example of interesting DLLs to look at: user32.dll, ole32.dll, crypt32.dll,
samcli.dll, winhttp.dll, urlmon.dll, secur32.dll, shell32.dll...
-o | --output <DUMP_FILE> Output path to the dump file that will be generated by the 'dump' mode.
Default to 'lsass' in the current directory.
-i | --internet Enables automatic symbols download from Microsoft Symbol Server
If a corresponding *Offsets.csv file exists, appends the downloaded offsets to the file for later use
OpSec warning: downloads and drops on disk a PDB file for ntoskrnl.exe and/or wdigest.dll
EDRSandBlast (x64 uniquement) a été compilé avec Visual Studio 2019 (Windows SDK
Version : 10.0.19041.0 et Plateform Toolset : Visual Studio 2019 (v142)).
Notez que ExtractOffsets.py a seulement été testé sur Windows.```
pip.exe install -m .\requirements.txt
ExtractOffsets.py [-h] -i INPUT [-o OUTPUT] [-d] mode
positional arguments: mode ntoskrnl or wdigest. Mode to download and extract offsets for either ntoskrnl or wdigest
optional arguments: -h, --help show this help message and exit -i INPUT, --input INPUT Single file or directory containing ntoskrnl.exe / wdigest.dll to extract offsets from. If in download mode, the PE downloaded from MS symbols servers will be placed in this folder. -o OUTPUT, --output OUTPUT CSV file to write offsets to. If the specified file already exists, only new ntoskrnl versions will be downloaded / analyzed. Defaults to NtoskrnlOffsets.csv / WdigestOffsets.csv in the current folder. -d, --download Flag to download the PE from Microsoft servers using list of versions from winbindex.m417z.com.
## Detection
Du point de vue du défenseur (fournisseur EDR, Microsoft, analystes SOC consultant la télémétrie de l'EDR, ...), plusieurs indicateurs peuvent être utilisés pour détecter ou prévenir ce type de techniques.
### Liste blanche des pilotes
Étant donné que chaque action effectuée par l'outil dans la mémoire en mode noyau repose sur un pilote vulnérable pour lire/écrire du contenu arbitraire, les événements de chargement de pilotes doivent être scrutés attentivement par le produit EDR (ou les analystes SOC), et déclencher une alerte pour tout chargement de pilote inhabituel, ou même bloquer les pilotes vulnérables connus. Cette dernière approche est même [recommandée par Microsoft eux-mêmes](https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/microsoft-recommended-driver-block-rules) : tout appareil Windows activé avec HVCI (*Hypervisor-protected code integrity*) intègre une liste de blocage de pilotes, et cela deviendra progressivement un comportement par défaut sur Windows (c'est déjà le cas sur Windows 11).
### Vérifications d'intégrité de la mémoire du noyau
Étant donné qu'un attaquant pourrait encore utiliser un pilote vulnérable inconnu pour effectuer les mêmes actions en mémoire, le pilote EDR pourrait périodiquement vérifier que ses callbacks noyau sont toujours enregistrés, en inspectant directement la mémoire du noyau (comme cet outil le fait), ou simplement en déclenchant des événements (création de processus, création de threads, chargement d'image, etc.) et en vérifiant que les fonctions de callback sont bien appelées par le noyau exécutif.
En remarque, ce type de structure de données pourrait être protégé via le récent mécanisme [Kernel Data Protection (KDP)](https://www.microsoft.com/security/blog/2020/07/08/introducing-kernel-data-protection-a-new-platform-security-technology-for-preventing-data-corruption/), qui repose sur la Virtual Based Security, afin de rendre le tableau de callbacks noyau non inscriptible sans appeler les bonnes API.
La même logique pourrait s'appliquer aux variables ETW sensibles telles que `ProviderEnableInfo`, utilisé abusivement par cet outil pour désactiver la génération d'événements ETW Threat Intelligence.
### Détection en mode utilisateur
Le premier indicateur qu'un processus tente activement d'échapper au hooking en mode utilisateur est l'accès aux fichiers de chaque DLL correspondant aux modules chargés ; dans une exécution normale, un processus en mode utilisateur a rarement besoin de lire les fichiers DLL en dehors d'un appel `LoadLibrary`, en particulier `ntdll.dll`.
Afin de protéger le hooking d'API contre le contournement, les produits EDR pourraient périodiquement vérifier que les hooks ne sont pas modifiés en mémoire, à l'intérieur de chaque processus surveillé.
Enfin, pour détecter le contournement de hook (abus d'une trampoline, utilisation d'appels système directs, etc.) qui n'implique pas la suppression des hooks, les produits EDR pourraient potentiellement s'appuyer sur des callbacks noyau associés aux appels système abusés (ex. `PsCreateProcessNotifyRoutine` pour l'appel système `NtCreateProcess`, `ObRegisterCallbacks` pour l'appel système `NtOpenProcess`, etc.), et effectuer une analyse de la pile d'appels en mode utilisateur afin de déterminer si l'appel système a été déclenché depuis un chemin normal (`kernel32.dll` -> `ntdll.dll` -> syscall) ou anormal (ex. `program.exe` -> appel système direct).
## Remerciements
- Énumération et suppression des callbacks noyau :
https://github.com/br-sn/CheekyBlinder
- Primitives de lecture/écriture de la mémoire noyau via le pilote vulnérable
`Micro-Star MSI Afterburner` :
https://github.com/Barakat/CVE-2019-16098/
- Désactivation du fournisseur ETW Threat Intelligence :
https://public.cnotools.studio/bring-your-own-vulnerable-kernel-driver-byovkd/exploits/data-only-attack-neutralizing-etwti-provider
- Installation / désinstallation de pilotes : https://github.com/gentilkiwi/mimikatz
- Liste initiale des noms de pilotes EDR :
https://github.com/SadProcessor/SomeStuff/blob/master/Invoke-EDRCheck.ps1
- Contournement de Credential Guard en réactivant `Wdigest` via le patching de la mémoire `LSASS` :
https://teamhydra.blog/2020/08/25/bypassing-credential-guard/
## Auteurs
[Thomas DIOT (Qazeer)](https://github.com/Qazeer/)
[Maxime MEIGNAN (themaks)](https://github.com/themaks)
## Licence
Licence CC BY 4.0 - https://creativecommons.org/licenses/by/4.0/