Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2024-6769 — Aktivierungscache-Poisoning zur Anhebung von mittlerer auf hohe Integrität (CVE-2024-6769) | Kitploit
Tools/GitHubGitHub/fortra/cve-2024-6769
Privilege EscalationSchwachstellenanalyseExploitationPapers & ForschungLernen & BildungBinary-Exploitation
GitHubfortra/cve-2024-6769

CVE-2024-6769

Aktivierungscache-Poisoning zur Anhebung von mittlerer auf hohe Integrität (CVE-2024-6769)

Repository anzeigen
7811vor 1 JahrVon Kitploit geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

Blogpost: CVE-2024-6769 Vergiftung des Aktivierungscaches zur Erhöhung von mittlerer auf hohe Integrität

Dieser Blogbeitrag handelt von zwei verketteten Bugs: Stufe eins ist ein DLL-Hijacking-Bug, der durch das Remapping des ROOT-Laufwerks verursacht wird, und Stufe zwei ist ein Bug zur Vergiftung des Aktivierungscaches, der vom CSRSS-Server verwaltet wird.

Die erste Stufe wurde auf der Ekoparty 2023 in der Präsentation mit dem Titel "I'm High" von Nicolás Economou im Detail vorgestellt, von BlueFrost Security. Er erklärte, wie die Schwachstelle ausgenutzt werden kann, die zu diesem Zeitpunkt noch nicht von Microsoft gepatcht worden war. Dadurch konnte ein Benutzer mit MEDIUM INTEGRITY auf begrenzte HIGH PRIVILEGES angehoben werden, jedoch ohne den vollständigen Zugriff, um ein vollwertiger Administrator zu sein.

Die zweite Stufe wurde auf dieser Konferenz nicht vorgestellt, obwohl einige Schritte vorgeschlagen wurden, um mit der Erforschung zu beginnen.

Zunächst werden wir die erste Stufe überprüfen, um einen einführenden Kontext zu schaffen. Von dort aus werden wir tief in meine Forschung zur zweiten Stufe eintauchen, und dabei ins Detail gehen, wie die vollständige Eskalation von begrenzter HIGH INTEGRITY zum vollwertigen Administrator erreicht wird. Dies beinhaltet einen vollständig funktionierenden PoC für beide Stufen für alle Windows-Versionen, der erfolgreich auf Windows 10, Windows 11, Windows Server 2022 und Windows Server 2019 mit allen angewendeten Updates getestet wurde.

Index:

  • Überblick über die erste Stufe
  • Schritte zur Ausnutzung der zweiten Stufe.
  • Was ist der Aktivierungscache?
  • Verwendung des ALPC-Angriffsvektors zur Vergiftung des Aktivierungscaches
  • Wie akzeptiert das System unseren Aktivierungskontext?
  • Wie vergiftet man den Aktivierungscache?
  • Wie wird mein eingebettetes XML-Manifest gelesen?
  • Wie wird das eingebettete XML-Manifest geparst?
  • Wie wurde meine gefälschte imm32.dll geladen?
  • Video-Demo.
  • Funktionierender Proof of Concept
  • TL;DR Kurze Beschreibung der Ausnutzungsschritte

Überblick über die erste Stufe

Ein rotes Quadrat mit weißem Text und einer Nummer darauf. Beschreibung
automatisch generiert

Die einzige Voraussetzung für diese Stufe ist, dass der anfängliche Prozess auf einer MEDIUM INTEGRITY-Ebene beginnt und der Benutzer zur Gruppe der Administratoren gehört.

Die erste Stufe der Ausnutzung kann in den folgenden Schritten zusammengefasst werden:

  1. Remapping des ROOT-Laufwerks mithilfe der NtCreateSymbolicLinkObject Funktion.

Zum Beispiel: Remapping des Laufwerks von "C:\" zu "C:\users\public"

Dadurch wird auch der Ordner "system32" von "C:\windows\system32" zu "C:\users\public\windows\system32" umgemappt.

  1. Nach dem Remapping sind einige Dienste betroffen und versuchen, Bibliotheken aus dem neuen, gefälschten, vom Benutzer kontrollierten system32 zu laden.

Eines dieser betroffenen Programme ist CTFMON, das auf einer HIGH INTEGRITY-Ebene läuft, jedoch ohne Administratorrechte.

Normalerweise versucht es, das Modul MsCtfMonitor.dll aus dem echten system32-Ordner zu laden, aber da das ROOT-Laufwerk umgemappt wurde, sucht es nach MsCtfMonitor.dll in unserem gefälschten, kontrollierten system32, wo wir eine präparierte DLL mit demselben Namen erstellen und ablegen können.

  1. MsCtfMonitor.dll erstellen

An diesem Punkt wird durch das Platzieren unserer Version von MsCtfMonitor.dll im gefälschten system32-Ordner die Funktion DoMsCtfMonitor aufgerufen und führt unseren Code auf einer HIGH INTEGRITY-Ebene aus.

  1. Platziere eine MessageBoxA in der Funktion DoMsCtfMonitor. Wenn MsCtfMonitor.dll geladen wird, zeigt sie die MessageBoxA „TRIGGER".

  2. Überprüfe, ob die DLL in den CTFMON-Prozess geladen wurde, der auf der HIGH INTEGRITY-Ebene läuft:

Gleichzeitig können wir bestätigen, dass der Prozess, obwohl er sich auf einer HIGH INTEGRITY-Ebene befindet, keine Administratorrechte besitzt:

Schritte zur Ausnutzung der zweiten Stufe Ein rotes Quadrat mit weißem Text und einer Nummer darauf. Beschreibung automatisch generiert

In seiner Ekoparty-Präsentation schlug Nicolas die folgenden Schritte vor, um die Ausnutzung abzuschließen:

Auch wenn dies einfach erscheint, erfordert es viel Zeit für Reversing und Debugging.

Beim näheren Eintauchen in diese Angriffsvektor-Geschichte wurde klar, dass die Vergiftung des Aktivierungskontext-Caches bereits in einigen Exploits verwendet wurde. Folglich lohnt es sich zu lernen, wie die Ausnutzung bisher durchgeführt wurde, um zusätzlichen Kontext und Einblicke zu gewinnen. Details zu dieser Ausnutzung sind im Write-up der Zero Day Initiative verfügbar: Activation Context Cache Poisoning: Exploiting CSRSS for Privilege Escalation.

Was ist der Aktivierungscache?

Der Aktivierungscache kommt zum Einsatz, wenn ein Programm eine Bibliothek laden möchte, die eine bestimmte Version erfordert.

Wenn eine Anwendung beispielsweise C:\Windows\System32\comctl32.dll laden möchte, gibt es keine Garantie, dass die comctl32.dll an diesem Speicherort die Version ist, die die Anwendung benötigt. Dies ist ein grundlegender Anwendungsfall des Aktivierungskontext-Caches. Das Programm kann eine Anfrage an den CSRSS-Server senden, um einen neuen Aktivierungskontext-Eintrag zu verarbeiten, der in den Cache aufgenommen werden soll, sodass dieses Programm die benötigte spezifische Bibliotheksversion laden kann.

Zu diesem Zweck wird das sogenannte Manifest verwendet, das im XML- Format vorliegt. Es ist normalerweise als Ressource in einer EXE- oder DLL-Datei eingebettet. Alternativ sucht Windows nach einer Manifestdatei im selben Ordner, in dem sich die ausführbare Datei des Programms befindet.

Die oben erwähnte URL enthält einige Beispiele für Manifestdateien, die von alten Exploits verwendet wurden, etwa um das System dazu zu bringen, die Bibliothek advapi32.dll aus einem vom Angreifer kontrollierten Verzeichnis zu laden, das durch die PATH-TRAVERSAL-Technik erreicht wurde.

Natürlich wurden einige verwendete Angriffsvektoren gepatcht, und einige neue Techniken wurden entdeckt. Darüber hinaus wurde im Oktober-2022-Patch für Windows 11 22H2 eine neue Prüfung hinzugefügt.

Nach der Implementierung dieses Patches kann die Prüfung, wenn ein Aktivierungskontext (ACTX) registriert wird, nur umgangen werden, wenn der Prozess, der den neuen Eintrag im Cache hinzufügt, dieselbe oder eine höhere RID hat als der Prozess, der ihn verwenden wird.

In winnt.h können wir die RID-Werte sehen:

Der Vorschlag zur Umgehung dieser Prüfung besteht darin, eine Anfrage mit einem Aktivierungskontext aus dem CTFMON-Prozess zu erstellen, in dem die präparierte DLL läuft. Diese präparierte DLL hat RID=0x3000, und nachdem der Eintrag dem Cache hinzugefügt wurde, wird TCMSETUP mit RID=0x3000 die tapi32.dll laden.

Bei meinem Versuch, den Schritten zu folgen, habe ich alle möglichen Kombinationen ausprobiert, um den ACTX mithilfe von CreateActCtx zu registrieren. Dies erwies sich als unmöglich, da es immer eine Prüfung gab, die dies verhindert.

Es ist wichtig zu beachten, dass sich diese Funktion im Userland befindet, von kernel32.dll exportiert wird. Die Prüfungen können durch Patchen der DLL im Speicher umgangen werden, was nicht sehr elegant ist, aber möglich und funktionsfähig sein sollte.

Die Präsentationsfolie von Nicolas schlägt die Verwendung von LOW LEVEL vor. Allerdings, angesichts des zwinkernden Smileys war klar, dass die Verwendung von CreateActCtx nicht die bessere Option ist, wenn dieser Bug ohne Patch ausgenutzt wird.

Verwendung des ALPC-Angriffsvektors zur Vergiftung des Aktivierungscaches

Ein Advanced Local Procedure Call (ALPC) ist ein prozessübergreifender Kommunikationsmechanismus zum schnellen Senden von Nachrichten innerhalb des Windows-Betriebssystems. Im Gegensatz zur Standard-Windows-API ist ALPC für Anwendungen nicht direkt verfügbar. Stattdessen ist es ein interner Mechanismus, auf den nur Komponenten des Windows-Betriebssystems zugreifen können. (Und wir .)

Bei weiterer Recherche stellte ich fest, dass einige alte Cache-Poisoning-Exploits ALPC verwendeten, um direkt mit dem Server zu kommunizieren. Ein Beispiel ist in Philip Tsukermans Artikel Activation Contexts—A Love Story.

Die Funktion CsrClientCallServer implementiert die ALPC-Schnittstelle zwischen Win32-Prozessen und dem CSRSS-Prozess.

Daher sollte ein Aufrufversuch an den CSRSS-Prozess, der als Server fungiert, mithilfe von CsrClientCallServer erfolgen.

Bei der Suche nach Beispielen in älteren Exploits fand ich eine Seite auf Packet Storm über ein relevantes Heap-Buffer-Overflow- Problem.

Wenn der CSRSS-Server mit dem korrekten Paket aufgerufen wird, wird es in der Funktion BaseSrvSxsCreateActivationContextFromMessage empfangen, die zum Modul sxssrv.dll gehört.

Die Funktion hat nur ein Argument: den Zeiger auf das empfangene Paket. Zum Reversing habe ich eine benutzerdefinierte TotalMessage-Struktur erstellt.

Das TotalMessage-Strukturpaket hat seine ersten 0x40 Bytes als HEADER, gefolgt von der eingebetteten Activation Context Message, deren Struktur _BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG ist.

Die TotalMessage-Struktur ist unten zu sehen:

Und hier ist die _BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG Struktur**:**

Innerhalb dieser Struktur gibt es sechs UNICODE_STRINGS, die der Sprache oder CultureFallbacks, AssemblyDirectory, TextualAssemblyIdentity, AssemblyName, und zwei _BASE_MSG_SXS_STREAM-Strukturen, die jeweils eine UNICODE_STRING enthalten.

Unten ist die _BASE_MSG_SXS_STREAM-Struktur:

Angesichts der Schwierigkeit, ein gültiges, vom Server akzeptiertes Paket zu erstellen, lohnt es sich, im Detail zu beschreiben, wie man das macht.

Der Wert des Flags-Felds innerhalb von _BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG ist sehr wichtig, da es viele Kombinationen gibt. Ohne den korrekten Flag-Wert kann der Bug nicht ausgenutzt werden.

Nehmen wir zum Beispiel meinen MsCtfMonitor.dll-Code. Nach vielen Versuchen kam ich zu dem Schluss, dass der eine korrekte flags-Wert für diese Bug-Ausnutzung 0x41 ist:

Die Kombination unterschiedlicher Werte könnte zu einem falschen Pfad-Flag- Wert führen:

Dieselbe TotalMessage-Struktur hat einen Header mit einer Größe von 0x40 Bytes. Die restlichen 0x1f8 Bytes sind für die _BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG-Struktur reserviert:

struct TotalMessage

{

signed __int64 pad[8];

_BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG message;

};

Die Größe für die Allokation beträgt 0x40+0x1f8:

Danach habe ich die Strings zusammengestellt und einen Aktivierungscache- Kontext für tapi32.dll durchgeführt. Dies ist eine sehr selten verwendete DLL, die von einem Prozess namens TCMSETUP geladen wird. Sie hat eine HIGH PRIVILEGES INTEGRITY LEVEL (RID=0x3000) mit denselben Rechten wie ein Administrator.

In meinem DLL-Code wird die Funktion CaptureUnicodestring aufgerufen. Dies führt letztendlich zu CsrCaptureMessageString:

NTSTATUS CaptureUnicodeString(LPVOID CaptureBuffer, PSTR OutputString,

PCWSTR String, ULONG Length = 0) {

if (Length == 0) {

Length = lstrlenW(String);

}

return CsrCaptureMessageString(CaptureBuffer, (PCSTR)String, Length * 2,

Length * 2 + 2, OutputString);

}

Dieser Schritt ist notwendig, um das Paket korrekt vorzubereiten, damit das System die Strings meines Pakets in den Prozess CSRSS kopieren kann. Dies hält die Strings gültig und ersetzt meine Zeiger durch gültige Zeiger in dessen Kontext.

Ich habe außerdem ein eingebettetes XML-Manifest mit der Sprache "Tasks" darin hinzugefügt, Dies ist eine nicht existierende Sprache, aber sie wird der Schlüssel zur Ausnutzung sein (Kudos an Nico dafür):

Ein weiteres wichtiges Detail in meinem Code ist die Erstellung von CaptureBuffer. Die Funktion CsrAllocateCaptureBuffer hat ein Argument, das festlegt, wie viele UNICODE_STRINGS sie verwalten und an den Server kopieren soll.

In meinem Fall habe ich „4" Strings verwendet:

Das Argument mit dem Wert „4" ist unten zu sehen:

Um den Aktivierungsserver zu erreichen, sendet die Funktion CsrClientCallServer mein Paket aus meiner MsCtfMonitor.dll mit derselben ApiNumber 0x1001001E wie die oben erwähnten alten Exploits.

Geoff Chappells Blog bietet weitere Details zu CsrClientCallServer:

Hier ist der Aufruf von CsrClientCallServer:

Und hier ist das zu sendende Paket, das in meiner DLL erstellt wurde:

Der Manifest.Offset-Wert zeigt auf mein eingebettetes XML-Manifest:

Ein interessanter Befehl zum Protokollieren des Aktivierungsprozesses ist sxstrace, der in einer Administratorkonsole auf dem Zielsystem verwendet wird.

Dieser Befehl aktiviert die Ablaufverfolgung und speichert die Protokollergebnisse in sxstrace.etl. (Drücke ENTER, um die Ablaufverfolgung zu beenden.)

sxstrace trace -logfile:sxstrace.etl

Die rohe sxstrace.etl-Datei kann dann in ein lesbares Format konvertiert werden:

sxstrace parse -logfile:sxstrace.etl -outfile:sxstrace.txt

Akzeptiert das System unseren Aktivierungskontext?

Wenn das Paket korrekt ist, sollte es die Funktion BaseSrvSxsCreateActivationContextFromMessage im Modul sxssrv von csrss erreichen. Beim Debuggen des Remote-Kernels muss der Kontext daher auf diesen Prozess umgestellt werden. Anschließend müssen die User-Mode-Symbole neu geladen werden, um einen Breakpoint darauf zu setzen:

Ich habe IDA PRO zum Debuggen des Kernels mit dem Windbg-Plugin verwendet:

Sobald es bei BaseSrvSxsCreateActivationContextFromMessage anhält, wird RCX auf die TotalMessage-Struktur zeigen:

Nach den ersten 0x40 HEADER-Bytes (die vom System mit einigen Werten wie der PID des Client-Prozesses usw. gefüllt werden), ist meine Aktivierungsnachricht, die zur _BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG-Struktur gehört, zu sehen:

Beachte, dass die Zeiger auf die Strings nicht denselben Wert haben wie beim Senden:

Aber sie zeigten korrekt auf die Strings:

Als das Paket vom Client an den Server gesendet wurde, kopierte das System die Strings aus meinem Prozess in den Prozess CSRSS und änderte die Zeiger in meinem Paket, sodass sie in dessen Kontext gültig sind.

Danach prüft die Funktion BaseSrvSxsCreateActivationContextFromMessage, ob die Strings gültig sind.

In einer Schleife prüft es sechs Strings, besteht die Prüfung aber einwandfrei. In meinem Fall habe ich nur vier Strings übergeben, die anderen beiden sind Null.

Nach weiteren kleineren Prüfungen ruft es BaseSrvSxsCreateActivationContextFromStructEx auf, die wichtigste Funktion im Aktivierungsprozess:

Wie vergiftet man den Aktivierungscache?

Sobald man bei BaseSrvSxsCreateActivationContextFromStructEx angekommen ist, zeigt r8 auf _BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG, also auf die Aktivierungsnachricht:

Es wertet den Wert der Flags aus. In meinem Fall betrug der Wert 0x41 gegenüber 0xD:

Die test-Funktion kann mithilfe der Flag-Option umgangen werden, die der Validierung der Prozessorarchitektur (1) entspricht.

Danach ermittelt es die RID des aufrufenden Prozesses und speichert sie für weitere Vergleiche. In diesem Fall ist die RID 0x3000, da CTFMON die HIGH INTEGRITY-Ebene hat.

Der wichtigste Teil dieser Funktion ist der Aufruf von BaseSrvActivationContextCacheLookupEntry:

Es durchsucht den Aktivierungskontext-Cache, um festzustellen, ob es einen Eintrag für tapi32.dll gibt.

Es ruft eine Funktion namens BaseSrvActivationContextCacheCompareEntries auf, die bestimmte Teile des Activation Message-Eintrags mit allen vorhandenen Einträgen im Cache vergleicht:

Es vergleicht den LastWriteTime-Wert, der in meinem Paket gesendet wurde, mit demselben Wert in allen Einträgen.

Ich hatte diesen Wert zuvor mithilfe von GetFileTime in tapi32.dll berechnet und ihn in meinem Aktivierungspaket gesendet:

Da es keinen Eintrag für tapi32.dll gibt, werden die Vergleiche nicht übereinstimmen. Wie erwartet, gibt es den Fehler 0xC0000225 zurück. Danach prüft es meinen ACTX, um festzustellen, ob er geeignet ist, dem Cache hinzugefügt zu werden:

Der Server muss mein eingebettetes XML-Manifest und die Manifest.Offset-Adresse lesen, die darauf zeigte. In diesem neuen Kontext ist es jedoch noch kein gültiger Zeiger. Es lohnt sich, einen Breakpoint auf diesen Wert zu setzen, um zu sehen, wie und wann mein eingebettetes XML-Manifest gelesen wird mithilfe dieses Werts.

Wie wird mein eingebettetes XML-Manifest gelesen?

Um zu überprüfen, wo CSRSS mein eingebettetes XML-Manifest liest, das in meiner ACTX-Anfrage gesendet wurde, sollten Breakpoints auf Manifest.Offset gesetzt werden. Zusätzlich sollten jedes Mal Breakpoints hinzugefügt werden, wenn es anhält, falls es in eine andere Adresse kopiert.

Es hält am Breakpoint an, wenn die Adresse des Manifest.Offset-Werts gelesen wird.

Es verwendet diese Adresse, um mein eingebettetes XML-Manifest aus dem CTFMON-Prozess mithilfe von NtReadVirtualMemory zu lesen, da die Adresse im Manifest.Offset-Feld zu diesem Kontext gehört:

Mein eingebettetes XML-Manifest wird gelesen und in den Ziel- puffer kopiert:

Wechsle zum Kontext des CTFMON-Prozesses und überprüfe, ob mein eingebettetes XML-Manifest an der Manifest.Offset-Adresse liegt, die ich zuvor gesendet habe. In meinem Fall war es 0x7ff93a261470.

Das Lesen des eingebetteten XML-Manifests wird von SxSGenerateActivationContext aufgerufen. Da es in keinem gültigen Eintrag im Cache gefunden wird, versucht es, es mithilfe des eingebetteten Manifests zu „generieren":

Von dort beginnt es, mein eingebettetes XML-Manifest zu parsen.

Wie wird das eingebettete XML-Manifest geparst?

Mit Blick auf den letzten Aufrufstapel habe ich beschlossen, einen Breakpoint auf den Aufruf von RtlReadOutOfProcessMemoryStream zu setzen, um anzuhalten, wenn der Puffer vollständig gefüllt ist.

Nun kann ein Breakpoint auf den Zugriff auf die Zeichenfolge "Tasks" gesetzt werden, um anzuhalten, wenn sie vom Server gelesen oder verarbeitet wird.

Hier ist die tasks-Zeichenfolge innerhalb des eingebetteten XML-Manifests:

Es hält mehrmals beim Lesen und Kopieren an:

Es hält in CharEncoder::wideCharFromUtf8 an, wenn es die Zeichenfolge „tasks" in Wide Char konvertiert:

Dann hält es im XML-Parser an:

Es fährt mit dem Parsen der XML-Attribute fort, wie der Funktionsname parseAttributes vermuten lässt.

Dann hält es in memcpy an, das von ValidateElementAttributes aufgerufen wird:

Ein weiterer Breakpoint kann an der Stelle gesetzt werden, an der kopiert wird:

Es validiert das Sprachattribut, wie der Funktionsname SxspValidateLanguageAttribute vermuten lässt:

Es hält erneut in memcpy an, wird diesmal jedoch von SxspCreateAssemblyIdentityfromIdentityElement aufgerufen:

Erneut hält es in memcpy an, diesmal aufgerufen von SxsInsertAssemblyIdentityAttribute+0xc48:

Dann stoppt es in SxsInsertAssemblyIdentityAttribute:

Es ruft memcpy ein letztes Mal auf, in diesem Fall von BufferedStream::prepairForInput:

Dann liest es hier die Zeichenfolge tasks:

Dann liest es sie von hier aus:

Es liest weiter von hier aus:

Die Namen dieser Funktionen haben meine Aufmerksamkeit erregt. Im Namen ProbingCandidate sind dieselben Wörter enthalten (probing manifests), die auch in der SXS-txt-Protokolldatei verwendet werden.

Es stoppt erneut hier:

Als Nächstes verwendet es GetFileAttributesExW, um zu prüfen, ob die erste in der SXS-txt-Protokolldatei erwähnte Datei existiert**.** Da sie nicht existiert, gibt es 0 zurück.

Die Reihenfolge der Dateiprüfung kann in der Protokolldatei gesehen werden:

Die zweite Datei existiert nicht, weil es sich um den Pfad im Ordner tasks zu tapi32.dll handelt:

Von dort aus scheint es in tasks nach tapi32.manifest zu „suchen“:

Dann erreicht es CProbedAssemblyInformation::ProbeManifestExistence:

Es prüft, ob meine Manifestdatei im Ordner tasks existiert. Da sie existiert, wird kein Fehler zurückgegeben:

Nun, die Datei tapi32.manifest im Ordner „tasks“ wurde gefunden.

Der Server wurde durch mein eingebettetes XML-Manifest mit dem Sprachwert „tasks“ darin gezwungen, in dem Unterordner „tasks“ von system32 nach einer Manifestdatei zu suchen:

Wenn weiterhin Haltepunkte gesetzt werden, um zu sehen, wo der Pfad verwendet wird, stoppt es in EncodingStream::Read, wo der Inhalt der Datei tapi32.manifest gelesen wird.

Als Nächstes wird der Inhalt der Datei TAPI32.manifest geparst. Wenn ein Fehler vorliegt, wird er im SXS-TRACE-Protokoll angezeigt, was die Korrektur erleichtert.

Wenn meine Datei TAPI32.manifest korrekt geparst wird, kehrt es ohne Fehler zu BaseSrvSxsCreateActivationContextFromStructEx zurück. Dadurch wird vermieden, dass eine Meldung mit der Zeichenfolge FAILED gedruckt wird.

In meinem Fall war die Generierung des Aktivierungskontexts unter Verwendung meiner Datei TAPI32.manifest erfolgreich**.**

Danach erreichte ich den Aufruf, bei dem mein Eintrag in den Cache eingefügt wird.

Er wird ohne Probleme übergeben und gibt 0 zurück. Dies ist der korrekte Wert, und der Eintrag mit der manipulierten TAPI32.manifest wird erfolgreich eingefügt.

Mein Eintrag ist im Aktivierungscache enthalten, und der Server antwortet mit OK auf den Aufruf der DLL aus CTFMON.

Die txt-Protokolldatei zeigt den vollständigen Vorgang.

Es liest das eingebettete XML-Manifest. Da dessen Sprache „Tasks“ ist, sucht es im Unterordner „Tasks“ von system32 nach einer neuen Manifestdatei, genauso wie es in dem Unterordner „en-us“ von system32 nach einem Manifest suchen würde, wenn die Sprache auf „en-us“ gesetzt wäre.

Die SXS-txt-Protokolldatei zeigt die Meldung „Activation Context generation succeeded“!

Wie wurde meine gefälschte imm32.dll letztendlich geladen?

Ein Paar Boxhandschuhe Beschreibung automatisch generiert

Nachdem mein ACTX-Eintrag zum Cache hinzugefügt wurde, wird beim Ausführen von tcmsetup.exe tapi32.dll geladen, und es sollte meine Manifestdatei verwenden, um imm32.dll zu laden.

Allerdings ist es nicht so einfach, denn es kann imm32.dll nicht laden, weil es einige Prüfungen gibt, die das Laden verhindern können.

Die Prüfungen erfolgen bei einem späteren Aufruf derselben Funktion BaseSrvSxsCreateActivationContextFromStructEx. Entfernen Sie daher alle Haltepunkte und setzen Sie nur einen darauf.

Von dort aus können wir TCMSETUP.EXE von einer Konsole aus ausführen, auch wenn meine PoC TCMSETUP nach Abschluss der Aktivierungscache-Vergiftung aus MsCtfMonitor.dll ausführt:

Es stoppt viele Male am Haltepunkt. Bei jedem Stopp schauen Sie sich die von r8 referenzierte Struktur an, um festzustellen, ob sie einer Anforderung im Zusammenhang mit tapi32.dll entspricht.

Nach vielen Stopps für andere Module erscheint eine Anforderung für TCMSETUP.exe:

Wir sehen im Aufrufstapel, dass es von dem Moment stammt, in dem der Prozess erstellt wird. Es wird aufgerufen, um zu prüfen, ob es einen Eintrag im Aktivierungscache für TCMSETUP gibt.

Lassen Sie es weiterlaufen, bis der Aufruf für TAPI32.dll eintrifft. Bevor dies geschieht, wird es mehrere Aufrufe für TCMSETUP geben.

Schließlich muss das eintreffende Paket demjenigen sehr ähnlich sein, das zuvor von meiner DLL erstellt wurde, als ich den Eintrag in den Cache eingefügt habe. Jetzt stoppt es jedoch, wenn TCMSETUP versucht, TAPI32.dll zu laden.

An diesem Punkt bemerkte ich einige wichtige Werte in diesem Paket.

Ausgehend vom Anfang der Struktur _BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG 0x40 Bytes nach oben ergibt sich die Struktur TotalMessage. Die PID des Prozesses, der die Anforderung für TAPI32.dll stellt, ist TCMSETUP, da dieser die DLL laden möchte.

Wenn man den Kontext auf den TCMSETUP-Prozess umstellt, ist zu sehen, dass der Wert Manifest.Offset auf ein eingebettetes XML-Manifest zeigt.

Öffnen Sie tapi32.dll in NOTEPAD, um zu sehen, dass das empfangene eingebettete XML-Manifest mit dem in der Datei enthaltenen übereinstimmt.

TCMSETUP liest zuvor die Dateiressource, um das Manifest zu lesen und es als eingebettetes XML-Manifest in das Paket aufzunehmen.

Danach wird der Vergleich erneut von der Funktion BaseSrvActivationContextCacheCompareEntries durchgeführt, die von BaseSrvSxsCreateActivationContextFromStructEx aufgerufen wird. Jetzt ist auch mein Eintrag für tapi32.dll im Cache.

BaseSrvActivationContextCacheCompareEntries wird innerhalb einer Schleife aufgerufen, um die tatsächliche Anforderung mit jedem Eintrag des Activation Context Cache zu vergleichen (so auch mit meinem).

Zunächst vergleicht es beide LastWriteTime-Werte. Da sie gleich sind, fährt es mit dem Vergleich weiterer Werte fort.

Dieser LastWriteTime-Wert ist entscheidend. Wenn die Werte unterschiedlich sind, verwirft es meinen zwischengespeicherten Eintrag und meine imm32.dll wird nicht geladen.

Es fährt fort und stoppt bei der nächsten Prüfung.

Nun prüft es den Wert ResourceName, der in beiden Fällen 0x7c sein muss.

Dann vergleicht es die Sprache des tatsächlichen ACTX-Pakets, die „en-us“ ist, mit der Sprache meines zwischengespeicherten Eintrags. Die Sprache meines zwischengespeicherten Eintrags ist ebenfalls „en-us“.

Mein Paket hat denselben Sprachwert:

Dann vergleicht es die Prozessorarchitektur, die in diesem Fall in beiden Fällen 9 sein wird:

Dann vergleicht es beide Manifest.path.

Ich habe denselben Pfad ohne Hartkodierung erstellt, indem ich den Wert des Systemverzeichnisses verwendet habe:

Dann vergleicht es das AssemblyDirectory, das ebenfalls gleich ist:

Wenn alle Vergleiche korrekt sind, gibt es 0 zurück. Das bedeutet, dass es meinen Eintrag im Aktivierungscache gefunden hat und dieser verwendet wird.

Denken Sie daran, dass der Vergleich beim ersten Senden meiner Anforderung zum Hinzufügen des Eintrags einen Fehler zurückgab, da kein Eintrag für TAPI32.dll im Cache vorhanden war. Da mein Eintrag zuvor hinzugefügt wurde, gibt es jetzt 0 zurück.

Danach vergleicht es die RIDs von TCMSETUP und CTFMON. Da beide RID = 0x3000 haben, wird der Prozess fortgesetzt.

Eine vollständige Erklärung des RID-Patches ist in einem Blog der Zero Day Initiative verfügbar.

Dies ist der Code für diesen Patch:

R15 enthält die RID des Aufrufers TCMSETUP = 0x3000, und der buffer hat die RID=0x3000 des CTFMON-Prozesses gespeichert.

Wie bereits erwähnt, hat Microsoft diesen RID-Check-Patch im Oktober 2022 hinzugefügt.

Nach der Implementierung dieses Patches gilt: Wenn Sie versuchen, den tapi32.dll-Eintrag unter Verwendung derselben MsCtfMonitor.dll aus einem MEDUIM INTEGRITY LEVEL PROCESS (0x2000) zum Cache hinzuzufügen, wird der Eintrag zwar zum Cache hinzugefügt, aber der Versuch schlägt fehl. Das liegt daran, dass die RID des aufrufenden Prozesses 0x2000 gespeichert wird, und wenn Sie versuchen, TCMSETUP mit RID=0x3000 zum Laden von imm32 auszuführen, werden die RIDs verglichen und der Eintrag entfernt.

In diesem hypothetischen Fall hätte R15 die RID=0x3000 des TCMSETUP-Prozesses, der das Laden von tapi32.dll angefordert hat, während die Variable „buffer“ die RID=0x2000 des Prozesses gespeichert hätte, der den Eintrag mit MEDIUM INTEGRITY LEVEL zum Cache hinzugefügt hat.

Auf den neuesten Windows-Versionen funktioniert die Cache-Vergiftung nicht, wenn der Prozess, der das Hinzufügen des Eintrags anfordert, eine niedrigere Integritätsstufe als der ausführende Prozess hat; der Eintrag wird entfernt. Frühere Versionen, die vor diesem Patch veröffentlicht wurden, funktionieren ohne Probleme mit jeder RID.

Zurück zu diesem Fall: Der RID-Check wird bestanden, und beide Prozesse haben dieselbe RID=0x3000. Folglich wird der Eintrag nicht gelöscht, und es wird ohne Fehler fortgefahren.

Der Server sendet die Antwort an TCMSETUP. Wenn es tapi32.dll lädt, verwendet es meinen Eintrag mit der Datei tapi32.manifest, die imm32.dll aus dem Ordner tasks lädt.

Dies ist der vollständige Weg von LoadLibrary bis zu dem Punkt, an dem TCMSETUP beim Laden von tapi32.dll die Anforderung an den Aktivierungscache stellt.

BasepCreateActCtx ist diejenige, die ihre Anforderung an den CSRSS-Server stellt. Es muss versucht werden zu erkennen, wann es schließlich das Modul IMM32.dll lädt.

Betrachtet man kernel32.dll, so ruft sie CsrBasepCreateActCtxCommon auf. Darin befindet sich ein Serveraufruf, der dem aus meiner DLL zum Einfügen meines Cache-Eintrags ähnelt.

Es verwendet dieselbe ApiNumber wie ich.

Bei der Ausführung von TCMSETUP kann dort ein Haltepunkt gesetzt werden, wenn es vom Server zurückkehrt, nachdem meine Datei tapi32.manifest akzeptiert wurde.

Dies ist der gesamte Aufrufstapel, bis der Aufruf an den Server in CsrBasepCreateActCtxCommon erfolgt.

Haltepunkte werden auf die Rückkehr einiger Funktionen des Aufrufstapels gesetzt.

Beim Stoppen kann beobachtet werden, dass imm32.dll aus dem Ordner „tasks“ geladen wurde:

Mit PROCESS MONITOR lässt sich bestätigen, dass TCMSETUP IMM32.dll aus dem Ordner „tasks“ lädt.

Der gerade ausgeführte CMD-Prozess verfügt über HIGH-Rechte.

Außerdem hat es dieselben Rechte wie Administrator.

Mit diesen Rechten können wir nun jedes Programm installieren, das eine Erhöhung auf Administrator erfordert, und in jeden Ordner schreiben. Zum Beispiel das Schreiben in SYSTEM32 oder in einen beliebigen Programm-Installationsordner, wie im folgenden VIDEO-DEMO zu sehen ist.

Hier sind die Rechte vor der Ausnutzung (Integritätsstufe Medium, nicht Administrator):

Und hier sind die Rechte nach der Ausnutzung (Integritätsstufe High, voller Administrator):

An diesem Punkt ist es eine gute Gelegenheit, sich einfach zur SYSTEM-Berechtigung zu erhöhen, indem man eine manipulierte DLL in einen Systemordner ablegt.

Video-Demo und PoC Ein Cartoon eines Fernsehers mit Boxhandschuhen Beschreibung automatisch generiertEine Goldmedaille mit rotem Band Beschreibung automatisch generiert

Sehen Sie sich das Video hier an und das funktionsfähige Proof of Concept finden Sie hier

TL; DR: Kurzbeschreibung der Ausnutzungsschritte

  • Ich habe eine manipulierte ACTX-Nachricht an den CSRSS-Server gesendet.

  • Diese ACTX-Nachricht enthielt ein eingebettetes XML-Manifest mit einem Offset, der darauf zeigte.

  • Als der Server sie empfing, verwendete er diesen Offset, um das eingebettete XML-Manifest aus dem Prozesskontext von CTFMON zu lesen.

  • Das eingebettete XML-Manifest wurde geparst. Wenn es akzeptiert wurde, versuchte es, ein zweites externes Manifest aus einem externen Ordner zu laden.

  • Der zu lesende Ordner hing von dem von mir kontrollierten Sprachfeld im eingebetteten XML-Manifest ab.

  • In meinem Fall hatte das eingebettete XML-Manifest „tasks“ als Sprache. Aus diesem Grund suchte es im Unterverzeichnis „tasks“ von system32 nach einem externen Manifest und fand es.

  • Es parste die von mir erstellte Datei tapi32.manifest und akzeptierte sie, sodass es die externe IMM32.dll aus demselben Ordner „tasks“ laden konnte.

Dank an Nicolas Economou, denn seine Präsentation war der Ausgangspunkt für meine Forschung und die Veröffentlichung dieses Blogbeitrags.

Ricardo Narvaja

Tool herunterladen