Zurück zu den Artikeln

Ausnutzung von CVE-2018-5093 in Firefox 56 und 57 – TEIL 1: Steuerung des Befehlszeigers

04 Juli 2022

Kontext

Ziel dieses Projekts ist es, während der Red-Team-Übung einen ersten Zugang zu erhalten, indem durch die Ausnutzung einer Sicherheitslücke eine Codeausführung erreicht wird.

Dieser erste Artikel beschreibt die ersten Schritte der Entwicklung des Exploits bezüglich der Schwachstelle CVE-2018-5093 und erklärt, wie man die Integer-Unterlauf-Schwachstelle von Firefox 56 und 57 ausnutzt, um den Befehlszeiger beim Öffnen einer speziell gestalteten Webseite zu steuern.
Die Entwicklung des Proof of Concept erfolgte unter Windows 10 in der Versionsumgebung 21H2 und mit Hilfe eines WINDBG-Debuggers.

Bisher wurde noch kein einziger Artikel mit einem funktionierenden Exploit veröffentlicht. Aus diesem Grund wurde zur Entwicklung dieses Exploits das Write-up von ExodusIntel genutzt.

Um diesen Artikel zu verstehen, müssen Sie über einige Kenntnisse in Bezug auf Assembler mit der Verwendung des Stacks und des Heaps sowie einige bekannte Techniken bei Browser Exploits wie Heap Spraying verfügen.

Analyse der Schwachstelle

Die Schwachstelle wurde im WebAssembly-Code in der xul.dll-Bibliothek gefunden und 2018 an Mozilla gemeldet. Seit 2020 ist diese Sicherheitslücke in Firefox 58 und 59 behoben.

In Abbildung 1 sehen Sie als Proof of Concept, wie Sie die Schwachstelle einfach auslösen können.

Figure 1 : Proof of concept

Diese Schwachstelle wird ausgelöst, wenn die get()-Funktion des Table-Objekts aufgerufen wird. Wenn die get()-Funktion aufgerufen wird, wird ein Aufruf der Funktion getImpl() ausgeführt. Allerdings beinhaltet die Funktion getImpl() eine Überprüfung des Index, der im Argument der Funktion get() eingegeben wurde. Die Funktion ToNonWrappingUint32() prüft, ob der Index zwischen 0 und table.length() – 1 liegt, wie in Abbildung 2. dargestellt.

Figure 2 : Unterlauf-Schwachstelle

Auslösen des Fehlers

Wie im obigen Screenshot dargestellt, lässt sich bei der Erstellung des Table-Objekts eine Tabellengröße von 0 mit der Anfangskomponente in Abbildung 1 angeben, sodass table.length() gleich 0 sein kann. Wenn table.length() gleich 0 ist, liegt der Wert table.length() – 1 bei -1, aber die Funktion ToNonWrappingUint32() hat den Argument-Typ „unsigned int 32“, also wird der Wert -1 zum Maximalwert von unsigned int 32. Dies ist ein Unterlauf, daher kann die Funktion den Wert des Index nicht überprüfen, und alle in 32 Bit kodierten Zahlen können als Index verwendet werden.

Ein signierter Integer mit 8 Bits, der gleich -1 ist, wird wie folgt binär dargestellt: 1111 1110.

Das Programm interpretiert dies also als den unsignierten Integer 254, was dem auf 8 Bit – 1 kodierten Maximalwert entspricht.

Umkehrung der betroffenen Komponente

Nach der Analyse mit GHIDRA ist der Assembler-Code, mit dem die Schwachstelle ausgenutzt werden kann, in Abbildung 3 zu sehen, da es sich um die Zeile handelt, die es erlaubt, das Objekt am Positionsindex abzurufen.
 

Figure 3 : Anfälliger Assembler-Code

Nach dieser Zeile können wir also einige Register kontrollieren und uns bewegen, wohin wir wollen. Aus diesem Grund sollten wir in der Lage sein, das EIP/RIP-Register zu kontrollieren, wenn wir einen Assembler-Befehl ausführen können, der einen Aufruf an ein von uns gesteuertes Register vornimmt.

Einige Funktionen verfügen über eine Anweisung, die es ermöglicht, ein Register aufzurufen. Nach einer tiefgreifenden Analyse haben wir herausgefunden, dass sich dieser Befehl in Funktion FUN_107bc8d6 befindet (siehe Abbildung 4).
 

Figure 4 : Zielfunktion

Über die Kontrolle einiger Register muss eine Kette von Aufrufen zum Auslösen der Funktion gefunden werden. Wir haben einen Weg gefunden, andere Funktionen zu verketten, wie hier dargestellt:

Figure 5 : Aufruf der Zielfunktion – unsere Kette

Steuerung des Befehlszeigers

Bei der Ausführung des JavaScript-Codes, der in Abbildung 1 als Proof of Concept dargestellt ist, sehen wir, dass das Programm versucht, ein Objekt an der Position 0x2000000 des Table-Objekts abzurufen. Mittels WINDBG können wir sehen, was passiert (siehe Abbildung 6).

Figure 6 : Zugriffsverletzung

In dieser Abbildung möchte das Programm ein Objekt an der Adresse 0x169a40b0 abrufen, das jedoch nicht existiert, weil die Position größer ist als die zugewiesene Tabellengröße.

Präzises Heap Spraying

Nun können wir durch einen präzisen Heap Spray eine gültige Adresse, die wir kontrollieren können, an die Stelle setzen, auf die zugegriffen wird, damit wir unser gefälschtes Objekt verwenden und einige nützliche Register steuern können.

Die präzise Heap-Spray-Payload wird auf der Grundlage des Artikels von Corelan[4] erstellt, in dem diese Technik demonstriert und gezeigt wird, wie man durch präzises Heap Spraying die unten dargestellte Payload erreicht.

Figure 7 : Präzise Heap-Spray-Payload

Mit dieser Technik werden wir den Heap mit der Adresse 0x10101010 sprayen. Wenn das Programm dann die Adresse des Objekts übernimmt, erhält es den Wert 0x10101010 (siehe Abbildung 8).

Figure 8 : Beschädigte Adresse

Mit dem präzisen Heap Spray enthält die Adresse 0x10101010 nur Nullbytes bis zur Adresse 0x10101090, wie in Abbildung 9 beabsichtigt und in Abbildung 10 dargestellt.

Figure 9 : Präziser Heap Spray mit Nullbytes an der Adresse 0x10101008 bis zur Adresse 0x101010A0

Figure 10 : Inhalt im Heap

Verkettung von Funktionsaufrufen

Nun wollen wir das Assembler-Programm bis zum Aufruf der Funktion FUN_1234294b ausführen.

Zu diesem Zweck müssen wir einige Adressen im präzisen Heap Spray modifizieren, um alle Kontrollen zu umgehen, die prüfen, ob das abgerufene Objekt ein gutes Format aufweist, d. h. ob das Objekt den in der Funktion getImpl() (siehe Abbildung 2) angeforderten Objekttyp hat, bevor der Aufruf der Zielfunktion erfolgt.

Um jedoch den Inhalt und den Ort der Daten zu identifizieren, werden wir Schritt für Schritt die Daten im Heap Spray mit WINDBG verändern, bis der Ausführungsfluss die Zielfunktion erreicht.

Wenn das Programm mit dem Debugger ausgeführt wird, kommt es zu einer Zugriffsverletzung (Abbildung 11), weil das Programm auf 0x00000008 zugreifen will, diese Adresse aber nicht existiert.

Figure 11 : Zugriffsverletzung

Das Programm versucht, auf diese Adresse zuzugreifen, weil an 0x10101018 0x00000000 vorhanden ist.

Figure 12 : ESI-Wert betroffen

Aus diesem Grund ändern wir den Heap Spray wie in Abbildung 13 dargestellt, um eine gute Adresse bei 0x10101018 zu haben, die wir kontrollieren können. Hier können wir die Adresse 0x10101040 wählen.

Figure 13 : Präziser Heap Spray mit einer Änderung

Anhand dieser Vorgehensweise nehmen wir weitere Änderungen am Heap Spray vor, um den Assembler-Code auszuführen, bis die Funktion FUN_1234294b aufgerufen wird.
So sieht unsere Heap-Spray-Payload jetzt aus:

Figure 14 : Präziser Heap Spray mit einigen Änderungen zur Umgehung aller Kontrollen

Nun haben wir Zugriff auf die Funktion FUN_1234294b und wollen die Funktion FUN_104e3000 aufrufen:

Figure 15 : Aufruf von FUN_104e3000

Leider springt das Programm, wie in Abbildung 17 zu sehen ist, beim schrittweisen Debuggen mit WINDBG zu 0x7a682a35, weil ESI gleich 0x00000000 ist. Springt das Programm aber zu dieser Adresse, führt es eine POP RET-Anweisung aus (Abbildung 16), wohingegen wir die Anweisungen in LAB_123429b2 in derselben Funktion ausführen wollen, d. h. wir wollen diesen Sprung vermeiden.

Figure 16 : LAB_123429b2-Funktion mit POP RET-Anweisungen

Figure 17 : Sprung in LAB_12342A35

Daher ändern wir den Wert an der Adresse 0x10101044, damit sich das ESI-Register von 0x00000000 unterscheidet, sodass verhindert wird, dass der Ausführungsfluss ungewollt springt.

Wie in Abbildung 18 zu sehen, möchte das Programm auf die Adresse 0x101ffff0 zugreifen, also müssen wir den Heap Spray wie in Abbildung 19 anpassen, um dies zu ermöglichen und den Wert 0x10101010 an 0x101ffff0 zu haben.
 

Figure 18 : Zugriff auf die Adresse 0x101ffff0

 

Figure 19 : Heap-Spray-Anpassung zur Kontrolle der 0x101ffff0-Adresse

 

Wenn wir nun den Heap mit unserem bevorzugten Debugger inspizieren, erhalten wir die im folgenden Screenshot dargestellten Daten, mit denen wir überprüfen können, ob unser Heap Spray korrekt repariert ist, da 101010101111111111 am Ende der Payload des präzisen Heap Sprays hinzugefügt wurde.

Figure 20 : Inhalt des Heap

Wir haben Zugriff auf die Funktion FUN_104e3000 und dann wird die Funktion FUN_104e33c0 aufgerufen – unsere Kette funktioniert nun!

Schließlich wird die Zielfunktion FUN_107bc8d6 aufgerufen und eine Zugriffsverletzung ausgelöst
 

Figure 21 : Zugriffsverletzung

Die Ursache für die obige Zugriffsverletzung wird dadurch ausgelöst, dass das Programm versucht, eine Funktion an der Adresse 0x00000010 aufzurufen.

Umsetzung der Steuerung des Befehlszeigers

Zum Abschluss muss diese Adresse durch eine andere ersetzt werden, die wir durch Anpassung der Payload kontrollieren können.
Für diese letzte Änderung (wirklich die letzte!) müssen wir den Adresswert 0x10101068 anpassen, da das EAX-Register an dieser Adresse enthält, was wir brauchen.
In Abbildung 22 ändern wir diesen Wert um 0x10101080, da wir wissen, dass 0x10101080 + 0x10 = 0x10101090 (EAX+0x10 Aufruf) ist, und haben 0x41414141 an dieser Adresse hinzugefügt, um den Wert 0x41414141 in unserem Befehlszeiger-Register zu erhalten.

Figure 22 : Endgültige präzise Heap-Spray-Payload

Abbildung 23 zeigt, dass der Befehlszeiger jetzt 0x41414141 entspricht, wir haben also erfolgreich die EIP mit unserem Wert 0x41414141 überschrieben.

Figure 23 : Steuerung des Befehlszeigers

Aus technischer Sicht können wir nun ausführen, was wir wollen, indem wir den Ausführungsfluss kontrollieren. Da es sich jedoch um eine reale Anwendung handelt, implementiert das Programm einige Schutzmechanismen wie ASLR, DEP/NX-Schutz usw.

Im zweiten Teil dieses Artikels wird erklärt, wie diese Anweisungen umgangen werden können, um eine Codeausführung zu erreichen.

Unsere Experten beantworten Ihre Fragen

Sie haben Fragen zu einem der Artikel? Sie brauchen Beratung, um die richtige Lösung für Ihre ICT-Probleme zu finden?

Weitere Artikel aus der Kategorie Sicherheit

DDoS-Angriffe in Luxemburg im Jahr 2024

Erfahren Sie mehr über die Statistiken zu DDoS-Angriffen, die POST Cyberforce im Jahr 2024 in Luxemburg entdeckt hat.

Artikel lesen

Veröffentlicht am

31 März 2024

DDoS-Angriffe in Luxemburg im Jahr 2023

Erfahren Sie mehr über die Statistiken zu DDoS-Angriffen, die POST Cyberforce im Jahr 2023 in Luxemburg entdeckt hat.

Artikel lesen

Veröffentlicht am

15 Februar 2023

DDoS-Angriffe in Luxemburg im Jahr 2022

Erfahren Sie mehr über die Statistiken zu DDoS-Angriffen, die POST Cyberforce im Jahr 2022 in Luxemburg entdeckt hat.

Artikel lesen

Veröffentlicht am

11 Oktober 2022