Zurück zu den Artikeln

Ausnutzung von CVE-2018-5093 in Firefox 56 und 57 – TEIL 2: Erreichen einer Codeausführung

19 September 2022

Kontext

Ziel dieses Projekts ist es, während der Red-Team-Übung oder der Gegner-Simulation einen ersten Zugang zu erhalten, indem durch die Ausnutzung einer Integer-Überlauf-Schwachstelle in Firefox 56/57 eine Codeausführung erreicht wird.
Dieser Write-up ist in zwei Teile gegliedert. Im ersten Teil wird beschrieben, wie sich die Speicherkorruption nutzen lässt, um den Befehlszeiger zu kontrollieren, indem mehrere Funktionen verkettet werden.

In diesem zweiten Artikel geht es darum, wie sich Minderungsmaßnahmen umgehen und beliebige R/W-Primitive nutzen lassen, um unser finales Ziel eines voll funktionsfähigen Exploits zu erreichen.
Derzeit steht kein Write-up oder funktionales Exploit öffentlich zur Verfügung.
Aus diesem Grund wurden zur Entwicklung dieses Exploits das Write-up von ExodusIntel und andere in diesem Artikel genannte Ressourcen 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 und ROP verfügen.

Entwicklung des Exploits

Das erste Problem besteht darin, dass wir zur Auslösung der Codeausführung eine Adresse nutzen müssen, die über Ausführungsberechtigungen verfügt.
Der Heap verfügt aufgrund des DEP-Schutzes allerdings nicht über diese Berechtigungen, und wir sind angesichts des ASLR-Schutzes, der die von uns benötigten Adressen nach dem Zufallsprinzip verteilt, nicht in der Lage, den Code direkt wiederzuverwenden.
In unserem Kontext werden durch den ASLR-Schutz alle Basisadressen willkürlich vergeben, so etwa die Basisadressen der Bibliotheken, der ausführbaren Datei, des Stacks und des Heaps.
Außerdem weisen durch den DEP-Schutz nur einzelne Codeabschnitte der ausführbaren Datei und der Bibliotheken eine Ausführungsflagge auf, doch diese Abschnitte sind leider nicht bearbeitbar, sodass wir erst diese Primitive erhalten müssen, um den DEP-Schutz zu umgehen.
Eine Lösung, die es ermöglicht, beliebigen Code auszuführen, besteht somit darin, zunächst einen Weg zu finden, den ASLR-Schutz zu umgehen, um eine ROP-Kette aufzubauen.
Mittels dieser ROP-Kette wird der DEP-Schutz dann im nächsten Schritt ausgehebelt, indem die Berechtigungen auf dem Heap geändert werden und im Anschluss der Shellcode ausgeführt wird.

Umgehen des ASLR-Schutzes

Eine Möglichkeit, den ASLR-Schutz zu umgehen, ist, ein Speicherleck einer Adresse zu ermitteln, das es uns im Anschluss ermöglicht, Offsets zu berechnen und Funktionsadressen zu finden, die es uns im weiteren Verlauf erlauben, unsere ROP-Kette zu erstellen.

Speicherleck

Auf den ersten Blick lässt sich ein Leck in der Funktion get(), genauer gesagt in der Unterfunktion putNewInfallibleInternal() finden.
Abbildung 1 zeigt, dass das Programm den Wert von EAX in der Tat an eine Adresse verschiebt, die im ESI-Register gespeichert ist.
Eine weitere interessante Sache ist, dass wir sowohl das ESI- als auch das EAX-Register kontrollieren, das eine Objektadresse innerhalb des Heaps enthält.
Mit unserem Heap Spray können wir eine Objektadresse dann so umreferenzieren, dass ESI auf eine Adresse innerhalb des Heaps verweist.

Hier die Details aus Abbildung 1 : Funktion putNewInfallibleInternal

 

Darüber hinaus verweist diese Adresse auf eine Adresse in der xul-Bibliothek. Im Anschluss können wir mittels dieser Adresse die Basisadressen der xul-Bibliothek berechnen und den xul-Code dann in der ROP-Kette verwenden.

Um zur Funktion putNewInfallibleInternal zu gelangen, müssen wir folgende Aufrufkette ausführen:

Hier die Details aus Abbildung 2 : Aufrufkette bis zur Funktion putNewInfallibleInternal

Die Funktion putNewInfallibleInternal wird durch die Funktion putNewInfallible aufgerufen (siehe Abbildung 3). Der entsprechende Assembler-Code ist in Abbildung 4 dargestellt.

Hier die Details aus Abbildung 3 : Funktion putNewInfallible


Hier die Details aus Abbildung 4 : Assembler-Code putNewInfallible

Die Funktion putNewInfallible wird dann durch die Funktion putNew aufgerufen (siehe Abbildung 5). Der entsprechende Assembler-Code ist in Abbildung 6 dargestellt.

Hier die Details aus Abbildung 5 : Funktion putNew


Hier die Details aus Abbildung 6 : Assembler-Code putNew

Und schließlich wird die Funktion putNew durch die Funktion getExportedFunction aufgerufen (siehe Abbildung 7). Der entsprechende Assembler-Code ist in Abbildung 8 dargestellt.

Hier die Details aus Abbildung 7 : Funktion getExportedFunction


Hier die Details aus Abbildung 8 : Assembler-Code getExportedFunction

 

Anpassung des Heap Spray

Wir müssen allerdings einige Bedingungen umgehen, um die Zielfunktion zu erreichen, so wie es beispielsweise in Abbildung 7 ersichtlich ist. Zu diesem Zweck müssen wir dieselbe Methode verwenden wie im ersten Teil des Write-up. Danach haben wir den folgenden Heap Spray erstellt (siehe Abbildung 9), der es uns ermöglicht, die Zielfunktion auszuführen und so die Objektadresse zu erlangen.

Hier die Details aus Abbildung 9 : Heap Spray zur Erlangung der Objektadresse

Dennoch benötigen wir eine Adresse, die auf 0xFFFF0 und 0xFF000 endet, um einige Bedingungen zu erfüllen und das Programm weiterhin ohne Abstürze ausführen können. Aus diesem Grund müssen wir eine andere Art von Heap Spray erstellen, der weniger zuverlässig ist, uns aber diese Art von Adresse verschafft.
Um diesen neuen Heap Spray zu erstellen, haben wir einen String Array entwickelt, der unsere Daten enthält (siehe Abbildung 10).

Hier die Details aus Abbildung 10 : Heap Spray mit Arrays, um eine Adresse zu erlangen, die auf 0xFF000 endet

Da wir die Adresse für den nächsten Schritt in JavaScript abrufen und die xul-Basisadresse durch Berechnung von Offsets abrufen müssen, darf es bei Ausführung dieser get-Funktion nicht zu Abstürzen kommen.
Nach Beendigung der get-Funktion können wir in unserem Heap Spray in Abbildung 11 sehen, dass eine Objektadresse geschrieben wurde. Da die Adresse in unserem Heap Spray liegt, können wir die Objektadresse in JavaScript mittels einer wie nachfolgend in Abbildung 12 beschriebenen Methode abrufen.

Hier die Details aus Abbildung 11 : Heap Spray mit Objektadresse


Hier die Details aus Abbildung 12 : Abruf der Objektadresse mit JavaScript

Nun verfügen wir über eine Objektadresse. Bei Betrachtung des Inhalts der Adresse (siehe Abbildung 13) wird deutlich, dass an der Adresse 0x0858907C eine Adresse mit dem Wert 0x7AE5BBE0 besteht. Nach einer Analyse sind wir zu dem Schluss gekommen, dass es sich um eine statische Objektadresse in der xul-Bibliothek handelt. Können wir diese Adresse also mit JavaScript abrufen, dann sind wir in der Lage, die Basisadresse der xul-Bibliothek zu berechnen.

Hier die Details aus Abbildung 13 : Objekt, das eine interessante Adresse beinhaltet

Da wir die get-Funktion korrekt beendet haben, können wir das von der get-Funktion ausgegebene Objekt einer Variablen zuweisen. Wir nennen diese Variable tabexp und werden sie später im Rahmen der set-Funktion verwenden.
Wie aus Abbildung 14 ersichtlich, weist die set-Funktion dieselbe Schwachstelle auf wie die get-Funktion.

Hier die Details aus Abbildung 14 : Funktion setImpl

Im Gegensatz zur get-Funktion zielt die set-Funktion darauf ab, dem Table-Objekt eine neue Funktion hinzuzufügen.
Wenn wir also als Argumente denselben Index wie bei der get-Funktion und der tabexp-Variable eingeben, wird die Funktion einige interessante Dinge tun, die es uns erlauben, die Adresse des statischen Objekts abzurufen.

Damit wir die set-Funktion ohne Abstürze nutzen können, müssen wir den bestehenden Heap Spray anpassen (Abbildung 15), um die Kontrollen zu bestehen und dann die Adresse zu finden, um den ASLR-Schutz zu umgehen.

Hier die Details aus Abbildung 15 : Anpassung des Heap Spray, um die set-Funktion zu nutzen

Aus Abbildung 16 geht hervor, dass die set-Funktion an der Adresse 0x10101094 in unserem Heap Spray nach einem Wert suchen wird. Aus diesem Grund haben wir die Objektadresse der Adresse 0x10101094 in unserem Heap Spray hinzugefügt (Abbildung 15).
Die Objektadresse wird sich zwar nach jeder Ausführung ändern, aber weiterhin auf dasselbe Objekt verweisen.

Hier die Details aus Abbildung 16 : WINDBG-Ansicht, Abruf der Objektadresse

Darüber hinaus wird das Programm die statische Objektadresse an der Objektadresse plus 4 suchen (Abbildung 17). Allerdings befindet sich die statische Objektadresse an der Objektadresse plus 0xC. Daher ändern wir die Objektadresse, bevor wir sie dem Heap Spray hinzufügen, indem wir die letzte Ziffer der Adresse um 8 anstatt um 0 ändern. Danach wird das Programm in der Lage sein, die statische Objektadresse abzurufen (siehe Abbildung 17).

Hier die Details aus Abbildung 17 : WINDBG-Ansicht, Abruf der statischen Objektadresse

Danach wird die set-Funktion die statische Objektadresse unserem Heap Spray hinzufügen (Abbildung 18).

Hier die Details aus Abbildung 18 : WINDBG-Ansicht, Hinzufügen der statischen Objektadresse in den Heap Spray

Nun rufen wir die Adresse im Heap Spray ab und berechnen dann die Basisadresse der xul-Bibliothek (siehe Abbildung 19).

Hier die Details aus Abbildung 19 : Berechnung der Basisadresse der xul-Bibliothek

Somit sind wir in der Lage, die xul-Basisadresse herauszufinden, die es uns ermöglicht, alle Funktionen in der xul-Bibliothek zu verwenden und endlich den ASLR-Schutz auszuhebeln.

Umgehung des DEP-Schutzes

Eine Möglichkeit, den DEP-Schutz zu umgehen, besteht darin, eine ROP-Kette zu erstellen, um die Funktion VirtualProtect aufzurufen, mit der die Berechtigungsflagge für den Heap geändert werden soll. Danach können wir mittels unseres Heap Spray einen Shellcode ausführen.
Bevor wir aber eine ROP-Kette erstellen können, müssen wir den Stack und damit den ESP-Zeiger kontrollieren. Aus diesem Grund müssen wir Kontrolle über den EIP-Zeiger erlangen und diesen dann benutzen, um an eine bestimmte Stelle in der xul-Bibliothek zu springen, wodurch wir dann in der Lage sind, einen Assembler-Code auszuführen, um den ESP-Zeiger zu kontrollieren. Zu diesem Zweck haben wir das Tool ROPgadget verwendet, um die Gagdets der xul-Bibliothek zu generieren. Damit sind wir in der Lage, eine Adresse zu finden, die dem Assembler-Code entspricht, den wir ausführen wollen.

Stack Pivot

In Teil 1 dieses Write-ups haben wir eine Möglichkeit aufgezeigt, das EIP-Register durch Verkettung von Funktionsaufrufen zu kontrollieren. Wir verwenden diese Technik hier, um einige „Code-Wiederverwendungs-Zauber“ auszuführen, mit denen wir einen Stack Pivot durchführen und Kontrolle über den ESG-Zeiger erlangen.

Wir haben ein Gadget gefunden, das den Wert von EAX – ein Register, das wir kontrollieren können, – in den ESP-Zeiger verschiebt (Abbildung 20). Diese Adresse weist einen Offset von 0x17E946C auf, und die Basisadresse der xul-Bibliothek ist 0x78870000.

Hier die Details aus Abbildung 20 : Assembler-Code zur Kontrolle des ESP-Zeigers

Um an diese Stelle zu springen, müssen wir nur den EIP-Zeiger mit dem entsprechenden Wert überschreiben. Danach können wir damit beginnen, die ROP-Kette zu erstellen, und schlussendlich den DEP-Schutz umgehen.
Um sicherzustellen, dass der Stack über genügend Platz verfügt, nutzen wir ein Gadget (Abbildung 21), um den ESP-Zeiger um 0x101010E0 zu ändern.

Hier die Details aus Abbildung 21 : ROP-Gadget zur Änderung des ESP-Zeigers

ROP-Kette

Durch den Einsatz von GHIDRA haben wir herausgefunden, wo die Funktion VirtualProtect in der xul-Bibliothek aufgerufen wird.
Das heißt, wir haben eine Adresse gefunden, an der das Programm die Argumente auf den Stack schreibt und dann die Funktion VirtualProtect aufruft (Abbildung 22).

Hier die Details aus Abbildung 22 : Aufruf der Funktion VirtualProtect in der xul-Bibliothek

Die Funktion VirtualProtect benötigt vier Argumente: lpAddress, dwSize, flNewProtect, lpflOldProtect.
Das erste Argument (EDI), d. h. lpAddress, entspricht der Ausgangsadresse. Die Funktion ändert den Schutz von dieser Adresse bis zu dieser Adresse plus dwSize (ECX).
Das zweite Argument gibt die Größe der Bytes an, die die Funktion ändern soll. Das Argument flNewProtect (EAX) ist die Schutzflagge, und das letzte Argument (EDX) stellt eine Adresse mit Schreibrechten dar, denn das Programm wird den alten Schutz an dieser Adresse schreiben.

Mithilfe des ersten Gadgets geben wir ECX den Wert 0x10101010 (Abbildung 23), um den EDX-Wert in 0x10101010 zu ändern (Abbildung 24).

Hier die Details aus Abbildung 23 : ROP-Gadget 1, um ECX den Wert 0x10101010 zu verleihen


Hier die Details aus Abbildung 24 : ROP-Gadget 2, um EDX den Wert 0x10101010 und ESI den Wert 0x641FF000 zu verleihen

Mit dem in Abbildung 24 dargestellten Gadget setzen wir lpflOldProtect auf 0x10101010. Danach setzen wir ESI auf 0x641FF000, um EDI diesen Wert zu verleihen (Abbildung 25).
Das EDI-Register in Abbildung 25 wird dadurch 0x641FF000 enthalten. Hierbei handelt es sich um die lpAddress. Wir nutzen dieses Gadget, um ECX gleichzeitig auf 0x1000 zu setzen, was dwSize entspricht.

Hier die Details aus Abbildung 25 : ROP-Gadget 3, um EDI den Wert 0x641FF000 und ECX den Wert 0x1000 zu verleihen

Die beiden nächsten Gadgets (Abbildung 26 und 27) werden verwendet, um EAX auf 0x40 (PAGE_EXECUTE_READWRITE) zu setzen, da dies der Flagge flNewProtect entspricht. Dadurch erhalten wir Ausführungs-, Lese- und Schreibrechte.

Hier die Details aus Abbildung 26 : ROP-Gadget 4, um EAX den Wert 0x4000 zu verleihen


Hier die Details aus Abbildung 27 : ROP-Gadget 5, um EAX den Wert 0x40 und EBP den Wert 0x1010111C zu verleihen

Zusammenfassend lässt sich sagen, dass all diese Gadgets verwendet werden, um die Argumente der Funktion VirtualProtect festzulegen und diese dann aufzurufen.
Nun ergänzen wir den Heap Spray um unsere ROP-Kette wie in Abbildung 28 veranschaulicht.
Danach beenden wir die ROP-Kette über die Shellcode-Adresse, um zu springen, und führen sie aus – und geschafft. ?

Hier die Details aus Abbildung 28 : Spray mit ROP-Kette

Tatsächlich haben wir den Shellcode bereits zuvor unserem Heap Spray hinzugefügt, um ihn an der Adresse 0x641FF200 parat zu haben (Abbildung 29).

Hier die Details aus Abbildung 29 : Shellcode dem endgültigen Heap Spray hinzugefügt

 

Nächste Schritte

Der im Rahmen des Exploits genutzte Shellcode ist ein Reverse Shell im Format „js_le“ (vielen Dank an msfvenom ?).
Wie in Abbildung 30 zu sehen ist, funktionieren der Exploit und die Rückverbindung zum Host, doch die Verbindung wird rasch wieder geschlossen.

Hier die Details aus Abbildung 30 : Verbindung geschlossen

In der Realität kann der Shellcode aufgrund der Sandbox in Firefox, die die Prozesserstellung beschränkt, nicht ordnungsgemäß ausgeführt werden. Unser Bestreben ist es dann, diese Beschränkungen mittels einiger SBX-Primitiven zu umgehen …

Hier die Details aus Abbildung 31 : pwned

Schlussendlich können wir nun beliebigen Code ausführen und verfügen über einen funktionierenden Exploit – zu diesem Zeitpunkt ohne SBX, da dies im Menü „about:config“ deaktiviert werden muss.
Um diesen Exploit fertig zu stellen und in allen Fällen einen ersten Zugang mit Firefox 57 zu erhalten, müssen wir diesen mit einer anderen Schwachstelle verketten, die es uns ermöglicht, der Sandbox wie bei der Schwachstelle CVE-2022-1529 zu entkommen.

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