Zurück zu den Artikeln

iOS Wi-Fi Demon: Von iOS Format String zu Zero-Click RCE

07 September 2021

Was ist eigentlich mit iOS passiert?

Vielleicht haben Sie den jüngsten Bug in iOS 14.0 bis 14.4 gesehen, der den Wi-Fi-Dienst abstürzen ließ, wenn ein Zugangspunkt auf eine bestimmte Weise benannt wurde. Apple bezeichnete diesen Fehler als Denial of Service für den Wi-Fi-Dienst, aber das Forschungsteam Zecops [1] hat nachgewiesen, dass er genutzt werden kann, um einen RCE, genauer gesagt einen Zero-Click-RCE, zu verursachen. Obwohl in diesem Artikel einige Details der Sicherheitslücke erläutert werden, wollten wir unsere eigenen Nachforschungen anstellen. Wir haben uns ein gutes Bild von der Schwachstelle gemacht und werden dies in diesem Artikel erläutern.

Einrichtung der Debug-Umgebung

In diesem Artikel wird nicht nur erklärt, worum es sich bei der Schwachstelle handelt, sondern Sie können auch eigene Nachforschungen anstellen. Der Einfachheit halber zeigen wir Ihnen, was Sie tun müssen, um den iOS Wi-Fi-Dienst zu debuggen. Die einzigen Voraussetzungen sind ein iPhone (oder vielleicht die Simulation eines solchen) und ein Gerät, auf dem MacOS läuft (ich habe einen Mac mini verwendet).

Vorbereitung des iPhone

Das Wichtigste dabei ist, die iOS-Version des iPhones auf eine anfällige Version zu flashen. Obwohl der Format-String-Bug in den Versionen von iOS 14.0 bis iOS 14.6 nicht behoben ist, existiert das Zero-Click-Formular nur in den Versionen 14.0 bis 14.4, wie das Zecops-Team erklärt. Ich habe meine Untersuchungen mit iOS v14.0 und einem iPhone 7+ durchgeführt. Firmware-Images können leicht online gefunden werden. Nachdem die richtige Firmware auf das iPhone geflasht wurde, musste es mit einem Jailbreak versehen werden, um eine SSH-Verbindung zwischen dem MacOS-Gerät und dem iPhone herzustellen und so die laufenden Prozesse zu debuggen. Für meine Untersuchungen verwendete ich den unc0ver [2] Jailbreak, es dürfte aber auch mit jedem anderen Jailbreak funktionieren. Anschließend installierte ich über Cydia die erforderlichen SSH-Pakete, um SSH auf dem iPhone zum Laufen zu bringen. Die Verbindung kann über das Netzwerk hergestellt werden, aber wir haben das iPhone über USB an den Mac mini angeschlossen und iproxy zur Weiterleitung der erforderlichen Ports verwendet. Wenn Sie sich für diese Technik entscheiden, benötigen Sie einen Port für die SSH-Verbindung und einen Port für die Remote-Debugging-Operation. Aber auch dies ist nicht zwingend erforderlich; SSH kann auch kabellos über das Netzwerk genutzt werden.

Remote-Debugging-Sitzung

Da wir nun meine SSH-Verbindung hatten, mussten wir die Debug-Umgebung einrichten. Hierfür haben wir lldb auf dem Mac mini und debugserver auf dem iPhone verwendet, die mit Cydia installiert werden können. Dann mussten wir nur noch debugserver verwenden und die richtige PID anhängen (der Prozessname ist wifid), um den Listener zu starten. Bitte beachten Sie, dass das iPhone von selbst neu startet, wenn der wifid-Dienst aufgrund des Debugging-Prozesses zu lange im Leerlauf bleibt; ich musste deswegen manchmal den Jailbreaking-Prozess wiederholen. Es blieb nur noch, den Befehl gdb-remote auf lldb auszuführen, um die Debugging-Sitzung zu starten.

Remote Debugging – debugserver und lldb

Analyse der Schwachstelle und der eigentlichen Ursache

Umkehrung des Binärsystems

Mit Hilfe des Artikels von Ghidra und Zecops haben wir den Format-String gefunden, der den Absturz verursacht:

Beim zweiten _objc_msgSend()-Aufruf wird keine Formatierung vorgenommen, was zu einer Schwachstelle des Format-Strings führt. Unter dem Gesichtspunkt der Fehlersuche ist dies die Stelle, an der Sie ansetzen sollten.

Diese Funktion sucht nach den vorhandenen Wi-Fi-Netzwerken in der Umgebung und läuft immer, auch wenn das Telefon gesperrt ist. Das ist der Grund, warum ein RCE keine Interaktion des Benutzers erfordert (Null-Klick), wenn es zustande kommt. Wie Zecops in seinem Artikel erläutert, ist dies ein unüblicher Format-String. Erstens wird das Apple-Format [NSString stringWithFormat:] verwendet und zweitens hat Apple die Unterstützung für „%n“ entfernt, sodass letzteres nicht zum Schreiben in den Speicher verwendet werden kann. Der Dienst ist jedoch in Objective-C geschrieben und wir können „%@“ verwenden, damit die Funktion versucht, ein Objective-C-Objekt zu drucken.

Das Zecops-Team hatte eine großartige Idee, wie man den Stack und damit den Code-Ausführungsfluss kontrollieren kann. Im Grunde handelt es sich dabei um einen Flooding-Angriff [3], bei dem Hunderte von Zugangspunkten gesendet werden. Dadurch erscheinen diese APs auf dem iPhone und möglicherweise auch auf dem Stack. Ich habe diese Technik reproduziert, indem ich airmon-ng verwendet habe, um die Monitorschnittstelle einzurichten, und mdk3 mit einer Liste mehrerer SSIDs, um das Flooding zu starten.

Auslösung des Absturzes

Da es sich um eine Format-String-Schwachstelle handelt, müssen wir nur einen Hotspot mit einer SSID erstellen, die einen Wert wie „%x%x%x...%@“ enthält. Die Länge der SSID ist auf 32 Bytes begrenzt, sodass wir nur die Hälfte davon für unsere Escape-Zeichen verwenden können. Mit XCode lassen sich die Anwendungsprotokolle des iPhone in Echtzeit analysieren. Wir filterten die Protokolle nach dem wifid-Prozess und dem „Crash“-String, um die Protokolle zu finden, die uns interessierten, und erstellten dann eine AP mit dem oben angegebenen Namen.

XCodes Konsole – wifid-Absturzprotokolle

Hier haben wir unseren Absturz mit dem Fehlercode „KERN_INVALID_ADDRESS“. Um zu verstehen, was passiert ist, haben wir gerade meine Debug-Sitzung gestartet. Wie bereits erwähnt, verwendeten wir die Remote-Debug-Sitzung mit dem Debug-Server auf dem iPhone und lldb auf dem Mac mini.

Einrichten eines ausnutzbaren Breakpunkts

Da die Kernel-ASLR (KASLR) aktiv ist, benötigen wir einen Offset, den wir dann auf eine Basisadresse anwenden können, um die Adresse der Anweisung zu berechnen, an der wir unseren Breakpunkt setzen wollen. Um den Offset zu ermitteln, verwendeten wir einfach Ghidra und gingen zurück zu der Anweisung, die die Schwachstelle verursacht hatte. Die Basisadresse für den __TEXT-Abschnitt in Ghidra ist 100000000. Wir mussten nur 1000f7fcc von 100000000 subtrahieren (und erhielten so f7fcc).

Ghidra – Offset des anfälligen Aufrufs ermitteln

Um den Breakpunkt zu setzen, musste lediglich die Debug-Sitzung gestartet werden. Wir verwendeten den wifid-Befehl der Image-Dump-Abschnitte auf lldb, um die Basisadresse des __TEXT-Abschnitts zu ermitteln.

lldb – Ermittlung der Basisadresse des __TEXT-Abschnitts

Dann mussten wir nur noch den Offset zu dieser Adresse hinzufügen und unseren Breakpunkt setzen.

lldb – Einrichten des Breakpunkts

Um etwas Zeit zu sparen, luden wir das wifid-Binary direkt auf den Mac mini herunter, damit wir lldb damit ausführen können. Denn wenn wir den Breakpunkt einmal setzen und sich die Adresse nach dem Neustart des Prozesses ändern sollte, findet lldb die neue Adresse anhand des Offsets selbst. So müssen wir den ganzen Prozess nicht noch einmal wiederholen.

Steuerung des Ausführungsablaufs

Nachdem der Breakpunkt gesetzt war, mussten wir nur noch den Absturz auslösen, um zu sehen, was vor sich ging. Davor führten wir den Beacon-Flooding-Angriff durch, um zu versuchen, Daten auf den Stack zu laden. Zecops hat ein Python-Skript für lldb entwickelt, das automatisch den Stack überprüft, um Spuren der geladenen SSIDs zu finden. Wir schafften es jedoch nicht, das Skript zum Laufen zu bringen, was an meiner lldb-Konfiguration lag. Daher konnte das Problem in der wenigen verfügbaren Zeit nicht behoben werden. Wir versuchten, den Stack manuell zu scannen, aber wir konnten auch keine SSID finden. Statt im Stack suchten wir also im Heap, und fanden dort meine Wi-Fi-Zugangspunkte. Das Laden von Daten auf den Stack/Heap sollte eine Möglichkeit sein, den Codeausführungsablauf durch die Kontrolle von Registern zu steuern. Es funktioniert zwar, aufgrund der anderen Netzwerke in der Umgebung und der zufälligen Anordnung der Netzwerke im Stack/Heap jedoch eher nach dem Zufallsprinzip. Im Artikel von Zecops finden Sie weitere Einzelheiten zum Laden von Daten auf den Stack.


einige APs auf dem Heap gefunden

Das x15-Register konnte von uns jedoch vollständig unter Kontrolle gebracht werden.

lldb – DEADBEEF in x15

Auf dem Screenshot oben ist der Grund für den Absturz leicht erkennbar. Im Grunde versucht das Programm, auf die Adresse zuzugreifen, die 28 Bytes weiter von der in x2 enthaltenen Adresse entfernt ist, und stürzt ab, weil diese Adresse nicht existiert.

Zur Kontrolle von x15 haben wir das Beacon-Spray verwendet und einen weiteren Hotspot mit folgendem Namen erstellt:
FEEBDAED%x%x%x%x%x%x%x%x%x%x%x%@

Die Eingaben, die vor den Escape-Zeichen stehen, werden systematisch in x15 platziert. Es ist der einzige Input, den wir vollständig kontrollieren können. Wir fanden auch heraus, dass das Verschieben der FEEBDAED-Zeichenfolge nach rechts den Wert in x15 so verändert, dass er zu den vorhandenen APs gehörte, die übertragen wurden (nicht durch das Spray, wir beziehen uns auf tatsächliche Wi-Fi-Netzwerke). Wir sind uns immer noch nicht sicher, ob diese Daten vom Stack oder vom Heap gelesen werden.

Es gibt eine Möglichkeit, auch x9 und x10 zu kontrollieren, aber dies ist uns nicht gelungen, da das Spray in diesem Fall den Stack nicht zu beeinflussen schien; wir sahen dies lediglich auf Zecops' Screenshots (siehe unten). Die Kontrolle beider Register scheint die Möglichkeit zu bieten, einen Teil von x2 zu kontrollieren.

Zecops – Registerwerte nach dem Spray + erstellte SSID

Das Forschungsteam von Zecops hat herausgefunden, dass x9 auf das erste Mitglied der Object-Datenstruktur verweist.

Um RCE zu erreichen, muss man ein gültiges Objective-C-Objekt durch das Spray leiten, um x9 zu steuern. Dann wird das Objekt an __objc_msgSend() übergeben und es kann ein beliebiger Code ausgeführt werden. (Im Artikel von Zecops finden Sie das Schema des gewünschten Ausführungsablaufs).

Erreichen von RCE und Entdecken zusätzlicher Schwachstellen (noch nicht aufgedeckt, aber auslösbar)

Wir hätten uns gerne ausführlicher mit dieser Untersuchung befasst, allerdings war die Zeit begrenzt. Es sind noch weitere Umkehrungen und Tests erforderlich, aber wir sind sehr zuversichtlich, dass diese Schwachstelle ausnutzbar ist.

Allerdings nicht durch ROP, da wir im wifid-Binary selbst keine für ROP geeigneten Gadgets finden konnten. JOP wäre erforderlich, um diese Schwachstelle auszunutzen, wenn eine Kontrolle des Ausführungsablaufs tatsächlich möglich ist, und die JOP-Payloads sollten auch Teil des AP-Sprays sein. KASLR ist immer noch ein Problem; nützlich erscheint uns allerdings, dass die Protokolle (verfügbar auf XCode) die AP-Namen ausgeben und beim Versuch, die böswillige SSID auszugeben, die „%x“-Escape-Zeichen durch tatsächliche Adressen ersetzt werden, wodurch ein Speicherleck entsteht. Dies geschieht nur, wenn die böswillige SSID Teil des Sprays und kein einzelner Hotspot ist. Wenn Sie also eine Adresse benötigen, können Sie technisch gesehen mehrere SSIDs erstellen und diese sprayen, um Speicheradressen abzugreifen.

XCode – Speicherleck

Als wir den Format-String-Bug in Ghidra entdeckten, dachten wir, es handele sich um die gleiche Funktion wie im Zecops-Artikel. Als wir jedoch einen Breakpunkt setzten, stürzte wifid ab, bevor der Breakpunkt erreicht wurde.

Dadurch stellten wir fest, dass zwei andere Funktionen anfällig für denselben Formatierungsfehler waren. Als wir das iPhone wieder auf iOS 14.7 aktualisierten (bei dem der Fehler behoben worden war), stellten wir fest, dass alle drei Funktionen (die ursprüngliche und die beiden anderen) gepatcht waren.

Wir sind uns nicht sicher, warum es drei Funktionen gibt, die den AP-Scan durchführen, aber Apple hat alle erfolgreich gepatcht, da sie ebenfalls ausnutzbar waren.


Quellenangaben

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