
Dies ist ein Exploit für CVE-2017-7047, funktioniert auf 10.3.2 und darunter.
triple_fetch - ianbeer [https://bugs.chromium.org/p/project-zero/issues/detail?id=1247]
Dies ist ein Exploit für CVE-2017-7047, einen Logikfehler in libxpc, der es bösartigen Nachrichtensendern erlaubte, xpc_data-Objekte zu senden, die von Shared Memory unterstützt wurden. Konsumenten von XPC-Nachrichten schienen nicht zu erwarten, dass die zugrunde liegenden Puffer von xpc_data-Objekten vom Sender modifiziert werden konnten, während sie vom Empfänger verarbeitet wurden.
Dieses Projekt nutzt CVE-2017-7047 aus, um einen Remote-lldb-Debugserver-Stub als Proof of Concept zu bauen, der in der Lage ist, sich an alle Userspace-Prozesse auf iOS anzuhängen und deren Remote-Debugging zu ermöglichen.
Dies ist ein allgemeiner Überblick über den Exploit; eine ausführliche Dokumentation könnte zu einem späteren Zeitpunkt folgen. Weitere Details findest du vorerst im Code :)
Teil I
Der Exploit zielt auf NSXPC, eine Objective-C-Implementierung von Remote Procedure Calls, die von vielen iOS-Diensten verwendet wird (1). Eine NSXPC-Nachricht besteht aus einem bplist16-serialisierten Objekt innerhalb eines xpc-serialisierten xpc_data-Objekts innerhalb einer Mach-Nachricht.
Unter anderem enthält das bplist16-Objekt einen Objective-C-Typcodierungs-String (2), der von der Funktion ___NSMS1 in CoreFoundation geparst wird. Diese Funktion erwartet nicht, dass sich der Inhalt des von ihr geparsten Strings ändert, und dieser Exploit nutzt das aus, um eine Heap-Overflow-Primitive zu konstruieren, indem er ausnutzt, dass ein bestimmter Teil des Strings dreimal aus dem Speicher abgerufen wird. Durch Umschalten zwischen drei verschiedenen, sorgfältig gewählten Werten können wir aus einer gewählten Heap-Allokationsgröße mit beliebigen Bytes überlaufen.
minibplist16.c enthält eine minimale Implementierung der bplist-Serialisierung und eine Diskussion darüber, wie sie funktioniert.
Die äußere XPC-Nachricht enthält das Heap-Grooming. Sie verwendet eine benutzerdefinierte Implementierung des XPC-Serialisierungsprotokolls, um den Heap zu groomen, indem XPC-Wörterbücher mit kollidierenden Schlüsseln erstellt werden, um Allokations- und Free-Primitives aufzubauen. Die äußere XPC-Nachricht enthält außerdem ein Heap-Spray (unter Verwendung mehrerer Kopien eines Shared-Memory-Objekts, um den Speicherverbrauch niedrig zu halten) und ein Mach-Port-Send-Right-Name-Spray.
Der Overflow zeigt den isa-Klassenzeiger eines Objective-C-Objekts auf ein heap-gespraytes Fake-Objekt, sodass bei einem Methodenaufruf auf dieses Fake-Objekt der Stack auf einen kleinen ROP-Stack umgelenkt wird. Der ROP probiert die gesprayten Mach-Port-Send-Right-Namen per Brute-Force durch, indem er versucht, das Send Right des Ziels auf seinen eigenen Task-Port an jeden der Kandidaten-Send-Right-Namen zu senden. Der Exploit lauscht auf allen gesprayten Ports, und wenn der Exploit erfolgreich ist, empfängt er ein Send Right auf den Task-Port des Ziels, woraufhin er die volle Kontrolle über den Ziel-Task hat.
Zwischenspiel
Der Exploit zielt auf den Dienst com.apple.CoreAuthentication.daemon, der vom coreauthd-Daemon gehostet wird und als Root läuft. Dieser Dienst ist aus der App-Sandbox erreichbar. Ein wenig Experimentieren, nachdem ich den Exploit zunächst zum Laufen gebracht hatte, zeigte, dass die processor_set_tasks-API aus dem Kontext von coreauthd in der Lage ist, Send Rights auf die Task-Ports aller auf dem Gerät laufenden Userspace-Prozesse zu erhalten. Dies ist seit mindestens 2012 allgemein bekannt, und die Geschichte wird von dem prominenten iOS-Internals-Forscher Jonathan Levin auf seiner Website ausführlich behandelt (3). Der Code, den Levin 2015 hochgeladen hat, funktioniert heute noch – er benötigt kein gejailbreaktes Gerät, sondern nur Root auf einem Stock-Gerät.
Teil II
Das Hauptziel des Debuggers, den ich mit diesem Exploit bauen wollte, war es, sich an einen beliebigen Prozess anhängen, Breakpoints setzen und bei deren Auslösung den Register- und Speicherzustand inspizieren und verändern zu können. Anstatt das gdb- oder lldb-Remote-Protokoll von Grund auf neu zu implementieren, entschied ich mich, die erforderlichen Änderungen am lldb-Debugserver-Projekt vorzunehmen und dann den Exploit zu verwenden, um es auszuführen.
Anstatt Software-Breakpoints zu verwenden, die entweder das Deaktivieren oder Umgehen der Codesignierung erfordern, wird der Debugserver so gepatcht, dass er ausschließlich Hardware-Breakpoints verwendet. ARM64 verfügt über 16 Hardware-Breakpoint-Register, was bedeutet, dass maximal 16 aktive Breakpoints möglich sind.
Prototyp-Unterstützung für ARM-Hardware-Breakpoints existierte zwar im lldb-Debugserver-Code, aber es bedurfte einiger Bastelei, um sie zum Laufen zu bringen. Beispielsweise musste ich Code hinzufügen, der den pthread_introspection_hook-Funktionszeiger im Debugee so patcht, dass er immer abstürzt, damit ich die Erstellung neuer Threads erkennen, den Hardware-Breakpoint-Zustand in neu erstellte Threads propagieren und so fortfahren kann, als wäre es nie abgestürzt.
Ich habe außerdem den Attach- und Continue-Code gepatcht, um den Task direkt über den Task-Port zu suspendieren und fortzusetzen, anstatt ptrace und Signale zu verwenden.
Build-Tipps
Alles sollte auf allen iOS-Geräten mit 10.0 bis einschließlich 10.3.2 funktionieren. Getestet habe ich auf:
iPhone 7 + 10.3.2 iPod Touch + 10.1.1 iPad Mini 2 + 10.2
Ich habe ein vorgebautes Debugserver-Binary beigelegt, dessen Verwendung ich empfehle, aber der Patch für den lldb-Debugserver ist auch in debugserver.diff enthalten.
Das Bauen des Debugservers ist nicht allzu schwierig. Ich bin von den folgenden Git-Revisionen ausgegangen: lldb: 0db640c4cd1ec4e4c2580336fa5f53be029c5bc7 llvm: ec48fd127774a4b67c72ea7c3057b5c964375e77 clang: b6e778e0bfa2fc32f8821c6b33762f5cb6724659
Wende den mitgelieferten debugserver.diff an.
Für den Build benötigst du (aktuelles) CMake und Ninja; du bekommst sie aus dem Quellcode oder als Binaries von deinem bevorzugten mac-Paketmanager.
(4) enthält eine Anleitung zum Einrichten eines normalen LLVM-Builds unter macOS, die hilfreich sein könnte.
Du musst eine Reihe von Header-Dateien in dein iOS-SDK verlinken, mindestens:
xpc/ launchd.h libproc.h sys/proc_info.h sys/kern_control.h net/route.h mach/mach_vm.h mach/shared_region.h sys/ptrace.h crt_externs.h
Die folgende CMake-Beschwörung sollte dir alle Hinweise geben, die du brauchst:
cmake -G "Ninja" -DCMAKE_OSX_ARCHITECTURES="armv7;armv7s;arm64" -DCMAKE_TOOLCHAIN_FILE=../cmake/platforms/iOS.cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_BUILD_RUNTIME=Off -DLLVM_INCLUDE_TESTS=Off -DLLVM_INCLUDE_EXAMPLES=Off -DLLVM_ENABLE_BACKTRACES=Off ../
ninja debugserver
Danach musst du das Debugserver-Binary signieren oder fakesignen und das im mitgelieferten xcode-Projekt ersetzen.
Codesigning
Das Exploit-Projekt installiert standardmäßig eine verbesserte Version des mach_portal-amfid-Hooks (diesmal mit funktionierender Unterstützung für Fat Files und ohne fest verdrahtete Offsets :) )
Wenn du nur Sachen debuggen willst, solltest du das Debugserver-Binary mit deinem eigenen Zertifikat signieren und den amfid-Hook deaktivieren können.
Wenn du den amfid-Hook verwendest, beachte, dass die App, in der er läuft, weiterhin den Limits für die Codeausführung im Hintergrund unterliegt. Die App fordert zwar über beginBackgroundTaskWithName mehr Zeit an.
Verwendung:
Verbinde deinen Host und das Ziel-iDevice mit demselben WLAN-Netzwerk und notiere die IP-Adresse des iDevices.
Baue und starte die Exploit-App. Ich empfehle, dies in xcode zu tun, aber sie funktioniert auch eigenständig.
Warte ein wenig. Wenn es nach ein paar Minuten nicht funktioniert, starte das Gerät hart neu, warte ein wenig und versuche es erneut.
Wenn es funktioniert, sollte es „patched debugserver listening on port 1234“ ausgeben.
Wenn du auf die Schaltfläche „get process listing“ klickst, solltest du die Ausgabe von ps sehen.