
Tutorial zu CVE-2022-37969 mit Fokus auf die Methodik der Kernel-Exploitation, nicht die internen Ursachen der CVE
Dies wurde erstellt, um allgemeine Aspekte der Windows-Ausnutzung zu klären. Es erklärt grundlegende Konzepte, die auf CVE-2022-37969 angewendet werden. Das Endergebnis ist ein funktionierender PoC. Es klärt nicht jeden Aspekt der CVE, liefert aber wiederverwendbare Codefragmente und erklärt Mechanismen, die in vielen allgemeinen Exploits zu finden sind.
Der Zielbenutzer wäre ein Anfänger im Reverse Engineering, ein Exploit-Entwickler, der einen funktionierenden Proof-of-Concept-Quellcode sucht, um grundlegende Windows-Interna zu testen und zu verstehen. Es bietet einen Referenzpunkt für weiterführendes Lernen.
Anforderungen: Grundlegende Kernel-Debugging-Fähigkeiten, grundlegendes Reverse Engineering, grundlegende Windows-Interna, C/C++-Programmierkenntnisse
Ein Programm ist ein Stück Code, das auf einer Maschine läuft. Im Allgemeinen empfängt ein Programm Daten (Eingabe), führt Berechnungen mit den Eingaben durch und erzeugt Daten (Ausgabe). Die meisten Programme werden von Menschen geschrieben und haben daher Fehler. Ein Fehler wird durch Quellcode erzeugt, der nicht korrekt geschrieben wurde (der Programmierer wollte etwas mit der Eingabe tun, der resultierende Code unterschied sich vom beabsichtigten Ergebnis). Die meisten Fehler werden vor der Veröffentlichung des Produkts behoben, aber einige bleiben bestehen. Dies liegt daran, dass es verschiedene Arten von Fehlern gibt, einige schwieriger zu erkennen als andere.
Windows ist ein Computerprogramm, wurde von Menschen geschrieben und hat daher Fehler. Warum ist das wichtig? Weil Windows-Systeme Programme ausführen können, die sensible Daten wie Bankkonten, Gesundheitsdatenbanken und andere verarbeiten. Einige Fehler können verwendet werden, um illegal auf geschützte Daten zuzugreifen (dies ist ein guter Anwendungsfall für einen Exploit).
Es gibt viele Arten von Fehlern, einige nützlich, einige nicht. Im Allgemeinen werden Fehler durch Eingaben in das Programm erzeugt, die in Verbindung mit den falsch geschriebenen Codezeilen eine fehlerhafte Ausgabe oder ein fehlerhaftes Verhalten des Programms erzeugen. Das Auffinden dieser Eingabe ist die Aufgabe des Sicherheitsspezialisten (oder Hackers). Der nächste Schritt besteht darin, die resultierende fehlerhafte Ausgabe/das fehlerhafte Verhalten zu bewerten und die Frage zu beantworten: "Kann es auf nützliche Weise verwendet werden?" Hier werden Fehler in verschiedene Kategorien eingeteilt. Zum Beispiel kann ein Fehler ein Verhalten erzeugen, das einige Datenstrukturen beschädigt und den Zielcomputer zum Neustarten zwingt. Sein Nutzen ist begrenzt. Ein Fehler kann dazu führen, dass die Eingabe in einen Speicherbereich geschrieben wird, der die Zugriffsberechtigungen für geschützte Dateien steuert. Diese Art von Fehler ist nützlicher.
Aus der Menge aller möglichen Fehler sucht der Hacker also die Teilmenge, die für seinen Zweck am nützlichsten ist. Allgemein gesprochen lautet das Problem: "Kann ich dem Zielprogramm eine speziell gestaltete Eingabe geben, sodass ich das System nicht beschädige, aber meine Zugriffsebene erhöhen und profitieren kann?"
Nach dieser nicht-technischen Einführung kann der Umfang des Tutoriums formuliert werden: Können wir ein Windows-Programm finden, das fehlerhafte Eingaben akzeptiert und infolge falschen Entwicklercodes unsere Berechtigungen illegal von einem normalen Benutzer auf Administrator erhöhen kann?
Zielprogramm: Windows CLFS (Common Log File System Driver)
Exploit-Name: CVE-2022-37969
Typ: Lokale Privilegieneskalation
VERWUNDBARE ISO ZUM HERUNTERLADEN: Hier herunterladen
Der Windows-Adressraum ist grob unterteilt in Benutzermodus (Ausführung allgemeiner Programme) und Kernel-Modus (Betriebssystem selbst und Hardware-Komponenten-Software → Treiber). Ein normaler Benutzer darf nicht auf den Kernel-Modus zugreifen, aber es gibt Mechanismen, durch die Programme von normalen Benutzern auf Teile des Kernel-Codes zugreifen können (Systemaufrufe, Treiberprozeduren). Warum brauchen wir Zugriff? Um mit dem Betriebssystem auf sichere und kontrollierte Weise zu interagieren, die von den OS-Designern bereitgestellt wird.
Einige Treiber verwenden vom Benutzer bereitgestellte Dateneingaben, um auf Kernel-Datenstrukturen zu operieren. Wenn die Eingabe einen Fehler erzeugt, könnte der Kernel beschädigt werden. Ein Fall ist der Common Log File System Driver. Durch die Verwendung spezieller Eingaben können wir den Treiber zwingen, Kernel-Datenstrukturen zu verändern, die die Berechtigungsstufe für den Benutzer halten, und normaler Benutzer durch Administrator zu überschreiben.
Was muss geändert werden, um die Berechtigung auf Admin zu erhöhen?
Wir beginnen mit dem Endziel im Kopf. Windows speichert in einer Kernel-Datenstruktur namens _EPROCESS Informationen für jeden auf dem System laufenden Prozess. Beispiel für _Eprocess
Ein wichtiges Feld ist struct _EX_FAST_REF Token. Dies ist eine weitere Datenstruktur, die weiter auf Daten verweist, die die Berechtigungsstufe des jeweiligen Prozesses referenzieren. Im folgenden Bild hat der Systemprozess ein System-Token und der Explorer-Prozess ein normales Benutzer-Token.

Um die Berechtigung von Explorer.exe zu erhöhen, müssten wir also den Wert aus dem _EPROCESS-->Token von System in das _EPROCESS-->Token von Explorer kopieren. Wir werden etwas Ähnliches erreichen, indem wir das System-Token in das Token unseres eigenen Programms kopieren und eine Eingabeaufforderung aus dem erhöhten Prozess starten (Kindprozesse erben das Token des Vaterprozesses).
Um diese Aktionen durchzuführen, benötigen wir Mechanismen für:
Einführung: Die Natur von Windows im Laufe der Jahre: Mit der Entdeckung neuer Schwachstellen musste Windows gepatcht werden, um sie zu entschärfen. Auch mit dem Aufkommen neuer Technologien musste Windows aktualisiert werden, um wettbewerbsfähig zu bleiben. Eine entscheidende Anforderung war die Abwärtskompatibilität mit früheren Versionen. Und manchmal wurde Sicherheit durch Unklarheit erreicht. Datenstrukturen, Funktionsdefinitionen wurden aus Handbüchern entfernt, aber die Funktionalität blieb erhalten. Durch Reverse Engineering konnten Forscher diese Funktionalitäten für verschiedene Zwecke nutzen.
Zum Auffinden der Kernel-Adresse von _EPROCESS verwenden wir eine undokumentierte Funktion: NtQuerySystemInformation (siehe Link für Parameter). Durch die Verwendung des Parameters SystemInformationClass können wir angeben, welche Art von Informationen wir abrufen möchten. Wir werden allgemeine Prozessinformationen abrufen, indem wir den Wert SystemExtendedHandleInformation angeben (#define SystemExtendedHandleInformation 0x40).
Ein Nachteil der Verwendung von NtQuerySystemInformation ist, dass wir nicht im Voraus wissen, welche Länge die zurückgegebenen Daten haben werden, aber NtQuerySystemInformation hat einen Mechanismus, der hilft. Wenn es mit einem falsch dimensionierten Array für die erforderlichen Daten aufgerufen wird, gibt es einen Fehler zurück und die korrekte Datengröße, die hätte angefordert werden sollen. Dies kann verwendet werden, um Prozessinformationen korrekt zu lesen, wie folgt: