
Un'implementazione dell'exploit Fusee Gelee (CVE-2018-6242) per Nintendo Switch, insieme a un payload personalizzato.
Un'implementazione dell'exploit Fusee Gelee (CVE-2018-6242) per Nintendo Switch, basata sulla vulnerabilità divulgata da Kate Temkin / ReSwitched a marzo 2018.
Consente di avviare un payload arbitrario (ad es. hekate) su un dispositivo Tegra X1 in modalità recovery USB (RCM), bypassando completamente la verifica delle firme della boot ROM.
L'RCM (Recovery Mode) è un protocollo di recovery basato su USB integrato nella boot ROM del Tegra X1. NVIDIA lo ha progettato per caricare piccoli programmi ("applet") su un dispositivo per diagnostica o riparazione — ad es. quando una Switch non trova un bootloader valido sulla sua memoria.
Sulla Nintendo Switch, l'RCM si attiva mettendo in ponte i pin 1 e 10 della guida del joycon destro durante l'avvio. In condizioni normali, solo NVIDIA può usare l'RCM — tutti i comandi devono essere firmati con la chiave privata RSA di NVIDIA e la boot ROM verifica le firme prima di eseguire qualsiasi cosa.
L'exploit opera su due livelli di protocollo indipendenti che condividono l'IRAM (RAM integrata):
Livello USB (standard, EP0): Ogni dispositivo USB ha un endpoint di controllo (EP0) che gestisce richieste standard come GET_STATUS, GET_DESCRIPTOR, SET_ADDRESS. La boot ROM le implementa come richiesto dalla specifica USB 2.0. L'EP0 è implicito — non compare nei descrittori degli endpoint del dispositivo.
Livello RCM (proprietario NVIDIA, EP1): NVIDIA definisce un endpoint bulk (EP1) per trasferire comandi e payload RCM. L'EP1 compare nel descrittore del dispositivo in due direzioni:
0x01 = OUT (l'host invia dati al dispositivo)0x81 = IN (il dispositivo invia dati all'host)I dati inviati tramite EP1 sono strutturati come:
[header comando RCM da 680 byte] [byte del payload]
L'header da 680 byte è la struct rcm_msg_t — una struttura proprietaria NVIDIA
contenente modulo/firma RSA, ECID, opcode e altri campi. Questa dimensione è stata
determinata tramite reverse engineering della boot ROM del Tegra X1 (vedi
database IDA di q3k).
Il tegrarcm open source di NVIDIA
documenta solo fino a 644 byte (Tegra124); la variante T210 è più grande di 36 byte.
L'handler delle richieste di controllo EP0 della boot ROM ha un bug nell'implementazione di GET_STATUS per i destinatari ENDPOINT. Dal whitepaper di Temkin:
// BUG: should be size_to_tx = sizeof(status), i.e. 2 bytes
size_to_tx = length_read; // attacker-controlled via wLength, up to 65535
data_to_tx = &status; // a uint16_t on the stack
memcpy(dma_buffer, data_to_tx, size_to_tx);
La memcpy legge da &status (una variabile di stack appena sotto 0x40010000) e scrive
nel buffer DMA (a 0x40009000). Con una lunghezza eccessiva:
&status, attraverso il resto dello stack, e nell'area del
payload controllata dall'attaccante a 0x40010000+ (posizionata lì tramite scritture bulk EP1).I dati di origine includono uno "stack spray" (0x40010000 ripetuto), che viene scritto
sugli indirizzi di ritorno dello stack. Quando l'handler ritorna, l'esecuzione salta a
0x40010000 — dove abbiamo posizionato un piccolo stub di rilocazione chiamato intermezzo.
Tutto questo avviene durante il ciclo di ricezione RCM (dentro handle_control_requests),
prima che la boot ROM possa validare le firme. Il protocollo RCM è il meccanismo di consegna;
l'handler di controllo USB è il trigger.
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
+------------------+
Il payload utente viene diviso attorno allo stack spray perché lo spray deve essere posizionato
in modo che l'overflow lo copi sugli indirizzi di ritorno dello stack. Intermezzo riassembla le
due metà in un blocco contiguo a 0x40010000 e ci salta.
0x0955:0x7321) con l'enumerazione USB standard0x40010000+, posizionando intermezzo, payload utente e stack spray0x40009000)wLength=0x7000 — questo attiva la
memcpy vulnerabile, lo stack spray sovrascrive gli indirizzi di ritorno e l'handler
ritorna in intermezzopip install pyusb
python launcher.py
Richiede una Switch in modalità RCM collegata via USB. Su macOS, potresti aver bisogno di
brew install libusb.
Posiziona il binario del payload in binaries/payload.bin. Il file intermezzo.bin incluso
gestisce la rilocazione del payload e non dovrebbe necessitare di essere sostituito.
launcher.py — lo script dell'exploitbinaries/intermezzo.bin — stub di rilocazione (124 byte), riassembla il payload divisobinaries/payload.bin — il payload utente da eseguire (ad es. hekate)