CONTOURNEMENT D'ANTIVIRUS ET D'EDR đŸ±â€

Les techniques présentées ici sont à but instructif uniquement pour permettre une meilleure compréhension des mécanismes de protection, notamment dans le cadre d'exercice de pentest/redteaming.
Toute utilisation en dehors d'un cadre légal ne pourra tenir responsable l'auteur de cet article.



Contournement de Microsoft Defender for Endpoints


Contournement de Sentinel One


Code execution
→ .NET Reflection
→ Process Hollowing
→ PE backdooring (Code caves)
→ APC Queue code injection
→ Early Bird APC injection
→ SetWindowsHookEx

Defense evasion
→ AMSI Bypass
→ Thread stack spoofing
→ PEB Patching
→ IAT Hooking
→ Unhooking DLL
→ Parsing ntdll from file
→ Perun's Fart unhooking
→ Direct syscalls
→ Kernel Callbacks deletion
→ Minifilters
→ ETW Patching
→ NtTraceEvent hooking
→ PPL Killer


.NET Reflection

La réflexion .NET est un mécanisme qui permet d'analyser et de manipuler des types, des objets et des assemblies en temps d'exécution.
Elle fournit un ensemble de classes et de méthodes qui permettent aux applications .NET d'obtenir des informations sur les types de données, les méthodes, les propriétés et les événements définis dans les assemblies chargés en mémoire, ainsi que de créer des instances de types, de modifier des propriétés et d'appeler des méthodes de maniÚre dynamique.

La réflexion en PowerShell est trÚs similaire à la réflexion en C# car PowerShell utilise l'infrastructure de la réflexion .NET pour gérer les types et les objets.



Dans cet exemple, nous utilisons la méthode GetProperty() pour obtenir une référence à la propriété Length du type System.String. Ensuite, nous utilisons la méthode GetValue() pour obtenir la valeur de la propriété Length sur un type "chaßne de caractÚres".
La méthode GetValue() prend un paramÚtre qui spécifie l'objet instancié sur lequel obtenir la valeur de la propriété (La chaßne de caractÚres "Les tutos de Processus").

Grùce à cette technique, un attaquant pourrait par exemple créer une instance d'un fichier exécutable directement en mémoire et invoquer ses fonctions pour l'exécuter.



Process Hollowing

Cette technique consiste à créer un processus légitime dans un état suspendu.
Le systÚme d'exploitation va automatiquement créer un espace mémoire dédié pour ce processus et un premier thread (fil d'exécution) en état suspendu.
Un nouveau bloc mémoire est ensuite alloué dans l'espace mémoire du processus pour y insérer du code malveillant (sous la forme d'un shellcode).
Ensuite, soit l'adresse mémoire de la prochaine instruction (registre EIP) est remplacée par l'adresse mémoire du début du shellcode malveillant dans le thread principal, soit un nouveau thread en état d'exécution est créé avec pour point d'entrée (Entry Point) l'adresse mémoire du début du shellcode malveillant.
Dans le cas de l'utilisation du thread principal, son état est restauré à l'état d'exécution pour permettre l'interprétation du shellcode malveillant.



Le code source C++ ci-dessous est un exemple de Process Hollowing :

1 - Un processus légitime "notepad.exe" est créé en état suspendu avec la fonction CreateProcessA
2 - Un handle (identifiant d'un objet servant à le gérer) sur le processus est récupéré
3 - Un nouveau bloc mémoire est alloué dans l'espace mémoire du processus avec la fonction VirtualAllocEx
4 - Les instructions du shellcode malveillant sont copiées dans le bloc mémoire alloué avec la fonction WriteProcessMemory
5 - Un nouveau thread est créé avec pour point d'entrée l'adresse du début du bloc mémoire contenant les instructions du shellcode malveillant, grùce à la fonction CreateRemoteThread
6 - Le thread principal est restauré en état d'exécution (Optionnel)





PE backdooring (Code caves)

La plupart des binaires disposent d'espaces mémoire inutilisés (remplis de NOPS par exemple) appelés caves de code.
Ces emplacements peuvent s'expliquer par une mauvaise gestion de la mémoire lors du développement ou par l'optimisation du code lors de la compilation pour faciliter les sauts et les accÚs mémoire.

Il est possible de détecter ces caves de code afin d'y insérer des instructions malveillantes sous la forme d'un shellcode, puis de modifier le point d'entrée du binaire afin d'exécuter le shellcode à son lancement.
Le code malveillant peut Ă©galement ĂȘtre personnalisĂ© afin de rediriger le flux d'exĂ©cution vers l'instruction d'origine (on parle de trampoline code) pour ne pas rompre le fonctionnement normal de l'application.



Dans la capture ci-dessus, l'application "calc.exe" de Windows est modifiée afin d'insérer dans une cave de code un shellcode de type reverse shell et son point d'entrée est ensuite patché pour exécuter les nouvelles instructions à son lancement, grùce à l'outil Frampton.



APC Queue code injection

Une APC (Asynchronous Procedure Call) est un mĂ©canisme utilisĂ© pour exĂ©cuter une routine de rappel dans le contexte d'un thread. Cette routine est placĂ©e dans une liste d'APC et sera exĂ©cutĂ©e lorsque le thread sera prĂȘt.
Un shellcode peut donc ĂȘtre injectĂ© dans un espace mĂ©moire d'un processus en cours d'exĂ©cution, puis une routine de rappel peut ĂȘtre crĂ©Ă©e dans la liste d'appels de chaque thread du processus pour que le shellcode soit exĂ©cutĂ©.


Crédits ired.team


L'exemple ci-dessus permet l'exécution un shellcode par le processus Explorer.exe via une file APC, pour obtenir un reverse shell.





Early Bird APC injection

L'EarlyBird APC injection est similaire à l'APC Queue code injection classique, mais l'exécution du shellcode est déclenchée via une routine APC par le thread principal d'un processus en état suspendu, ce qui implique bien souvent que l'EDR n'a pas encore hooké le processus.


L'exemple ci-dessus permet d'exécuter un shellcode via une file APC dans le thread d'un processus notepad.exe démarré en état suspendu, pour obtenir un reverse shell.





SetWindowsHookEx

BientĂŽt :)



AMSI Bypass

L'AMSI (Antimalware Scan Interface) est un composant qui permet de détecter et de bloquer les scripts ou les modules malveillants qui tentent de s'exécuter ou qui sont chargés en mémoire.

Cependant, il est possible de contourner l'AMSI en utilisant plusieurs techniques :

- Obfuscation : les attaquants peuvent utiliser des techniques d'obfuscation pour masquer le code malveillant ou certaines de ses parties.
- Injection de code : les attaquants peuvent utiliser des bibliothĂšques tierces ou des appels systĂšme pour exĂ©cuter du code sans ĂȘtre dĂ©tectĂ©s.
- Désactivation de l'AMSI : les attaquants peuvent tenter de désactiver l'AMSI en modifiant les paramÚtres des politiques de sécurité ou modifier les paramÚtres de registre.



Les commandes ci-dessus utilisent la réflexion (voir section suivante) pour accéder à la classe AmsiUtils de PowerShell et désactiver la variable amsiInitFailed, qui est utilisée pour contrÎler si l'AMSI est activé ou non.
Ces commandes doivent ĂȘtre exĂ©cutĂ©es avec des privilĂšges Ă©levĂ©s pour s'exĂ©cuter.



Thread stack spoofing

BientĂŽt :)



PEB Patching

Le Process Environment Block (PEB) est une structure de données interne à chaque processus sous Windows. Elle contient des informations sur l'état et les paramÚtres du processus, tels que les chemins d'accÚs aux fichiers, les variables d'environnement, les descripteurs de fichiers, etc...



Dans l'exemple ci-dessus, on modifie la ligne de commande d'un processus en surchargeant une donnée dans la mémoire de son PEB.



Lorsque le processus est analysé dans le gestionnaire des tùches, sa ligne de commande ne correspond plus à ses données d'origine.
Le PEB peut ĂȘtre utilisĂ© par des attaquants pour masquer l'exĂ©cution d'un processus ou pour tromper les outils de dĂ©bogage en leur faisant croire que le processus ne doit pas ĂȘtre dĂ©boguĂ©.
Il peut Ă©galement ĂȘtre utilisĂ© pour contourner certaines mesures de sĂ©curitĂ©, telles que l'Address Space Layout Randomization (ASLR), en modifiant les adresses de base des DLL chargĂ©es dans le processus.

L'accÚs au PEB d'un processus nécessite des privilÚges élevés, tels que le privilÚge SeDebugPrivilege.



IAT Hooking

La table d'importation d'un binaire permet de connaitre l'adresse mémoire d'une fonction d'une librairie spécifiée.
Par exemple, lorsqu'on souhaite afficher un message, l'adresse de la fonction "MessageBoxA" de la librairie User32.dll est récupérée depuis la table d'importation puis elle est appelée avec les arguments nécessaires.

Cette table d'importation peut ĂȘtre modifiĂ©e afin de remplacer l'adresse d'une fonction lĂ©gitime par l'adresse d'une fonction malveillante, qui sera appelĂ©e lors de l'appel Ă  la fonction lĂ©gitime.
La fonction malveillante peut exécuter ses instructions et ensuite rediriger le flux d'exécution vers l'adresse d'origine de la fonction légitime afin de ne pas perturber le fonctionnement normal de l'application, on parle dans ce cas de fonction "trampoline".



Dans l'exemple ci-dessus, l'adresse de la fonction "MessageBoxA" est modifiée pour exécuter nos propres instructions avant de rediriger le flux d'exécution vers l'adresse d'origine.



Unhooking DLL

Un hook est un point d'attache utilisé pour intercepter et surveiller les appels de fonctions dans un systÚme d'exploitation.
La plupart des protections de type EDR mettent en place un mécanisme de hooking des fonctions des DLL (Dynamic Link Libraries) pour détecter les comportements malveillants suivant les fonctions appelées par les exécutables.

Le unhooking de DLL permet de détecter et de supprimer ces hooks en utilisant des techniques de réflexion.
La technique de unhooking la plus courante consiste à charger la DLL malveillante dans un processus isolé et à analyser ses exports pour détecter les hooks. Une fois que les hooks sont identifiés, on peut écraser les adresses des hooks par les adresses des fonctions d'exportation d'origine et ainsi restaurer les adresses d'origine des fonctions.



Dans l'exemple ci-dessus, la fonction LoadLibrary() permet de charger en mémoire la DLL dont les fonctions sont hookées et GetProcAddress() permet d'obtenir l'adresse de la fonction d'exportation originale.
La fonction GetHookedFunctionAddress() est ensuite utilisée pour obtenir l'adresse de la fonction hookée.

Grùce à la fonction VirtualProtect(), la mémoire de la fonction hookée est rendue accessible en écriture afin d'écraser l'adresse de la fonction hookée par l'adresse de la fonction d'exportation originale en utilisant la fonction memcpy().
Les protections de mémoire sont ensuite restaurées à l'aide de la fonction VirtualProtect().



Parsing ntdll from file

Comme expliqué ci-dessus, il est possible de mapper en mémoire une copie d'un fichier, comme une librairie DLL par exemple, afin de lire son contenu.

La technique de unhooking DLL la plus répandue consiste à mapper en mémoire la librairie dont les fonctions sont actuellement hookées par l'EDR afin de lire son contenu pour écraser le contenu des fonctions de la librairie chargée en mémoire par notre processus courant.



Dans l'exemple ci-dessus, on ouvre le fichier ntdll.dll afin de crée une vue en mémoire de ce fichier, puis on modifie la mémoire du module ntdll.dll chargé en mémoire par notre processus (hookée par l'EDR) en écrasant une section spécifique avec les données de la vue en mémoire (instructions d'origine).



Perun's Fart unhooking

La méthode Perun's Fart consiste a démarrer un processus en état suspendu et à effectuer une copie des librairies chargées par ce processus (comme la librairie NTDLL).
En état suspendu, les mécanismes de sécurité de type EDR n'ont pas encore mis en place de hooking des adresses des fonctions des librairies.
La copie de ces librairies permet donc de contourner la protection appliquée en appellant directement les fonctions par leurs adresses d'origine.



Dans l'exemple ci-dessus, on utilise un processus démarré en état suspendu pour faire une copie de sa librairie NTDLL afin de réécrire la section .text de notre librairie chargée en mémoire, pour restaurer les adresses d'origine des fonctions de la librairie, et ainsi contourner la protection mise en place.



Direct Syscall

Afin d'Ă©viter les contournements de type unhooking, certains EDR mettent en place de la dĂ©tection de mapping de fichier pour empĂȘcher la rĂ©Ă©criture d'instructions hookĂ©es, notamment en hookant la fonction MapViewOfFile().
Lors de l'exécution d'une fonction, un identifiant de procédure, appelé "syscall", est inscrit dans le registre EAX afin de déclencher une routine en mode kernel correspondant à l'action désirée :


Certains membres de la communauté ont recensé les identifiants de procédure correspondant à certaines routines spécifiques et ont mis au point une technique consistant à inscrire directement l'identifiant dans le registre EAX, sans passer par un appel de fonction d'une librairie DLL.

Cette technique s'est améliorée au fil du temps, changeant réguliÚrement de nom : Hell's Gate, Halo's Gate, puis Tartarus Gate.

Auparavant basé sur une table de correspondance statique, la technique consiste désormais à identifier l'adresse de la structure INMEMORYORDERMODULELIST dans le PEB (Process Environment Bloc) du processus en cours, qui contient les adresses des librairies chargées en mémoire, pour obtenir l'adresse de la librairie NTDLL et récupérer dynamiquement les identifiants de syscall de chaque fonction.



Kernel Callbacks deletion

Sysmon est un outil de surveillance de l'activité systÚme de Windows, développé par Microsoft.

En se basant sur le fonctionnement de Sysmon, certains EDR peuvent surveiller les créations de processus, les chargement d'images ou encore la création de threads directement en mode noyau, via des routines de notification telles que PspCreateProcessNotifyRoutine, CmRegisterCallback, PspCreateThreadNotifyRoutine ou encore PspLoadImageNotifyRoutine.

Il est possible, en connaissant les adresses mémoire (offsets) de ces routines, de supprimer les callbacks mis en place.
Ces adresses peuvent ĂȘtre rĂ©cupĂ©rĂ©es soit en faisant une recherche de patterns (une suite d'instructions "caractĂ©ristiques" de la routine qu'on recherche), soit en utilisant des adresses mĂ©moire connues pour une version spĂ©cifique de systĂšme, obtenues en analysant la mĂ©moire via un dĂ©bugger.



Dans l'exemple ci-dessus, un pilote vulnérable signé numériquement (RTCore64.sys) est utilisé pour supprimer les routines de notification qui permettent à l'antivirus de surveiller les activités des processus.



Minifilters

Lorsqu'un processus souhaite interagir avec un fichier, une requĂȘte est transmise au gestionnaire d'I/O, qui va se charger de contacter le pilote du systĂšme de fichier pour effectuer l'action dĂ©sirĂ©e.
Un composant, le Manager de Filtres, intercepte les échanges entre le gestionnaire d'I/O et le pilote du systÚme de fichier. Il est possible, en mode kernel, d'inscrire auprÚs du gestionnaire de filtres un monitoring de ces échanges, appelé Minifiltre, afin de surveiller l'activité des processus au niveau des fichiers (détection de ransomware par exemple).



Dans l'exemple ci-dessus, un pilote vulnĂ©rable permet de requĂȘter le gestionnaire de filtres pour supprimer les Minifiltres mis en place par des solutions de sĂ©curitĂ©.



ETW Patching

L'ETW (Event Tracing for Windows) est une fonctionnalité de Windows qui permet de surveiller les événements systÚme enregistrés par les applications et les composants systÚme, via un abonnement à un flux d'évÚnements.
Beaucoup de solutions de sécurité l'utilisent pour identifier les comportements malveillants et les activités suspectes sur un systÚme.



Dans l'exemple ci-dessus, la fonction HookEtwWrite() vérifie que l'adresse de la fonction EtwWrite() existe et patche (remplace) la fonction en écrivant une instruction RETN pour désactiver la fonction.
De cette façon, lors d'un appel légitime de la fonction EtwWrite(), l'instruction en assembleur "RETN", qui est utilisée pour indiquer la fin d'une fonction ou d'un sous-programme, est exécutée et l'évÚnement n'est donc pas inscrit dans le flux.



NtTraceEvent hooking

PlutĂŽt que de patcher la fonction EtwWrite() qui trace et publie les Ă©vĂšnements dans une session, il est Ă©galement possible de patcher la fonction NtTraceEvent() qui permet d'Ă©crire les Ă©vĂšnements du fournisseur en passant par l'API ntdll.dll.




Dans l'exemple ci-dessus, la fonction NtTraceEvent() est patchée avec une instruction RETN pour ne plus inscrire d'évÚnements dans le flux.



PPL Killer

Afin de sécuriser un processus, il est possible de lui appliquer une protection, appelée Protected Process Light, au niveau de la mémoire du systÚme en mode Kernel.
Une liste appelée "ActiveProcessLinks" contient des structures, appelées "EPROCESS", qui comportent des informations sur les processus telles que leur Process ID (PID) et le statut de leur protection (PS_PROTECTION).



Si la protection d'un processus est activée, les autres processus ne pourront pas, entre autres :
- Stopper le processus
- Accéder à sa mémoire virtuelle
- Attacher un debugger
- Impersonifier ses threads

Beaucoup d'EDR utilise ce mécanisme pour protéger leurs processus, on parle alors de "Run as PPL".

Il est néanmoins possible de charger en mémoire le noyau Windows (ntoskrnl.exe) avec la fonction LoadLibrary() et de récupérer l'adresse mémoire du pointeur vers les structures EPROCESS, PsInitialSystemProcess, grùce à la fonction GetProcAddress().



Dans l'exemple ci-dessus, l'adresse mémoire des structures EPROCESS est récupérée, puis la mémoire de chaque structure est lue pour trouver l'identifiant du processus souhaité, afin de réécrire le statut de sa protection.
Une fois la protection du processus dĂ©sactivĂ©, il peut ĂȘtre stoppĂ©.







Kudos :

Merci à Ph3n1x pour ses précieuses propositions