
Eine Implementierung des Fusee-Gelee-Exploits (CVE-2018-6242) für die Nintendo Switch, zusammen mit einer benutzerdefinierten Payload.
Eine Implementierung des Fusee-Gelee-Exploits (CVE-2018-6242) für die Nintendo Switch, basierend auf der Sicherheitslücke, die von Kate Temkin / ReSwitched im März 2018 veröffentlicht wurde.
Startet eine beliebige Nutzlast (z. B. hekate) auf einem Tegra-X1-Gerät im USB-Wiederherstellungsmodus (RCM) und umgeht dabei vollständig die Signaturprüfung des Boot-ROMs.
RCM (Recovery Mode) ist ein USB-basiertes Wiederherstellungsprotokoll, das im Boot-ROM des Tegra X1 integriert ist. NVIDIA hat es entwickelt, um kleine Programme („Applets“) zu Diagnose- oder Reparaturzwecken auf ein Gerät laden zu können – z. B. wenn eine Switch keinen gültigen Bootloader auf ihrem Speicher findet.
Auf der Nintendo Switch wird RCM durch Überbrücken der Pins 1 und 10 auf der rechten Joycon-Schiene beim Booten aktiviert. Im normalen Betrieb kann nur NVIDIA RCM verwenden – alle Befehle müssen mit NVIDIAs RSA-Private-Key signiert sein, und das Boot-ROM überprüft Signaturen, bevor es etwas ausführt.
Der Exploit arbeitet über zwei unabhängige Protokollebenen, die sich IRAM (On-Chip-RAM) teilen:
USB-Ebene (Standard, EP0): Jedes USB-Gerät hat einen Control-Endpunkt (EP0), der Standardanfragen wie GET_STATUS, GET_DESCRIPTOR, SET_ADDRESS bearbeitet. Das Boot-ROM implementiert diese gemäß der USB-2.0-Spezifikation. EP0 ist implizit – es erscheint nicht in den Endpunktdeskriptoren des Geräts.
RCM-Ebene (NVIDIA-proprietär, EP1): NVIDIA definiert einen Bulk-Endpunkt (EP1) zum Übertragen von RCM-Befehlen und Nutzlasten. EP1 erscheint im Gerätedeskriptor mit zwei Richtungen:
0x01 = OUT (Host sendet Daten an Gerät)0x81 = IN (Gerät sendet Daten an Host)Über EP1 gesendete Daten sind wie folgt strukturiert:
[680-Byte-RCM-Befehlskopf] [Nutzlastbytes]
Der 680-Byte-Header ist die rcm_msg_t-Struktur – eine NVIDIA-proprietäre Struktur,
die RSA-Modul/Signatur, ECID, Opcode und andere Felder enthält. Diese Größe wurde
durch Reverse Engineering des Tegra-X1-Boot-ROMs ermittelt (siehe
q3ks IDA-Datenbank).
NVIDIAs Open-Source-tegrarcm
dokumentiert nur bis zu 644 Bytes (Tegra124); die T210-Variante ist 36 Bytes größer.
Der EP0-Control-Request-Handler des Boot-ROMs hat einen Fehler in seiner GET_STATUS-Implementierung für ENDPOINT-Empfänger. Aus Temkins Whitepaper:
// FEHLER: sollte size_to_tx = sizeof(status) sein, d.h. 2 Bytes
size_to_tx = length_read; // vom Angreifer kontrolliert über wLength, bis zu 65535
data_to_tx = &status; // ein uint16_t auf dem Stack
memcpy(dma_buffer, data_to_tx, size_to_tx);
Das memcpy liest von &status (einer Stack-Variablen knapp unterhalb von
0x40010000) und schreibt in den DMA-Puffer (bei 0x40009000). Mit einer
übermäßigen Länge:
&status hinaus, durch den restlichen Stack und in den
vom Angreifer kontrollierten Nutzlastbereich bei 0x40010000+ (dorthin platziert
über EP1-Bulk-Schreibvorgänge).Die Quelldaten enthalten einen „Stack Spray“ (wiederholtes 0x40010000), der über
die Rücksprungadressen des Stacks geschrieben wird. Wenn der Handler zurückkehrt,
springt die Ausführung zu 0x40010000 – wo wir einen kleinen Relocator-Stub namens
intermezzo platziert haben.
Dies alles geschieht während der RCM-Empfangsschleife (innerhalb von
handle_control_requests), bevor das Boot-ROM jemals Signaturen validiert. Das
RCM-Protokoll ist der Zustellungsmechanismus; der USB-Control-Handler ist der
Auslöser.
0x40005000 +------------------+
| DMA buffer LOW | USB controller writes odd packets here
0x40009000 +------------------+
| DMA buffer HIGH | USB controller writes even packets here
+------------------+
| execution stack | grows downward toward DMA buffers
0x40010000 +------------------+ <-- stack ends here / payload starts here
| intermezzo | small relocator stub (124 bytes)
0x40010E40 +------------------+
| user payload pt1 | first ~16KB of the user payload
0x40014E40 +------------------+
| stack spray | 0x40010000 repeated (8640 bytes)
0x40017000 +------------------+
| user payload pt2 | remainder of user payload
+------------------+
Die Benutzer-Nutzlast wird um den Stack Spray herum aufgeteilt, da der Spray so
positioniert sein muss, dass der Überlauf ihn über die Rücksprungadressen des Stacks
kopiert. Intermezzo setzt die beiden Hälften zu einem zusammenhängenden Block bei
0x40010000 wieder zusammen und springt dorthin.
0x0955:0x7321) über die standardmäßige
USB-Enumeration finden0x40010000+ und platziert dabei unser
intermezzo, die Benutzer-Nutzlast und den Stack Spray0x40009000)
adressiert hatwLength=0x7000 senden – dies
löst das anfällige memcpy aus, der Stack Spray überschreibt Rücksprungadressen,
und der Handler kehrt zu intermezzo zurückpip install pyusb
python launcher.py
Erfordert eine Switch im RCM-Modus, die über USB verbunden ist. Unter macOS
benötigen Sie möglicherweise brew install libusb.
Legen Sie Ihre Nutzlast-Binärdatei unter binaries/payload.bin ab. Die enthaltene
intermezzo.bin übernimmt die Nutzlastverschiebung und sollte nicht ersetzt werden
müssen.
launcher.py — das Exploit-Skriptbinaries/intermezzo.bin — Relocator-Stub (124 Bytes), setzt die aufgeteilte
Nutzlast wieder zusammenbinaries/payload.bin — die auszuführende Benutzer-Nutzlast (z. B. hekate)