Ausnutzung von VBS-Enklaven zur Erstellung evasiver Malware

Ori David

Verfasser

Ori David

February 25, 2025

Ori David

Verfasser

Ori David

Ori David ist Sicherheitsforscher bei Akamai. Seine Forschung konzentriert sich auf offensive Sicherheit, Malware-Analyse und Bedrohungssuche. 

Malware, der es gelingt, innerhalb einer Enklave ausgeführt zu werden, ist für die speicherbasierte Erkennung und Forensik möglicherweise unsichtbar.
Malware, der es gelingt, innerhalb einer Enklave ausgeführt zu werden, ist für die speicherbasierte Erkennung und Forensik möglicherweise unsichtbar.

Inhalt

Einführung

Virtualisierungsbasierte Sicherheit (VBS) ist eine der faszinierendsten Sicherheitsentwicklungen der letzten Zeit. Die Möglichkeit, kritische Komponenten des Betriebssystems zu isolieren, hat es Microsoft ermöglicht, mit Funktionen wie Credential Guard und Hypervisor-Protected Code Integrity (HVCI) erhebliche Sicherheitsverbesserungen zu erzielen.

Eine häufig übersehene Funktion, die von VBS ermöglicht wird, sind die VBS-Enklaven . Dies ist eine Technologie, die die Isolierung eines Bereichs eines Prozesses ermöglicht, sodass dieser für andere Prozesse, den Prozess selbst und sogar den Kernel unzugänglich ist. 

VBS-Enklaven können für eine breite Palette von Sicherheitsszenarien verwendet werden. So werden sie von Microsoft genutzt, um mehrere wichtige Dienste zu implementieren, darunter die umstrittene Recall-Funktion. Darüber hinaus unterstützt Microsoft die Entwicklung von VBS-Enklaven durch Dritte und fördert aktiv deren Einführung.

Enklaven können zwar zum Schutz von Systemen beitragen, aber auch für Angreifer sehr attraktiv sein, denn eine Malware, der es gelingt, innerhalb einer Enklave ausgeführt zu werden, ist für die speicherbasierte Erkennung und Forensik möglicherweise unsichtbar.

Wir wollten uns VBS-Enklaven näher ansehen und verstehen, wie sie für böswillige Zwecke ausgenutzt werden können. In diesem Blogbeitrag werden unsere wichtigsten Erkenntnisse dazu erläutert. Wir untersuchen VBS-Enklaven, indem wir uns mit bisher nicht dokumentierten Verhaltensweisen befassen, die verschiedenen Szenarien beschreiben, in denen Angreifer Schadcode ausführen können, und uns die verschiedenen Methoden ansehen, die „Enklaven-Malware“ anwenden kann.

Wir stellen auch Miragevor, eine Speicherumgehungstechnik, die auf einem neuen Ansatz beruht, den wir „Bring Your Own Vulnerable Enclave“ (BYOVE) getauft haben. Wir werden untersuchen, wie Angreifer alte, anfällige Versionen legitimer Enklaven nutzen können, um diese Technik der heimlichen Umgehung zu implementieren.

Virtuelle Vertrauensebenen 

Bisher verließ sich Windows auf die Prozessor-Ring-Ebenen, um zu verhindern, dass Nutzeranwendungen das Betriebssystem manipulieren. Diese Hardwarefunktion ermöglicht eine Trennung des Betriebssystems von Nutzeranwendungen, bei der der Kernel isoliert von den Ring‑3-Nutzermodusanwendungen in Ring 0 ausgeführt wird. Das Problem bei diesem Ansatz ist, dass Angreifer hier einen relativ einfachen Weg haben, das Betriebssystem zu kompromittieren, nämlich über Kernel-Exploits.

Der Windows-Kernel bietet eine sehr große Angriffsfläche. Eine riesige Liste von Treibern von Drittanbietern sowie eine Vielzahl von Diensten, die vom Kernel selbst bereitgestellt werden, führt zur Entwicklung eines scheinbar konstanten Stroms von Kernel-Exploits. Und mit einem solchen Exploit kann ein Angreifer potenziell jeden Aspekt des Betriebssystems kontrollieren. Die Grenze des Nutzer/Kernel-Modus erwies sich als unzureichend.

Um diese Lücke zu schließen, hat Microsoft eine zusätzliche Sicherheitsgrenze in Form von virtuellen Vertrauensebenen (Virtual Trust Levels, VTLs) in das Betriebssystem eingeführt. VTL-Berechtigungen basieren auf Speicherzugriff, das heißt, jede Vertrauensebene bietet den unter ihr ausgeführten Elementen unterschiedliche Zugriffsberechtigungen für den physischen Speicher. Diese Berechtigungen stellen unter anderem sicher, dass niedrigere VTLs nicht auf den Speicher höherer VTLs zugreifen können.

Ähnlich wie bei der herkömmlichen Prozessor-Ring-Architektur trennen VTLs das Betriebssystem in verschiedene „Modi“ der Ausführung – beginnend mit VTL0 und endend bei (potenziell) VTL16. Im Gegensatz zu Prozessor-Ringen, bei denen Ring 0 die privilegierteste Ebene ist, haben höhere VTLs mehr Rechte als niedrigere.

Windows verwendet derzeit zwei Hauptvertrauensebenen: VTL0 und VTL1 (VTL2 wird ebenfalls verwendet, ist aber nicht Gegenstand dieses Blogbeitrags). VTL0 wird verwendet, um die herkömmlichen Betriebssystemkomponenten auszuführen, einschließlich der Kernel- und Nutzermodusanwendungen. VTL1, das mehr Rechte als VTL0 hat, erstellt zwei neue Ausführungsmodi: sicherer Kernel-Modus und isolierter Nutzermodus.

Sicherer Kernel-Modus

Der sichere Kernel-Modus bezieht sich auf den VTL1-Ausführungsmodus „Ring 0“. Dieser Modus wird zur Ausführung des sicheren Kernels verwendet. Dies ist ein Kernel, der in VTL1 ausgeführt wird und daher mehr Berechtigungen als der normale Kernel hat. Mit diesen Berechtigungen kann der sichere Kernel Richtlinien auf dem normalen Kernel erzwingen und dessen Zugriff auf sensible Speicherbereiche einschränken.

Wie bereits erwähnt, stellt der Kernel eine große Angriffsfläche dar und ist anfällig für Kompromittierungen. Durch Entfernen einiger Berechtigungen aus dem Kernel und Übertragen dieser Rechte an den sicheren Kernel können wir die Auswirkungen einer Kernel-Kompromittierung verringern.

Theoretisch kann eine Kompromittierung des sicheren Kernels es einem Angreifer dennoch ermöglichen, das System vollständig zu kompromittieren. Dennoch ist dieses Szenario viel weniger wahrscheinlich, da der sichere Kernel sehr schlank ist und keine Treiber von Drittanbietern unterstützt, was eine deutlich geringere Angriffsfläche bedeutet.

Isolierter Nutzermodus

VTL1 erstellt einen weiteren interessanten Ausführungsmodus, den „isolierten Nutzermodus“ (Isolated User Mode, IUM), der sich auf den VTL1-Ausführungsmodus „Ring 3“ bezieht. IUM wird zur Ausführung sicherer Prozesse verwendet – ein spezieller Nutzermodusprozess, der die Speicherisolierungsfunktionen von VTL1 nutzt. Auf den Speicher im IUM kann mit keinem VTL0-Code zugegriffen werden. Dies schließt den normalen Kernel ein. Dieser Ausführungsmodus ist das Rückgrat von isolationsbasierten Features wie Credential Guard.

Zusammenfassend lässt sich sagen, dass die Einführung von VTL0/1 vier Ausführungsmodi ergibt (Abbildung 1).

  • Ring 0 VTL0 – normaler Kernelmodus
  • Ring 0 VTL1 – sicherer Kernel-Modus
  • Ring 3 VTL0 – normaler Nutzermodus
  • Ring 3 VTL1 – isolierter Nutzermodus
Zusammenfassend lässt sich sagen, dass die Einführung von VTL0/1 vier Ausführungsmodi ergibt (Abbildung 1). Abb. 1: Ausführungsmodi, die durch Hinzufügen von VTLs erstellt werden

Was sind VBS-Enklaven?

Eine weitere Funktion, die über den IUM ermöglicht wird, sind die VBS-Enklaven. Eine VBS-Enklave ist ein Bereich eines Nutzermodusprozesses im IUM, in den wir DLLs laden können, die als „Enklavenmodule“ bezeichnet werden.

Wird ein Modul in eine Enklave geladen, wird es zu einer „vertrauenswürdigen Ausführungsumgebung“. Die Daten und der Code in der Enklave sind für alle in VTL0 ausgeführten Elemente nicht zugänglich und können daher nicht manipuliert oder gestohlen werden (Abbildung 2). Diese Funktion ist nützlich, um sensible Vorgänge gegenüber Angreifern zu isolieren, die das System kompromittieren könnten.

Wird ein Modul in eine Enklave geladen, wird es zu einer „vertrauenswürdigen Ausführungsumgebung“. Die Daten und der Code in der Enklave sind für alle in VTL0 ausgeführten Elemente nicht zugänglich und können daher nicht manipuliert oder gestohlen werden (Abbildung 2). Abb. 2: Speicherlayout von VBS-Enklaven Quelle: https://learn.microsoft.com/de-de/windows/win32/trusted-execution/vbs-enclaves

Ein Nutzermodusprozess kann die dedizierten Windows-APIs aufrufen, um eine Enklave zu erstellen, ein Modul in die Enklave zu laden und es zu initialisieren. Ein Enklavenmodul hat die Form einer DLL, die speziell für diesen Zweck kompiliert wurde. Nachdem die Enklave initialisiert wurde, kann der hostende Nutzermodusprozess nicht auf ihren Speicher zugreifen. Er kann nur mit ihr interagieren, indem er die von ihren Modulen exportierten Funktionen über die API CallEnclave aufruft.

Abbildung 3 zeigt ein Beispiel für den Code, der diesen Prozess implementiert (ein ausführliches Beispiel wird von Microsoftbereitgestellt).

Abbildung 3 zeigt ein Beispiel für den Code, der diesen Prozess implementiert (ein ausführliches Beispiel wird von Microsoft bereitgestellt). Abb. 3: Beispielcode für die Erstellung von VBS-Enklaven

Da Microsoft den Zugriff auf VTL1 so weit wie möglich einschränken möchte, muss eine DLL, die in eine Enklave geladen wird, mit einem speziellen, von Microsoft ausgestellten Zertifikat ordnungsgemäß signiert werden. Jeder Versuch, ein Modul ohne eine solche Signatur zu laden, schlägt fehl. Die Option zum Signieren von Enklavenmodulen wird nur an vertrauenswürdige Dritte delegiert. Interessanterweise gibt es keine Einschränkungen dahingehend, wer diese Module laden kann. Solange sie signiert sind, kann jeder Prozess beliebige Module in eine Enklave laden.

Enklavenmodule dienen als kleine „Recheneinheiten“, die nur sehr begrenzt mit dem System interagieren oder es beeinflussen können. Aus diesem Grund sind sie auf die Verwendung eines minimalen Satzes von APIsbeschränkt, was sie am Zugriff auf die meisten Betriebssystemkomponenten hindert.

APIs, die in Enklaven verfügbar sind, werden aus dedizierten Bibliotheken importiert, die in VTL1 geladen werden. Zum Beispiel nutzen normale Prozesse die Bibliothek ntdll.dll zur Anforderung von Services vom Betriebssystem, und Enklavenmodule verwenden vertdll.dll – eine „Ersatz“-ntdll, die für die Kommunikation mit dem sicheren Kernel über Syscalls verwendet wird.

Enklaven-Malware

Das Konzept der Enklaven-Malware kann für Angreifer durchaus attraktiv sein, da es zwei bedeutende Vorteile bietet:

  1. Ausführung in einem isolierten Speicherbereich: Der Adressbereich von Enklaven ist für alles, was in VTL0 ausgeführt wird, nicht zugänglich, einschließlich EDRs und Analysetools, was die Erkennung wesentlich komplexer macht.

  2. Nicht rückverfolgbare API-Aufrufe: API-Aufrufe innerhalb einer Enklave können für EDRs unsichtbar sein. EDRs überwachen APIs im Nutzermodus, indem sie Hooks in Systembibliotheken platzieren, und sie setzen Treiber ein, um Aktivitäten im Kernel zu überwachen. API-Aufrufe, die von einer Enklave ausgelöst werden, können mit diesen Methoden nicht erkannt werden, da Enklavenaufrufe von VTL1 aus erfolgen und keine dieser „gehookten“ VTL0-Komponenten durchlaufen.

Abbildung 4 veranschaulicht diesen Vorteil: Ein normaler Prozess, der eine Windows-API aufruft, kann über einen Hook innerhalb von NTDLL oder im Kernel selbst überwacht werden. Ein Enklavenmodul durchläuft jedoch die in VTL1 befindliche vertdll und tätigt Aufrufe zum sicheren Kernel – beides für EDRs nicht zugänglich.

Abbildung 4 veranschaulicht diesen Vorteil: Ein normaler Prozess, der eine Windows-API aufruft, kann über einen Hook in NTDLL oder im Kernel selbst überwacht werden. Abb. 4: Unterschiede zwischen dem normalen Nutzermodus und den IUM-API-Aufrufprozessen

In Anbetracht dieses Potenzials wollten wir uns das Konzept der Enklaven-Malware näher anschauen. Um Enklaven für diesen Zweck zu nutzen, müssen zwei Fragen geklärt werden:

  1. Wie können Angreifer schädlichen Code innerhalb einer Enklave ausführen?
  2. Welche Techniken können Angreifer anwenden, wenn sie einmal in eine Enklave eingedrungen sind?

Wie können Angreifer schädlichen Code innerhalb einer Enklave ausführen?

Wie bereits erwähnt, müssen Enklavenmodule mit einem von Microsoft ausgestellten Zertifikat signiert werden, damit sie geladen werden können, das heißt, dass nur von ihnen genehmigte Elemente ihren eigenen Code innerhalb einer Enklave ausführen können. Dennoch bleiben den Angreifern noch einige Möglichkeiten.

Erstens kann sich ein Angreifer eine Schwachstelle des Betriebssystems zunutze machen. Ein Beispiel hierfür ist die Schwachstelle CVE-2024-49706, die von Alex Ionescu (arbeitet für Winsider Seminars & Solutions) entdeckt wurde und die es einem Angreifer ermöglichen könnte, ein nicht signiertes Modul in eine Enklave zu laden. Für die Schwachstelle wurde von Microsoft zwar ein Patch bereitgestellt, entsprechend motivierte Angreifer könnten in Zukunft aber ähnliche Bugs aufspüren.

Ein zweiter unkomplizierter Ansatz wäre die Beschaffung einer legitimen Signatur. Dies könnte möglich sein, da Microsoft Enklavensignaturen über die Plattform Vertrauensvolle Signatur für Dritte zugänglich macht. Auch wenn dieses Konzept sicher alles andere als trivial ist, könnte ein fortgeschrittener Angreifer in der Lage sein, sich Zugang zu einem Element der vertrauensvollen Signatur zu verschaffen und seine eigenen Enklaven zu signieren.

Zusätzlich zu diesen beiden Optionen haben wir zwei weitere Techniken untersucht, die es Angreifern ermöglichen könnten, Code innerhalb einer VBS-Enklave auszuführen und so debugfähige Enklaven und anfällige Enklaven auszunutzen.

Ausnutzung debugfähiger Enklavenmodule

Beim Erstellen eines VBS-Enklavenmoduls kann ein Entwickler dieses als debugfähigkonfigurieren. Das Kompilieren eines Moduls mit dieser Einstellung ermöglicht, wie der Name schon sagt, es zu debuggen. Da Enklavenmodule in VTL1 ausgeführt werden, ist deren Debugging normalerweise nicht möglich, da der Debugger nicht auf den Speicher der Enklave zugreifen kann, um Daten abzurufen oder Breakpoints zu setzen. Abbildung 5 zeigt ein Beispiel für einen Debugger, der nicht von einer Speicheradresse in einer Enklave lesen konnte.

Abbildung 5 zeigt ein Beispiel für einen Debugger, der nicht von einer Speicheradresse in einer Enklave lesen konnte. Abb. 5: Versuch, den Speicher eines nicht debugfähigen VBS-Enklavenmoduls zu lesen

Interessanterweise wird ein debugfähiges Enklavenmodul bei Ausführung weiterhin in VTL1 geladen. Um das Debuggen zu ermöglichen, implementiert der sichere Kernel einige Ausnahmen, die auf debugfähige Enklavenmodule angewendet werden. Wenn beispielsweise versucht wird, aus dem Speicher eines solchen Moduls zu lesen, gibt der normale Kernel einen Aufruf an den Aufruf des sicheren Kernels SkmmDebugReadWriteMemory aus. Dieser Aufruf überprüft, ob das Zielmodul tatsächlich debugfähig ist, bevor der angeforderte Vorgang ausgeführt wird.

Ein Beispiel dafür ist in Abbildung 6 zu sehen – nach dem Laden eines debugfähigen Enklavenmoduls kann der Debugger erfolgreich aus dem Enklavenspeicher lesen.

Ein Beispiel dafür ist in Abbildung 6 zu sehen – nach dem Laden eines debugfähigen Enklavenmoduls kann der Debugger erfolgreich aus dem Enklavenspeicher lesen. Abb. 6: Erfolgreiches Lesen von derselben Adresse wie in Abbildung 5, wenn das Enklavenmodul debugfähig ist

Auf ähnliche Weise können die Speicherberechtigungen in einem debugfähigen Enklavenmodul auch durch den VTL0-Prozess geändert werden (eine Ausnahme, die im Aufruf des sicheren Kernels SkmmDebugProtectVirtualMemory implementiert ist).

Microsoft empfiehlt Entwicklern dringend, keine debugfähigen Enklavenmodule auszuliefern, da dies den Hauptzweck von Enklaven untergräbt, nämlich einen Speicherbereich von VTL0 zu isolieren (Abbildung 7). Die Verwendung eines debugfähigen Moduls bedeutet, dass die von ihm verarbeiteten Daten leicht offengelegt werden können.

Microsoft empfiehlt Entwicklern dringend, keine debugfähigen Enklavenmodule auszuliefern, da dies den Hauptzweck von Enklaven untergräbt, nämlich einen Speicherbereich von VTL0 zu isolieren (Abbildung 7). Abb. 7: Empfehlung von Microsoft für debugfähige Enklavenmodule. Quelle: https://learn.microsoft.com/de-de/windows/win32/trusted-execution/vbs-enclaves-dev-guide#:~:text=Step%204%3A%20Debugging%20VBS%20enclaves

Die Risiken einer Produktionsanwendung, die ein debugfähiges Enklavenmodul ausführt, liegen also auf der Hand. Angreifer können es jedoch auch für einen anderen Zweck missbrauchen, indem sie unsignierten Code in VTL1 ausführen. Wenn ein Angreifer Zugriff auf ein beliebiges debugfähiges signiertes Enklavenmodul erhält, kann er die Ausführung von VTL1-Code erreichen, indem er die folgenden vier Schritte durchführt:

  1. Ermitteln der Adresse einer Routine innerhalb des Enklavenmoduls mit GetProcAddress
  2. Ändern des Routinespeicherschutzes in RWX
  3. Überschreiben des Routinecodes mit beliebigem Shellcode
  4. Auslösen der Routine mit CallEnclave

Abbildung 8 zeigt Code zur Implementierung dieses Prozesses.

Abbildung 8 zeigt Code zur Implementierung dieses Prozesses. Abb. 8: Beispielcode, der nicht signierten Shellcode innerhalb einer Enklave ausführt

Das offensichtliche Problem aus Angreifersicht ist, dass es sich hierbei um ein zweischneidiges Schwert handelt: Genauso wie der Angreifer auf den Enklavenspeicher zugreifen kann, kann dies auch ein EDR tun. Und dennoch bietet es den Vorteil der Umgehung der API-Überwachung: API-Aufrufe, die vom Enklavenmodul ausgeführt werden, durchlaufen weiterhin VTL1-DLLs und den sicheren Kernel, was die Sichtbarkeit von EDRs begrenzt.

Insgesamt könnte diese Methode nützlich sein, um ein unauffälliges „Semi-VTL1“-Implantat zu entwickeln, das einige der Vorteile nutzt, die sich durch die Ausführung innerhalb einer Enklave ergeben.

Wir haben versucht, VirusTotal und andere Quellen zu verwenden, um ein debugfähiges signiertes Enklavenmodul zu identifizieren, zum Zeitpunkt der Erstellung dieses Textes waren wir damit allerdings bislang erfolglos. Wir sind jedoch davon überzeugt, dass mit genügend Zeit und einer breiteren Akzeptanz der Enklaventechnologie einige Lecks entstehen werden.

Bring Your Own Vulnerable Enclave

Wie bereits besprochen, verwendet Windows Signaturen, um das Laden nicht vertrauenswürdiger Enklaven in VTL1 zu verhindern. Dieser Ansatz gilt nicht nur für Enklaven, das Konzept entstand durch die Treibersignatur-Durchsetzung (Driver Signing Enforcement, DSE), die verhindert, dass nicht vertrauenswürdige Treiber im Windows-Kernel ausgeführt werden.

Um diese Durchsetzung zu umgehen, begannen Angreifer, die Methode „Bring Your Own Vulnerable Driver“ (BYOVD) anzuwenden, das heißt, wenn ein Angreifer seinen eigenen Treiber nicht laden kann, lädt er stattdessen einen legitimen signierten Treiber mit einer bekannten Sicherheitslücke. Er kann diese Sicherheitslücke dann ausnutzen, um nicht signierten Code im Kernel auszuführen.

Wir wollten uns diesen Ansatz im Kontext von Enklaven näher ansehen: Können wir ein anfälliges signiertes Enklavenmodul missbrauchen, um Code im IUM auszuführen?

Der erste Schritt bestand darin, eine solche Enklave zu finden. Dies führte uns schnell zu CVE-2023-36880 , einer Schwachstelle in einem VBS-Enklavenmodul, das von Microsoft Edge genutzt wird. Die Schwachstelle ermöglicht es einem Angreifer, beliebige Daten in der Enklave zu lesen und zu schreiben. Die Schwachstelle wurde von Microsoft als Anfälligkeit für die Offenlegung von Informationen bezeichnet. In den Hinweisen heißt es außerdem, dass sie zu einer eingeschränkten Codeausführung führen kann (Abbildung 9).

Die Schwachstelle wurde von Microsoft als Anfälligkeit für die Offenlegung von Informationen bezeichnet. In den Hinweisen heißt es außerdem, dass sie zu einer eingeschränkten Codeausführung führen kann (Abbildung 9). Abb. 9: Microsoft-Patch-Hinweise für CVE-2023-36880, die darauf hinweisen, dass dies zur Codeausführung führen könnte

Diese Sicherheitslücke wurde von Alex Gough vom Chrome Security Team entdeckt, der auch einen Proof of Concept (PoC) zur Ausnutzung der Schwachstelle vorstellte. Nachdem wir eine anfällige Version dieser Enklave in VirusTotalgefunden hatten, versuchten wir, diese Schwachstelle auszunutzen, um Code auszuführen.

Wir wollten das Read/Write-Primitiv ausnutzen, um den Enclave-Stack mit einer ROP-Kette zu überschreiben, was uns schließlich die Ausführung von Shellcode innerhalb der Enklave ermöglichen würde. Bei der Untersuchung dieser Option sind wir auf eine interessante Tatsache gestoßen: Enklaven sind vor der Ausführung von unsigniertem Code durch Arbitrary Code Guard (ACG)geschützt.

ACG ist eine Abwehrmaßnahme, mit der die Ausführung von dynamisch generiertem Code blockiert wird – also Code, der zur Laufzeit erstellt wird und nicht Teil der ausführbaren Datei des ursprünglichen Prozesses oder seiner DLLs ist. ACG wird durch die Durchsetzung von zwei Regeln implementiert:

  1. Nach dem erstmaligen Laden des Prozesses können keine neuen ausführbaren Seiten generiert werden.
  2. Eine bereits ausführbare Seite kann nicht beschreibbar werden.

ACG wird standardmäßig im normalen Kernel durchgesetzt. Im Nutzermodus ist ACG nur für Prozesse anwendbar, die für seine Verwendung konfiguriert sind. Unsere Untersuchungen haben ergeben, dass für IUM-Prozesse, die Enklaven enthalten, ACG anscheinend automatisch angewendet wird, so wie es auch beim normalen Kernel der Fall ist.

Dies können wir beobachten, indem wir versuchen, eine neue RWX-Seite innerhalb einer Enklave über VirtualAlloc zuzuweisen – der Vorgang schlägt mit Fehlercode 0x677, STATUS_DYNAMIC_CODE_BLOCKED fehl (Abbildung 10). Der Versuch, VirtualProtect zum Ändern der Berechtigungen einer ausführbaren Seite oder zum Umdrehen einer ausführbaren Seite zu verwenden, führt zum gleichen Ergebnis.

Dies können wir beobachten, indem wir versuchen, eine neue RWX-Seite innerhalb einer Enklave über VirtualAlloc zuzuweisen – der Vorgang schlägt mit Fehlercode 0x677, STATUS_DYNAMIC_CODE_BLOCKED fehl (Abbildung 10). Abb. 10: Versuch, eine RWX-Seite innerhalb einer Enklave zuzuweisen

Um dieses Verhalten zu verstehen, können wir SecureKernel!NtAllocateVirtualMemoryExuntersuchen. Dies ist die Funktion des sicheren Kernels, die die dynamische Speicherzuweisung im IUM verarbeitet. Die Funktion wertet die angeforderte Schutzmaske aus, und wenn eine ausführbare Seite angefordert wird, wird der ACG-Fehler STATUS_DYNAMIC_CODE_BLOCKEDzurückgegeben. Ähnliche Prüfungen sind in SkmmProtectVirtualMemory implementiert, um Änderungen an vorhandenen IUM-Seiten zu verhindern (Abbildung 11).

Ähnliche Prüfungen werden in SkmmProtectVirtualMemory implementiert, um Änderungen an vorhandenen IUM-Seiten zu verhindern (Abbildung 11). Abb. 11: Der Code „NtAllocateVirtualMemoryEx“ weist eine ausführbare Speicherzuweisung zurück.

Wir haben keine Methode gefunden, um ACG innerhalb einer Enklave zu umgehen und nicht signierten Code in diese Enklave zu laden. Theoretisch ist ein Full-ROP-Exploit möglich, der Angreifer in die Lage versetzt, z. B. in VTL1 beliebige APIs aufzurufen. Wir haben diese Richtung aber nicht weiter verfolgt. Dennoch konnten wir eine weitere interessante Anwendung für anfällige Enklaven finden, die wir später in diesem Beitragerörtern werden.

Welche Techniken können Angreifer anwenden, wenn sie einmal in eine Enklave eingedrungen sind?

Da wir wissen, dass Enklaven ein großes Potenzial für bösartige Aktivitäten bergen und dass ein motivierter Angreifer diese durchaus starten kann, haben wir uns als Nächstes gefragt, welche Techniken für diese Art von Malware zur Verfügung stehen könnten.

Am einfachsten wäre es, Enklaven entsprechend ihrem Zweck zu verwenden. So wie eine Enklave sensible Daten vor Angreifern schützen kann, kann sie auch von Angreifern verwendet werden, um ihre eigenen „Geheimnisse“ vor VTL0-Elementen zu verbergen.

Dies könnte in vielerlei Szenarien nützlich sein – durch Speichern von Nutzlasten außerhalb der Reichweite von EDRs, durch Versiegeln von Verschlüsselungsschlüsseln, die vor Analysten verborgen bleiben, oder durch Verhindern, dass sensible Malware-Konfigurationen in Speicherauszügen gespeichert werden.

Was fortschrittlichere Optionen anbelangt, so sind viele herkömmliche Malware-Techniken innerhalb einer Enklave unmöglich zu implementieren. Da Enklaven auf eine begrenzte Untergruppe von System-APIs beschränkt sind, können sie nicht mit wichtigen Betriebssystemkomponenten wie Dateien, der Registrierung, dem Netzwerk, anderen Prozessen usw. interagieren.

Dennoch gibt es noch eine Reihe von Techniken, die innerhalb einer Enklave ausgeführt werden können und es uns ermöglichen, von den Vorteilen zu profitieren, die sich aus der Ausführung im IUM ergeben.

Zugriff auf den VTL0-Nutzermodusspeicher

Trotz ihres eingeschränkten Zugriffs auf das System haben Enklaven immer noch Zugang zu einer wichtigen Ressource: dem Prozessspeicher. Enklaven können Lese-/Schreibvorgänge im gesamten Prozessadressbereich durchführen, VTL0 inbegriffen.

Für diesen Zugriff gelten einige Einschränkungen. Code, der innerhalb von Enklaven ausgeführt wird, hält sich an die Speicherberechtigungen und kann diese nicht ändern. Dies bedeutet, dass eine Enklave nicht in der Lage ist, in nicht beschreibbaren Speicher zu schreiben oder nicht ausführbaren Speicher ausführbar zu machen. Abbildung 12 zeigt Code, der innerhalb einer Enklave ausgeführt wird, und stellt die verschiedenen Möglichkeiten und Einschränkungen dar.

Abbildung 12 zeigt Code, der innerhalb einer Enklave ausgeführt wird, und stellt die verschiedenen Möglichkeiten und Einschränkungen dar. Abb. 12: Beispielcode für Zugriffsszenarien auf VTL0-Speicher von einer Enklave

Durch den Zugriff auf den VTL0-Nutzermodusspeicher können wir verschiedene nützliche Techniken implementieren. Indem wir eine schädliche Enklave in einen Zielprozess laden, können wir heimlich sensible Informationen überwachen und stehlen oder Werte innerhalb des Prozesses patchen, um sein Verhalten zu ändern.

Die Implementierung dieser Techniken unter Verwendung einer Enklave hat einen erheblichen Vorteil: Wie bereits beschrieben können wir durch das Auslösen von APIs aus einer Enklave die Überwachung durch EDRs umgehen. Da der Speicherzugriff bei diesen Techniken von einer Enklave durchgeführt wird, können sie unsichtbar bleiben.

Ausführung von VTL0-Nutzermoduscode

Obwohl eine Enklave mit den entsprechenden Berechtigungen aus dem VTL0-Nutzermodusspeicher lesen/schreiben kann, kann in VTL0 gespeicherter Code niemals innerhalb einer Enklave ausgeführt werden, selbst wenn er über Ausführungsberechtigungen verfügt.

Auch wenn VTL0-Code nicht innerhalb einer Enklave ausgeführt werden kann, hat eine Enklave die Möglichkeit, ihn „remote“ auszulösen. Durch Verwendung der API CallEnclave mit einer VTL0-Adresse kann eine Enklave die Ausführung von VTL0-Code in einem normalen Nutzermodus-Thread auslösen (Abbildung 13).

Durch die Verwendung der CallEnclave-API mit einer VTL0-Adresse kann eine Enklave die Ausführung von VTL0-Code in einem normalen Nutzermodus-Thread auslösen (Abbildung 13). Abb. 13: Beispielcode, der zeigt, dass eine Enklave VTL0-Code nicht ausführen kann.

Indem sie den Prozess im Nutzermodus dazu bringen, „in ihrem Auftrag zu handeln“, können Enklaven indirekt auf das System in einer Weise zugreifen, wie es ihnen normalerweise nicht möglich ist. Eine Enklave kann z. B. eine VTL0-Routine auslösen, die aus einer Datei liest, einen Socket erstellt usw.

Es sei darauf hingewiesen, dass das Ausführen von Nutzermoduscode auf diese Weise keinen Vorteil in Bezug auf die Umgehung hat. Da der Code wie jeder andere Nutzermoduscode ausgeführt wird, kann er für EDRs sichtbar sein.

Anti-Debugging

Eine weitere interessante Anwendung für Enklaven-Malware ist das Anti-Debugging. Die Tatsache, dass innerhalb der Enklave ausgeführter Code für VTL0-Anwendungen, einschließlich Debuggern, unzugänglich bleibt, verschafft der Malware einen erheblichen Vorteil gegenüber diesen.

Die Reduzierung der APIs, die für Enklaven zugänglich sind, bedeutet, dass nicht alle herkömmlichen Anti-Debugging-Techniken von einer Enklave aus verfügbar sind. Beispielsweise sind die APIs NtQueryInformationProcess oder IsDebuggerPresent und alle Datums- und Zeit-APIs für Enklaven nicht verfügbar. Und doch bleiben uns noch ein paar Möglichkeiten.

Erstens können wir uns auf den Zugriff der Enklave auf den VTL0-Prozessadressbereich verlassen, was es ihr ermöglicht, den Prozess-PEB manuell zu lesen und den Wert des FlagsBeingDebuggedzu überprüfen. Wird ein Debugger erkannt, kann der Prozess von der Enklave beendet werden.

Ein weiterer Ansatz wäre die Implementierung einer zeitbasierten Anti-Debugging-Methode (Abbildung 14). Obwohl Datums- und Zeit-APIs für Enklaven nicht verfügbar sind, können sie dennoch die Assemblierungsanweisung rdtsc verwenden. Diese Anweisung gibt die Anzahl der Prozessortaktzyklen seit dem Booten zurück. Damit können wir die Zeit zwischen verschiedenen Enklavenaufrufen messen und den Prozess beenden, wenn eine erhebliche Verzögerung erkannt wird.

Ein weiterer Ansatz wäre die Implementierung einer zeitbasierten Anti-Debugging-Methode (Abbildung 14). Abb. 14: Zeitbasiertes Anti-Debugging innerhalb einer Enklave mit der rdtsc-Assemblierungsanweisung

Indem wir kritische Teile unseres Codes in eine Enklave verschieben und eine Anti-Debugging-Prüfung durchführen, sind wir in der Lage, eine Malware zu erstellen, die für dynamische Analysen nahezu unangreifbar ist.  Die Malware verlässt sich darauf, dass die Enklave ordnungsgemäß ausgeführt wird, während der Nutzermodusprozess die darin ausgeführten Prüfungen nicht patchen kann. Richtig implementiert könnte dieser Ansatz nur durch das Debuggen von Hyper-V oder des sicheren Kernels umgangen werden.

BYOVE – Runde 2

In einem vorherigen Abschnitt versuchten wir, ein anfälliges Enklavenmodul zu verwenden, um eine Codeausführung im IUM zu erreichen. Als wir erkannten, dass dies vielleicht nicht möglich ist, dachten wir uns, dass das BYOVE-Konzept auch andere Anwendungen haben könnte, und beschlossen, das anfällige Enklavenmodul weiter zu untersuchen.

Die Schwachstelle (CVE-2023-36880) ist auf die Funktionen SealSettings und UnsealSettings zurückzuführen, die vom Enklavenmodulexportiert werden. Die Funktion SealSettings empfängt einen Verweis auf einen Datenpuffer, verschlüsselt ihn und schreibt die Ergebnisse in eine Zieladresse, die vom Aufrufer bereitgestellt wird. UnsealSettings funktioniert auf ähnliche Weise und entschlüsselt einen angegebenen Puffer und schreibt ihn in eine angegebene Adresse.

Das Problem bei beiden Funktionen ist, dass weder die Zieladresse noch die Quellpufferadresse validiert werden, sodass sie auf eine beliebige Adresse innerhalb des Prozesses verweisen können, auch auf Adressen innerhalb der Enklave selbst. Abbildung 15 zeigt den anfälligen Code, wobei UnsealSettings einen memcpy-Vorgang zu einer vom Nutzer angegebene beliebige Adresse durchführt.

In Abbildung 15 ist der gefährdete Code dargestellt, der UnsealSettings bei der Ausführung eines „memcpy“ an eine vom Nutzer angegebene beliebige Adresse zeigt. Abb. 15: Der gefährdete Code innerhalb der UnsealSettings-Funktion

Diese Schwachstelle bietet Angreifern zwei Möglichkeiten (Abbildung 16).

Diese Schwachstelle bietet Angreifern zwei Möglichkeiten (Abbildung 16). Abb. 16: Ausnutzung von CVE-2023-36880 zum Lesen/Schreiben beliebiger Daten innerhalb der Enklave
  1. Willkürliches Schreiben innerhalb der Enklave: Ein Angreifer kann SealSettings aufrufen, um beliebige Daten zu verschlüsseln, und dann UnsealSettings aufrufen, um auf eine Zieladresse in der Enklave zu verweisen. Dies führt dazu, dass die ursprünglichen Daten in den Enklavenspeicher geschrieben werden.

  2. Willkürliches Lesen innerhalb der Enklave: Ein Angreifer kann SealSettingsaufrufen und dabei eine Adresse innerhalb der Enklave als Quellpufferzeiger angeben. Dies führt dazu, dass die Enklave Daten aus dem Enklavenspeicher verschlüsselt und an einen vom Angreifer kontrollierten Ort schreibt. Der Angreifer kann diese Daten dann durch Aufrufen von UnsealSettingsentschlüsseln und so beliebige Daten aus der Enklave lesen.

Mirage – VTL1-basierte Speicherumgehung

Obwohl es uns nicht ermöglicht hat, Code in VTL1 auszuführen, bietet das Arbitrary-Write-Primitiv zwei einzigartige Möglichkeiten: 

  1. Speichern beliebiger Daten in VTL1, wo sie für VTL0-Elemente nicht mehr zugänglich sind

  2. Schreiben beliebiger Daten in den normalen Prozessadressbereich (VTL0) von VTL1, sodass verhindert wird, dass VTL0-Elemente diese Operation überwachen können

Da diese Fähigkeiten durch das Laden eines signierten Enklavenmoduls ermöglicht werden, können sie von jedem Angreifer genutzt werden, ohne dass dieser selbst etwas signieren muss.

Um das Potenzial dieser Fähigkeiten zu demonstrieren, haben wir eine Technik zur Umgehung von Speicherscans entwickelt, die wir „Mirage“ genannt haben. Mirage wurde inspiriert von Gargoyle, einer Umgehungsmethode, bei der eine Nutzlast erzeugt wird, die ständig zwischen einem harmlosen und einem „bewaffneten“ Zustand wechselt (Abbildung 17).

Mirage wurde von Gargoyle inspiriert, einer Umgehungsmethode, bei der eine Nutzlast erzeugt wird, die ständig zwischen einem harmlosen und einem „bewaffneten“ Zustand wechselt (Abbildung 17). Abb. 17: Mirage-Speicherumgehung

Während Gargoyle dies durch Umschalten zwischen ausführbarem und nicht ausführbarem Speicher implementiert, zielt Mirage darauf ab, ein ähnliches Ergebnis durch den Übergang zwischen VTL1- und VTL0-Speicher zu erzielen. Mirage speichert den Shellcode im VTL1-Enklavenspeicher, überträgt ihn unter Ausnutzung der Schwachstelle regelmäßig zurück zu VTL0, führt den Shellcode aus und löscht ihn dann umgehend aus dem VTL0-Speicher (Abbildung 18).

Mirage speichert Shellcode im VTL1-Enklavenspeicher, überträgt ihn regelmäßig über die Sicherheitslücke zurück an VTL0, führt den Shellcode aus und löscht ihn dann umgehend aus dem VTL0-Speicher (Abbildung 18). Abb. 18: Lebenszyklus des Mirage-Umgehungsprozesses

Dieser Ansatz hat zwei wesentliche Vorteile. Erstens ist die Nutzlast resistent gegen Speicherscans und -auszügen, da sie die meiste Zeit in VTL1 verborgen ist. Dies hat gegenüber der Gargoyle-Technik den Vorteil, dass unsere Nutzlast in der Ruhephase nicht nur „getarnt“, sondern auch nicht zugänglichist.

Der zweite Vorteil ist, dass das Schreiben des Shellcodes in VTL0 durch die Enklave erfolgt. Wie zuvor beschrieben, können EDRs in VTL1 ausgeführten Code nicht überwachen, was bedeutet, dass typische EDR-Hooks den Shellcode nicht abfangen können, während er in den Speicher geschrieben wird.

Wir haben einen PoC für Mirage erstellt (Abbildung 19). Dieser PoC soll nur die Idee hinter der Technik demonstrieren und ist daher nicht vollständig.

Wir haben ein PoC für Mirage entwickelt (Abbildung 19). Abb. 19: Demonstration der Mirage-Ausführung

Erkennung

Aktuell werden Enklaven nur von einer sehr begrenzten Anzahl von Anwendungen genutzt. Und selbst wenn sie umfassender eingesetzt werden sollten, werden sie nur von bestimmten Prozessen und nicht von beliebigen Prozessen geladen. Beispielsweise sollte calc.exe wahrscheinlich niemals eine VBS-Enklave laden.

Deshalb kann die anormale Verwendung von Enklaven eine großartige Erkennungschance darstellen. Sicherheitsspezialisten sollten sich dies zunutze machen, indem sie eine Baseline-Liste bekannter, legitimer Verwendungen von VBS-Enklaven erstellen und alle Abweichungen von dieser Liste kennzeichnen. Der Einsatz von Enklaven lässt sich auf zweierlei Arten erkennen: durch die Überwachung von Enklaven-APIs und durch die Erkennung von geladenen Enklaven-DLLs.

Enklaven-APIs

Die folgenden APIs werden zur Verwaltung von VBS-Enklaven durch einen Hosting-Prozess verwendet und weisen wahrscheinlich darauf hin, dass eine VBS-Enklave geladen wird:

  • CreateEnclave
  • LoadEnclaveImageW
  • InitializeEnclave
  • CallEnclave
  • DeleteEnclave
  • TerminateEnclave

Geladene Enklaven-DLLs

Eine weitere Möglichkeit zur Erkennung einer anormalen Enklavennutzung wäre die Erkennung des Ladens von Umgebungs-DLLs, die typischerweise von Enklaven verwendet werden, d. h. vertdll.dll und ucrtbase_enclave.dll. Da diese DLLs nur von einer Enklave verwendet werden sollten, weist ihre Anwesenheit darauf hin, dass der Prozess wahrscheinlich eine Enklave verwendet.

Fazit

VBS-Enklaven bieten Entwicklern ein großartiges Tool für den Schutz sensibler Bereiche ihrer Anwendungen. Wie wir gerade gezeigt haben, können sie aber auch von Bedrohungsakteuren zum „Schutz“ ihrer Malware verwendet werden. Obwohl dieses Konzept zum jetzigen Zeitpunkt eher theoretisch ist, ist es durchaus möglich, dass fortschrittliche Bedrohungsakteure in Zukunft VBS-Enklaven für bösartige Zwecke einsetzen. 

Danksagung

Wir möchten an dieser Stelle die Arbeit von Matteo Malvica von Offsec und Cedric Van Bockhaven von Outflank würdigen, die vor Kurzem ein Forschungsprojekt durchgeführt haben, das diesem hier sehr ähnlich ist. Bitte sehen Sie sich ihren ersten Blogbeitrag an – eine zweiteilige Serie. Freuen Sie sich außerdem auf Teil 2, der nach dem Insomni'Hack 2025 veröffentlicht wird.



Ori David

Verfasser

Ori David

February 25, 2025

Ori David

Verfasser

Ori David

Ori David ist Sicherheitsforscher bei Akamai. Seine Forschung konzentriert sich auf offensive Sicherheit, Malware-Analyse und Bedrohungssuche.