Utilisation abusive des enclaves VBS pour créer des logiciels malveillants évasifs
Contenu
Introduction
La sécurité virtualisée (VBS) est l'une des avancées récentes les plus fascinantes en matière de sécurité. La possibilité d'isoler les composants critiques du système d'exploitation a permis à Microsoft d'améliorer considérablement la sécurité grâce à des fonctions telles que Credential Guard et Hypervisor-Protected Code Integrity (HVCI).
Les enclaves VBS sont une fonctionnalité souvent négligée, qui permet d'isoler une région d'un processus, la rendant inaccessible aux autres processus, au processus proprement dit et même au noyau.
Les enclaves VBS peuvent avoir un large éventail d'applications de sécurité, et Microsoft les utilise pour mettre en œuvre plusieurs services notables, notamment la fonction Retrouver controversée. En outre, Microsoft soutient le développement d'enclaves VBS par des tiers et encourage activement leur adoption.
Si les enclaves peuvent contribuer à sécuriser les systèmes, elles peuvent aussi être très attrayantes pour les attaquants : un logiciel malveillant qui parvient à s'exécuter à l'intérieur d'une enclave peut être potentiellement invisible à la détection basée sur la mémoire et à l'analyse.
Nous avons entrepris d'explorer les enclaves VBS et de comprendre comment elles pouvaient être utilisées à des fins malveillantes. Cet article de blog détaillera nos principales conclusions. Nous allons nous plonger dans les enclaves VBS en explorant des comportements précédemment non documentés, en décrivant les différents scénarios qui peuvent permettre aux attaquants d'exécuter du code malveillant à l'intérieur de ces enclaves, et en examinant les différentes techniques que les « logiciels malveillants d'enclaves » peuvent utiliser.
Nous vous présenterons également Mirage, une technique d'évasion de la mémoire basée sur une nouvelle approche que nous avons baptisée « Bring your own vulnerable enclave ». Nous verrons comment les attaquants peuvent utiliser d'anciennes versions vulnérables d'enclaves légitimes pour mettre en œuvre cette technique d'évasion furtive.
Virtual Trust Levels
Windows s'appuyait traditionnellement sur les niveaux d'anneau du processeur pour empêcher les applications utilisateur d'altérer le système d'exploitation. Cette caractéristique matérielle permet de séparer le système d'exploitation des applications utilisateur, le noyau tourne dans l'anneau 0, isolé des applications en mode utilisateur de l'anneau 3. Le problème de cette approche est qu'elle offre aux attaquants un moyen relativement simple de compromettre le système d'exploitation : l'exploitation du noyau.
Le noyau Windows présente une surface d'attaque très riche. Une liste impressionnante de pilotes tiers, ainsi qu'une grande variété de services exposés par le noyau lui-même, conduisent au développement d'un flux apparemment constant d'exploitations du noyau. Un attaquant peut ainsi contrôler potentiellement tous les aspects du système d'exploitation. La limite entre le mode utilisateur et le mode noyau s'est avérée insuffisante.
Pour combler cette lacune, Microsoft a introduit une limite de sécurité supplémentaire dans le système d'exploitation sous la forme de Virtual Trust Levels (VTL). Les privilèges des VTL sont basés sur l'accès à la mémoire : chaque niveau de confiance fournit aux entités qui s'exécutent sous ce niveau différentes autorisations d'accès à la mémoire physique. Ces autorisations garantissent notamment que les VTL inférieurs ne peuvent pas accéder à la mémoire des VTL supérieurs.
À l'instar de l'architecture traditionnelle des anneaux de processeurs, les VTL séparent le système d'exploitation en différents « modes » d'exécution, commençant à VTL0 et se terminant (potentiellement) à VTL16. Contrairement aux anneaux de processeurs, où l'anneau 0 est le plus privilégié, les VTL supérieurs disposent de davantage de privilèges que les inférieurs.
Windows utilise actuellement deux niveaux de confiance principaux : VTL0 et VTL1 (VTL2 est également utilisé, mais n'entre pas dans le cadre de cet article de blog). VTL0 est utilisé pour exécuter les composants traditionnels du système d'exploitation, y compris le noyau et les applications en mode utilisateur. VTL1, qui dispose de davantage de privilèges que VTL0, crée deux nouveaux modes d'exécution : le mode noyau sécurisé et le mode utilisateur isolé.
Mode noyau sécurisé
Le mode noyau sécurisé fait référence au mode d'exécution de l'anneau 0 VTL1. Ce mode est utilisé pour exécuter le noyau sécurisé, un noyau qui s'exécute en VTL1 et qui dispose donc de davantage de privilèges que le noyau normal. Grâce à ces privilèges, le noyau sécurisé peut appliquer des politiques au noyau normal et restreindre son accès aux zones de mémoire sensibles.
Comme nous l'avons noté, le noyau présente une grande surface d'attaque et est susceptible d'être compromis. En supprimant certains privilèges du noyau et en les accordant au noyau sécurisé, nous pouvons réduire l'impact d'une compromission du noyau.
En théorie, la compromission du noyau sécurisé peut toujours permettre à un attaquant de compromettre entièrement le système. Néanmoins, ce scénario est beaucoup moins probable, car le noyau sécurisé est très étroit et ne prend en charge aucun pilote tiers, ce qui réduit considérablement la surface d'attaque.
Mode utilisateur isolé
VTL1 crée également un autre mode d'exécution intéressant appelé mode utilisateur isolé (IUM), qui fait référence au mode d'exécution de l'anneau 3 VTL1. L'IUM est utilisé pour exécuter des processus sécurisés, un type spécial de processus en mode utilisateur qui utilise les capacités d'isolation de la mémoire du VTL1. Aucun code VTL0, y compris le noyau normal, ne peut accéder à la mémoire à l'intérieur de l'IUM. Ce mode d'exécution est l'épine dorsale des fonctions basées sur l'isolation, telles que Credential Guard.
Pour résumer, l'introduction du VTL0/1 se traduit par quatre modes d'exécution (figure 1).
- Ring0 VTL0 — Mode noyau normal
- Ring0 VTL1 — Mode noyau sécurisé
- Ring3 VTL0 — Mode utilisateur normal
- Ring3 VTL1 — Mode utilisateur isolé
Que sont les enclaves VBS ?
Les enclaves VBS sont une autre fonction activée par l'IUM. Une enclave VBS est une section d'un processus en mode utilisateur qui réside dans l'IUM et dans laquelle nous pouvons charger des DLL appelées « modules d'enclave ».
Lorsqu'un module est chargé dans une enclave, il devient un « environnement d'exécution fiable », les données et le code à l'intérieur de l'enclave sont inaccessibles à tout ce qui s'exécute dans VTL0 et ne peuvent donc pas être altérés ou dérobés (figure 2). Cette fonction est utile pour isoler les opérations sensibles des attaquants capables de compromettre le système.
Un processus en mode utilisateur peut appeler les API Windows dédiées pour créer une enclave, y charger un module et l'initialiser. Un module d'enclave se présente sous la forme d'une DLL compilée spécifiquement à cette fin. Une fois l'enclave initialisée, le processus hôte en mode utilisateur ne peut pas accéder à sa mémoire et ne peut interagir avec elle qu'en invoquant les fonctions exportées par ses modules à l'aide de l'API CallEnclave .
La figure 3 est un exemple de code mettant en œuvre ce processus (un exemple plus détaillé est fourni par Microsoft).
Microsoft souhaitant restreindre autant que possible l'accès à VTL1, le chargement d'une DLL dans une enclave nécessite qu'elle soit correctement signée à l'aide d'un certificat spécial émis par Microsoft. Toute tentative de chargement d'un module sans cette signature échouera. L'option de signer les modules de l'enclave n'est déléguée qu'à des tiers de confiance. Il est intéressant de noter qu'il n'existe aucune restriction quant aux personnes autorisées à charger ces modules : tant qu'ils sont signés, n'importe quel processus peut charger des modules arbitraires dans une enclave.
Les modules d'enclave sont conçus pour servir de petites « unités de calcul », qui ont une capacité très limitée à interagir avec le système ou à l'affecter. Pour cette raison, ils sont limités à l'utilisation d'un ensemble minimal d'API,ce qui les empêche d'accéder à la plupart des composants du système d'exploitation.
Les API disponibles dans les enclaves sont importées de bibliothèques spécialisées chargées dans le VTL1. Par exemple, alors que les processus normaux s'appuient sur la bibliothèque ntdll.dll pour demander des services au système d'exploitation, les modules d'enclave utilisent vertdll.dll ,un « substitut » de ntdll qui permet de communiquer avec le noyau sécurisé par le biais de syscalls.
Logiciel malveillant d'enclave
Le concept de logiciel malveillant d'enclave est certainement être attrayant pour les attaquants, car il offre deux avantages significatifs :
Exécution dans une région de mémoire isolée: l'espace d'adressage des enclaves est inaccessible à tout ce qui s'exécute dans VTL0, y compris les EDR et les outils d'analyse, ce qui rend la détection beaucoup plus complexe.
Appels d'API intraçables: les appels d'API effectués à l'intérieur d'une enclave peuvent être invisibles pour les EDR. Les EDR surveillent les API en mode utilisateur en plaçant des hooks dans les bibliothèques système et utilisent des pilotes pour surveiller l'activité dans le noyau. Les appels d'API déclenchés à partir d'une enclave ne sont pas détectables par ces techniques, car les appels d'enclave sont effectués à partir de VTL1 et ne passent par aucun de ces composants VTL0 « accrochés ».
La figure 4 illustre cet avantage : un processus normal invoquant une API Windows peut être surveillé par un hook à l'intérieur de NTDLL ou du noyau lui-même. Cependant, un module enclavé passe par le vertdll résidant dans le VTL1 et invoque des appels au noyau sécurisé, tous deux inaccessibles aux EDR.
Conscients de ce potentiel, nous avons entrepris d'étudier le concept de logiciel malveillant d'enclave. Pour exploiter les enclaves à cette fin, il faut répondre à deux questions :
- Comment les attaquants peuvent-ils exécuter un code malveillant à l'intérieur d'une enclave ?
- Quelles techniques les attaquants peuvent-ils employer une fois qu'ils se trouvent dans une enclave ?
Comment les attaquants peuvent-ils exécuter un code malveillant à l'intérieur d'une enclave ?
Comme nous l'avons indiqué précédemment, les modules d'enclaves doivent être signés par un certificat émis par Microsoft pour être chargés, ce qui signifie que seules les entités approuvées par Microsoft devraient être en mesure d'exécuter leur propre code dans une enclave. Malgré cela, les attaquants ont encore quelques options.
Tout d'abord, ils peuvent s'appuyer sur une vulnérabilité du système d'exploitation. C'est le cas de la vulnérabilité CVE-2024-49706, découverte par Alex Ionescu (travaillant pour Winsider Seminars & Solutions), qui permet à un attaquant de charger un module non signé dans une enclave. La vulnérabilité a été corrigée par Microsoft, mais des attaquants motivés pourraient identifier des bugs similaires à l'avenir.
Une deuxième approche simple consiste à obtenir une signature légitime, ce qui est possible puisque Microsoft expose la signature de l'enclave à des tiers par l'intermédiaire de la plateforme Signatures de confiance . Bien que cela ne soit pas anodin, un attaquant chevronné peut être en mesure d'obtenir l'accès à une entité Signatures de confiance et de signer ses propres enclaves.
En plus de ces deux options, nous avons exploré deux autres techniques qui pourraient permettre aux attaquants d'exécuter du code à l'intérieur d'une enclave VBS : abuser des enclaves déboguables et exploiter les enclaves vulnérables.
Utilisation abusive de modules d'enclaves déboguables
Lors de la création d'un module d'enclave VBS, un développeur peut le configurer pour qu'il soit déboguable. La compilation d'un module avec ce paramètre permet, eh bien… de le déboguer. Comme les modules d'enclaves s'exécutent dans VTL1, le débogage n'est normalement pas possible, car le débogueur ne peut pas accéder à la mémoire de l'enclave pour récupérer des données ou placer des points d'arrêt. La figure 5 montre un exemple de débogueur qui ne parvient pas à lire une adresse mémoire à l'intérieur d'une enclave.
Il est intéressant de noter que lorsqu'un module d'enclave déboguable est exécuté, il est toujours chargé dans VTL1. Pour permettre le débogage, le noyau sécurisé met en œuvre certaines exceptions qui s'appliquent aux modules d'enclaves déboguables. Par exemple, lorsqu'il essaie de lire la mémoire d'un tel module, le noyau normal émet un appel au noyau sécurisé SkmmDebugReadWriteMemory qui vérifie que le module cible est effectivement déboguable, avant d'effectuer l'opération demandée.
La figure 6 en donne un exemple : après avoir chargé un module d'enclave déboguable, le débogueur peut lire avec succès dans la mémoire de l'enclave.
De la même manière, les autorisations de mémoire au sein d'un module d'enclave déboguable sont également modifiables par le processus VTL0 (une exception mise en œuvre dans l'appel du noyau sécurisé SkmmDebugProtectVirtualMemory ).
Microsoft recommande vivement aux développeurs de ne pas livrer de modules d'enclaves déboguables, car cela compromet l'objectif principal des enclaves, qui est d'isoler une section de la mémoire de VTL0 (figure 7). L'utilisation d'un module déboguable signifie que les données qu'il manipule peuvent être facilement exposées.
Les risques d'une application de production utilisant un module d'enclave déboguable sont clairs, mais les attaquants peuvent en fait les utiliser à d'autres fins : l'exécution de code non signé dans le VTL1. Si un attaquant obtient un module d'enclave signé déboguable, il peut exécuter du code VTL1 en effectuant les quatre étapes suivantes :
- Obtenir l'adresse d'une routine à l'intérieur du module d'enclave à l'aide de GetProcAddress
- Remplacer la protection de la mémoire de la routine par RWX
- Remplacer le code de la routine par un shellcode arbitraire
- Déclencher la routine à l'aide de CallEnclave
La figure 8 illustre le code mettant en œuvre ce processus.
Le problème évident du point de vue d'un attaquant est qu'il s'agit d'une arme à double tranchant, tout comme l'attaquant peut accéder à la mémoire de l'enclave, un EDR peut également le faire. Malgré tout, cette méthode présente l'avantage d'échapper à la surveillance des API. En effet, les appels d'API effectués par le module d'enclave passeront toujours par les DLL VTL1 et le noyau sécurisé, ce qui limite la visibilité des EDR.
Dans l'ensemble, cette méthode pourrait être utile pour créer un implant « semi-VTL1 » furtif, qui tire parti de certains des avantages obtenus en fonctionnant à l'intérieur d'une enclave.
Nous avons tenté d'utiliser VirusTotal et d'autres sources pour identifier un module d'enclave signé déboguable, mais au moment où nous écrivons ces lignes, nous n'y sommes pas parvenus. Cependant, nous pensons qu'il est prudent de supposer qu'avec suffisamment de temps et une adoption plus large de la technologie de l'enclave, certaines fuites finiront par se produire.
Bring your own vulnerable enclave
Comme nous l'avons vu, Windows utilise des signatures pour empêcher le chargement d'enclaves non fiables dans VTL1. Cette approche n'est pas propre aux enclaves. Le concept est apparu avec l'application de la signature des pilotes (DSE), qui empêche les pilotes non approuvés de s'exécuter dans le noyau Windows.
Pour contourner cette application, les attaquants ont commencé à utiliser la technique « bring your own vulnerable driver » (BYOVD), c'est-à-dire que ne pouvant pas charger son propre pilote, un attaquant charge donc un pilote légitime signé avec une vulnérabilité connue à la place. Il peut ensuite exploiter cette vulnérabilité pour exécuter du code non signé dans le noyau.
Nous avons voulu explorer cette approche dans le contexte des enclaves : Pouvons-nous abuser d'un module d'enclave signé vulnérable pour exécuter du code dans l'IUM ?
La première étape consistait à trouver une telle enclave, ce qui nous a rapidement conduits à la CVE-2023-36880 , une vulnérabilité dans un module d'enclave VBS utilisé par Microsoft Edge. Cette vulnérabilité permet à un attaquant de lire et d'écrire des données arbitraires à l'intérieur de l'enclave. Bien que la vulnérabilité ait été qualifiée par Microsoft de vulnérabilité de divulgation d'informations, les notes précisent également qu'elle pourrait conduire à une exécution de code limitée (figure 9).
La vulnérabilité a été découverte par Alex Gough de l'équipe de sécurité de Chrome, qui a également partagé une démonstration de faisabilité pour l'exploiter. Après avoir trouvé une version vulnérable de cette enclave dans VirusTotal, nous avons entrepris de l'exploiter pour obtenir une exécution de code.
Notre idée était d'abuser de la primitive lecture/écriture pour écraser la pile de l'enclave avec une chaîne ROP, ce qui nous permettrait finalement d'exécuter un shellcode dans l'enclave. En explorant cette option, nous avons relevé un fait intéressant : les enclaves sont protégées contre l'exécution de code non signé à l'aide d'une garde de code arbitraire (ACG).
L'ACG est une mesure de sécurité conçue pour bloquer l'exécution de code généré dynamiquement, c'est-à-dire du code créé au moment de l'exécution plutôt que de faire partie de l'exécutable du processus d'origine ou de ses DLL. L'ACG est mise en œuvre en appliquant deux règles :
- De nouvelles pages exécutables ne peuvent être générées après le chargement initial du processus
- Une page déjà exécutable ne peut pas devenir accessible en écriture
L'ACG est appliquée par défaut dans le noyau normal, mais en mode utilisateur, elle ne s'applique qu'aux processus qui sont configurés pour l'utiliser. Nos recherches ont montré que pour les processus IUM, qui incluent des enclaves, l'ACG semble s'appliquer automatiquement, comme c'est le cas dans le noyau normal.
Nous pouvons l'observer en essayant d'allouer une nouvelle page RWX dans une enclave à l'aide de VirtualAlloc, l'opération échoue avec le code d'erreur 0x677, STATUS_DYNAMIC_CODE_BLOCKED (figure 10). Toute tentative d'utilisation de VirtualProtect pour modifier les autorisations d'une page exécutable ou pour rendre une page exécutable aboutira au même résultat.
Pour comprendre ce comportement, nous pouvons examiner SecureKernel!NtAllocateVirtualMemoryEx, la fonction du noyau sécurisé qui gère l'allocation dynamique de la mémoire dans l'IUM. La fonction évalue le masque de protection demandé, et si une page exécutable est demandée, elle renvoie l'erreur ACG STATUS_DYNAMIC_CODE_BLOCKED. Des contrôles similaires sont mis en œuvre dans SkmmProtectVirtualMemory pour empêcher les modifications des pages IUM existantes (figure 11).
Nous n'avons pas trouvé de méthode pour contourner l'ACG à l'intérieur d'une enclave et y charger du code non signé. En théorie, une exploitation complète des ROP est possible, permettant potentiellement aux attaquants d'invoquer des API arbitraires dans VTL1, par exemple, mais nous n'avons pas poursuivi dans cette direction. Malgré cela, nous avons réussi à identifier une autre application intéressante pour les enclaves vulnérables, dont nous parlerons plus loin dans cet article.
Quelles techniques les attaquants peuvent-ils employer une fois qu'ils se trouvent dans une enclave ?
Sachant que les enclaves peuvent présenter un grand potentiel d'activités malveillantes et qu'un attaquant motivé peut les lancer, nous nous sommes ensuite demandé quelles techniques pouvaient être utilisées pour ce type de logiciels malveillants.
L'utilisation la plus simple consiste à utiliser les enclaves comme elles sont censées l'être. Tout comme une enclave permet de protéger les données sensibles des attaquants, elle peut également être utilisée par les attaquants pour cacher leurs propres « secrets » aux entités VTL0.
Cela peut être utile dans de nombreux scénarios, en stockant les charges utiles hors de portée des EDR, en scellant les clés de chiffrement à l'abri des analystes ou en gardant la configuration sensible des logiciels malveillants à l'écart des vidages de mémoire.
En ce qui concerne les options plus avancées, de nombreuses techniques traditionnelles de logiciels malveillants sont impossibles à mettre en œuvre dans une enclave. Les enclaves étant limitées à un sous-ensemble restreint d'API système, elles ne peuvent pas interagir avec les principaux composants du système d'exploitation tels que les fichiers, le registre, le réseau, les autres processus, etc.
Malgré cela, il existe encore un certain nombre de techniques qu'il est possible de mettre en œuvre à l'intérieur d'une enclave et qui nous permettent de capitaliser sur les avantages liés à l'exécution dans l'IUM.
Accès à la mémoire en mode utilisateur VTL0
Malgré leur accès limité au système, les enclaves ont toujours accès à une ressource critique : la mémoire du processus. Les enclaves peuvent effectuer des opérations de lecture/écriture dans l'ensemble de l'espace d'adressage du processus, VTL0 inclus.
Certaines restrictions s'appliquent à cet accès, le code s'exécutant dans les enclaves adhère aux permissions de la mémoire et ne peut pas les modifier. Cela signifie qu'une enclave ne pourra pas écrire dans une mémoire non accessible en écriture, ni permettre l'exécution d'une mémoire non exécutable. La figure 12 représente un code s'exécutant dans une enclave et illustre ces différentes possibilités et contraintes.
En accédant à la mémoire en mode utilisateur VTL0, nous pouvons mettre en œuvre diverses techniques utiles. En chargeant une enclave malveillante dans un processus cible, nous pouvons furtivement surveiller et dérober des informations sensibles, ou corriger des valeurs dans le processus pour modifier son comportement.
La mise en œuvre de ces techniques à l'aide d'une enclave présente un avantage significatif, comme nous l'avons décrit précédemment, le déclenchement d'API à partir d'une enclave nous permet d'échapper à la surveillance des EDR. Comme l'accès à la mémoire dans ces techniques est effectué par une enclave, elles peuvent rester invisibles.
Exécution du code en mode utilisateur VTL0
Bien qu'une enclave puisse lire/écrire dans la mémoire en mode utilisateur du VTL0 avec les autorisations appropriées, le code stocké dans le VTL0 ne peut jamais être exécuté dans une enclave, même s'il dispose des autorisations d'exécution.
Bien qu'elle ne puisse pas exécuter le code VTL0 à l'intérieur d'une enclave, une enclave a la possibilité de le déclencher « à distance ». En utilisant l'API CallEnclave avec une adresse VTL0, une enclave peut déclencher l'exécution du code VTL0 dans un thread en mode utilisateur normal (figure 13).
En faisant en sorte que le processus en mode utilisateur « agisse en leur nom », les enclaves peuvent accéder indirectement au système d'une manière qui leur est normalement impossible. Par exemple, une enclave peut déclencher une routine VTL0 qui lit un fichier, crée un socket, etc.
Il est important de noter que l'exécution du code en mode utilisateur de cette manière ne présente pas d'avantage en termes d'évasion, comme le code s'exécute comme n'importe quel autre code en mode utilisateur, il peut être visible par les EDR.
Anti-débogage
Une autre application intéressante des logiciels malveillants d'enclave est l'anti-débogage. Le fait que le code exécuté dans l'enclave reste inaccessible aux applications VTL0, y compris aux débogueurs, confère aux logiciels malveillants un avantage significatif sur ces dernières.
La réduction des API exposées aux enclaves signifie que toutes les techniques anti-débogage traditionnelles ne seront pas disponibles à partir d'une enclave. Par exemple, les API NtQueryInformationProcess ou IsDebuggerPresent , ainsi que toutes les API de date et d'heure ne sont pas accessibles aux enclaves. Malgré cela, nous avons encore quelques options.
Tout d'abord, nous pouvons nous appuyer sur l'accès de l'enclave à l'espace d'adressage VTL0 du processus, ce qui lui permet de lire manuellement le PEB du processus et de vérifier la valeur du drapeau « BeingDebugged ». En cas de détection d'un débogueur, le processus peut être arrêté par l'enclave.
Une autre approche consiste à mettre en œuvre une technique anti-débogage basée sur le temps (figure 14). Bien que les API de date et d'heure ne soient pas accessibles aux enclaves, elles peuvent toujours utiliser l'instruction d'assemblage rdtsc . Cette instruction renvoie le nombre de cycles d'horloge du processeur depuis le démarrage. Elle nous permet de mesurer le temps passé entre les différents appels de l'enclave et mettre fin au processus en cas de détection d'un retard important.
En déplaçant des parties critiques de notre code dans une enclave, parallèlement à une vérification anti-débogage, nous pouvons créer un logiciel malveillant presque totalement à l'épreuve de l'analyse dynamique. Le logiciel malveillant dépend de l'enclave pour fonctionner correctement, tandis que le processus en mode utilisateur ne peut pas corriger les contrôles qui s'exécutent en son sein. Correctement mise en œuvre, cette approche ne pourrait être déjouée qu'en déboguant Hyper-V ou le noyau sécurisé.
BYOVE — Deuxième partie
Dans une section précédente, nous avons tenté d'exploiter un module d'enclave vulnérable pour exécuter du code dans l'IUM. Lorsque nous avons réalisé que cela pourrait ne pas être possible, nous avons voulu voir si le concept de BYOVE pourrait avoir d'autres applications, et nous avons décidé d'explorer plus avant le module d'enclave vulnérable.
La vulnérabilité (CVE-2023-36880) provient des fonctions SealSettings et UnsealSettings qui sont exportées par le module d'enclave. La fonction SealSettings reçoit un pointeur vers un tampon de données, le chiffre et écrit les résultats à une adresse de destination fournie par l'appelant. UnsealSettings fonctionne de manière similaire, décryptant un tampon fourni et l'écrivant à une adresse spécifiée.
Le problème de ces deux fonctions est qu'elles ne valident ni l'adresse de destination ni l'adresse du tampon source, ce qui leur permet de pointer vers n'importe quelle adresse à l'intérieur du processus, y compris des adresses à l'intérieur de l'enclave elle-même. La figure 15 illustre le code vulnérable, montrant UnsealSettings exécutant un memcpy vers une adresse arbitraire fournie par l'utilisateur.
Cette vulnérabilité offre à un attaquant deux possibilités (figure 16).
Écriture arbitraire à l'intérieur de l'enclave: Un attaquant peut appeler SealSettings pour chiffrer des données arbitraires, puis appeler UnsealSettings pour pointer vers une adresse de destination à l'intérieur de l'enclave. Les données originales sont alors écrites dans la mémoire de l'enclave.
Lecture arbitraire à l'intérieur de l'enclave: Un attaquant peut appeler SealSettings, tout en fournissant une adresse à l'intérieur de l'enclave comme pointeur de tampon source. L'enclave cryptera alors les données de la mémoire de l'enclave et les écrira à un emplacement contrôlé par l'attaquant. L'attaquant peut ensuite décrypter ces données en appelant UnsealSettings, ce qui lui permet de lire des données arbitraires dans l'enclave.
Mirage — Évasion de mémoire basée sur VTL1
Bien qu'elle ne nous permette pas d'exécuter du code dans VTL1, la primitive d'écriture arbitraire offre deux possibilités uniques :
Stocker des données arbitraires dans VTL1, où elles deviennent inaccessibles aux entités VTL0
Écrire des données arbitraires dans l'espace d'adressage du processus normal (VTL0) à partir de VTL1, en empêchant les entités VTL0 de surveiller cette opération
De plus, comme ces capacités sont facilitées par le chargement d'un module enclave signé, elles peuvent être utilisées par n'importe quel attaquant,sans qu'il ait à signer quoi que ce soit lui-même.
Pour démontrer le potentiel de ces capacités, nous avons mis au point une technique d'évasion par balayage de la mémoire que nous avons baptisée « Mirage ». Mirage s'inspire de Gargoyle, une technique d'évasion qui crée une charge utile passant continuellement d'un état bénin à un état malveillant (figure 17).
Alors que Gargoyle réalise cette opération en passant de la mémoire exécutable à la mémoire non exécutable, Mirage vise à obtenir un résultat similaire en passant de la mémoire VTL1 à la mémoire VTL0. Il stocke le shellcode dans la mémoire de l'enclave VTL1, le transfère périodiquement vers VTL0 en utilisant la vulnérabilité, l'exécute, puis l'efface rapidement de la mémoire VTL0 (figure 18).
Cette approche présente deux avantages principaux. Premièrement, comme la charge utile passe la plupart du temps cachée dans le VTL1, elle résiste aux analyses de la mémoire et aux vidages. C'est un avantage par rapport à la technique Gargoyle, car pendant sa phase dormante, notre charge utile n'est pas seulement « furtive », elle est aussi inaccessible.
Le deuxième avantage est que l'écriture du shellcode dans VTL0 est effectuée par l'enclave. Comme nous l'avons décrit précédemment, les EDR ne peuvent pas surveiller le code s'exécutant dans VTL1, ce qui signifie que les hooks EDR typiques ne seront pas en mesure d'intercepter le shellcode pendant qu'il est écrit dans la mémoire.
Nous avons développé une démonstration de faisabilité (PoC) pour Mirage (figure 19). Cette démonstration de faisabilité vise uniquement à démontrer l'idée qui sous-tend la technique et n'est donc pas entièrement opérationnelle.
Détection
À ce jour, les enclaves sont utilisées par un nombre très limité d'applications. Même si leur adoption se généralise, elles ne seront chargées que par des processus spécifiques, et non par des processus arbitraires. Par exemple, calc.exe ne devrait probablement jamais charger une enclave VBS.
C'est pourquoi l'utilisation anormale d'une enclave peut constituer une excellente opportunité de détection. Les spécialistes de la sécurité doivent en tirer parti en établissant une base de référence des utilisations connues et légitimes des enclaves VBS et en signalant tout écart par rapport à cette dernière. L'utilisation d'enclaves peut être identifiée de deux manières : en surveillant les API d'enclaves et en détectant les DLL d'enclaves chargées.
API d'enclave
Les API suivantes sont utilisées pour gérer les enclaves VBS par un processus d'hébergement et indiquent probablement qu'un processus est en cours de chargement :
- CreateEnclave
- LoadEnclaveImageW
- InitializeEnclave
- CallEnclave
- DeleteEnclave
- TerminateEnclave
DLL d'enclave chargées
Une autre option pour détecter une utilisation anormale des enclaves consiste à détecter le chargement des DLL d'environnement qu'elles utilisent généralement, à savoir Vertdll.dll et ucrtbase_enclave.dll. Comme ces DLL ne doivent pas être utilisées par autre chose qu'une enclave, leur présence indique que le processus en utilise probablement une.
Conclusion
Les enclaves VBS constituent un outil extraordinaire pour les développeurs qui veulent protéger les sections sensibles de leurs applications, mais, comme nous venons de le démontrer, elles peuvent également être utilisées par les acteurs malveillants pour « protéger » leurs logiciels malveillants. Bien que ce concept soit essentiellement théorique à ce stade, il est certainement possible que des acteurs malveillants chevronnés commencent à utiliser les enclaves VBS à des fins malveillantes à l'avenir.
Remerciements
Nous tenons à remercier Matteo Malvica d'Offsec et Cedric Van Bockhaven d'Outflank, qui ont récemment mené un projet de recherche très similaire à celui-ci. Nous vous invitons à consulter leur premier article de blog issu d'une série en deux parties. Ne manquez pas la deuxième partie, qui sera publiée après Insomni's Hack 2025.