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 l'antivirus ESET


Contournement de l'antivirus KASPERSKY


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

Defense evasion
→ AMSI Bypass
→ Thread stack spoofing
→ PEB Patching
→ IAT Hooking
→ Direct syscalls
→ Unhooking DLL
→ Parsing ntdll from PEB
→ Perun's Fart unhooking
→ Tartarus' Gate unhooking
→ Sysmon unloading
→ ETW Patching
→ NtTraceEvent hooking


.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)



Source : https://www.microsoft.com/en-us/security/blog/2017/07/12/detecting-stealthier-cross-process-injection-techniques-with-windows-defender-atp-process-hollowing-and-atom-bombing/



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.



runPE

Bientôt :)



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 :)



Atom Bombing

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.



Direct syscalls

Un syscall (ou appel système) est une interface entre un programme utilisateur et le noyau d'un système d'exploitation.
Il permet à un programme utilisateur de demander au noyau du système d'exploitation d'exécuter une fonctionnalité qui ne peut pas être réalisée directement par le programme en raison de restrictions de sécurité.
Lorsqu'un processus notepad.exe cherche à créer un fichier par exemple, il appelle la fonction CreateFileW() de la librairie Kernel32.dll, qui va transmettre l'appel à la fonction CreateFileW() de la librairie Kernelbase.dll, qui va elle-même transmettre l'appel à la fonction NtCreateFile() de la librairie Ntdll.dll.
La plupart des antivirus et des EDR surveillent les appels de fonction envoyés à certaines librairies sensibles (notamment la Ntdll.dll par exemple).

Un direct syscall est une technique qui permet d'appeler directement une fonction du système d'exploitation (souvent appelée "syscall") sans passer par les couches d'abstraction de la bibliothèque standard C ou C++.
Autrement dit, via un appel direct, il n'est pas nécessaire de passer par les fonctions des librairies surveillées par les mécanismes de sécurité.




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 PEB

Bientôt :)



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.



Tartarus' Gate unhooking

Bientôt :)



Sysmon unloading

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

La plupart des solutions EDR utilisent Sysmon pour surveiller les événements système et les activités des processus afin de détecter les comportements malveillants.
Les événements Sysmon peuvent être utilisés pour suivre les modifications de fichiers, les connexions réseau, les exécutions de commandes, les événements de registre... etc

Il est possible de contourner Sysmon en utilisant des techniques de contournement spécifiques, comme la technique de l'injection de DLL masquée, qui consiste à injecter une DLL dans un processus existant et à masquer la DLL en modifiant les tables d'importation (IAT) du processus pour qu'elles pointent vers une fonction personnalisée.



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.



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

Bientôt :)







Kudos :

Merci à Ph3n1x pour ses précieuses propositions