
Una implementación del exploit Fusee Gelee (CVE-2018-6242) para la Nintendo Switch, junto con un payload personalizado.
Una implementación del exploit Fusee Gelee (CVE-2018-6242) para la Nintendo Switch, basada en la vulnerabilidad divulgada por Kate Temkin / ReSwitched en marzo de 2018.
Ejecuta un payload arbitrario (p. ej. hekate) en un dispositivo Tegra X1 en modo de recuperación USB (RCM), omitiendo por completo la verificación de firmas de la boot ROM.
RCM (Recovery Mode) es un protocolo de recuperación basado en USB integrado en la boot ROM del Tegra X1. NVIDIA lo diseñó para poder cargar pequeños programas ("applets") en un dispositivo con fines de diagnóstico o reparación, p. ej. cuando una Switch no encuentra un bootloader válido en su almacenamiento.
En la Nintendo Switch, se entra en RCM puenteando los pines 1 y 10 del riel del joycon derecho durante el arranque. En funcionamiento normal, solo NVIDIA puede usar RCM: todos los comandos deben estar firmados con la clave privada RSA de NVIDIA, y la boot ROM verifica las firmas antes de ejecutar nada.
El exploit opera a través de dos capas de protocolo independientes que comparten la IRAM (RAM integrada en el chip):
Capa USB (estándar, EP0): Cada dispositivo USB tiene un endpoint de control (EP0) que gestiona solicitudes estándar como GET_STATUS, GET_DESCRIPTOR, SET_ADDRESS. La boot ROM los implementa según lo exige la especificación USB 2.0. EP0 es implícito: no aparece en los descriptores de endpoints del dispositivo.
Capa RCM (propietaria de NVIDIA, EP1): NVIDIA define un endpoint bulk (EP1) para transferir comandos y payloads de RCM. EP1 aparece en el descriptor de dispositivo en dos direcciones:
0x01 = OUT (el host envía datos al dispositivo)0x81 = IN (el dispositivo envía datos al host)Los datos enviados a través de EP1 se estructuran como:
[680-byte RCM command header] [payload bytes]
El encabezado de 680 bytes es la struct rcm_msg_t, una estructura propietaria de NVIDIA
que contiene módulo/firma RSA, ECID, opcode y otros campos. Este tamaño se determinó
mediante ingeniería inversa de la boot ROM del Tegra X1 (ver
base de datos IDA de q3k).
El tegrarcm de código abierto de NVIDIA
solo documenta hasta 644 bytes (Tegra124); la variante T210 es 36 bytes más grande.
El manejador de solicitudes de control EP0 de la boot ROM tiene un error en su implementación de GET_STATUS para destinatarios ENDPOINT. Del whitepaper de 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);
El memcpy lee desde &status (una variable de pila justo debajo de 0x40010000) y escribe
en el búfer DMA (en 0x40009000). Con una longitud sobredimensionada:
&status, a través del resto de la pila, y entra en el
área de payload controlada por el atacante en 0x40010000+ (colocada allí mediante escrituras bulk por EP1).Los datos de origen incluyen un "stack spray" (0x40010000 repetido), que se escribe
sobre las direcciones de retorno de la pila. Cuando el manejador retorna, la ejecución salta a
0x40010000, donde colocamos un pequeño stub reubicador llamado intermezzo.
Todo esto ocurre durante el bucle de recepción de RCM (dentro de handle_control_requests),
antes de que la boot ROM valide firmas. El protocolo RCM es el mecanismo de entrega;
el manejador de control USB es el desencadenante.
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
+------------------+
El payload de usuario se divide alrededor del stack spray porque el spray debe posicionarse
de modo que el desbordamiento lo copie sobre las direcciones de retorno de la pila. Intermezzo
vuelve a ensamblar las dos mitades en un bloque contiguo en 0x40010000 y salta a él.
0x0955:0x7321) mediante enumeración USB estándar0x40010000+, colocando nuestro intermezzo, el payload de usuario y el stack spray0x40009000)wLength=0x7000 — esto activa el
memcpy vulnerable, el stack spray sobrescribe las direcciones de retorno y el manejador retorna a intermezzopip install pyusb
python launcher.py
Requiere una Switch en modo RCM conectada por USB. En macOS, puede que necesites
brew install libusb.
Coloca el binario de tu payload en binaries/payload.bin. El intermezzo.bin incluido
maneja la reubicación del payload y no debería necesitar ser reemplazado.
launcher.py — el script del exploitbinaries/intermezzo.bin — stub reubicador (124 bytes), vuelve a ensamblar el payload divididobinaries/payload.bin — el payload de usuario a ejecutar (p. ej. hekate)