jamais vu:un exploit sulla TrustZone del firmware 1.0.0 su Nintendo Switch da SciresMTempo di lettura : 10 min

Come promesso, SciresM ha pubblicato una recensione dell’exploit di motezazer sulla TrustZone per il firmware 1.0.0 del Nintendo Switch su Reddit . Il lavoro è durato alcuni giorni e iniziato all’inizio di dicembre 2017.Il firmware 1.0.0 è fondamentalmente un “firmware beta”, internamente denominato “pilota”. Nintendo ha dovuto spedirli con le prime console per rispettare la scadenza di produzione dello switch, quindi questo firmware include alcune vulnerabilità critiche che sono state corrette con la versione 2.0.0. Questa è una pratica comune nel settore, quindi fino al rilascio effettivo della console,c’e’ ancora molto tempo per applicare patch al software e incorporare anche funzionalità online.Per comprendere il testo, l’utente  dovrebbe avere una conoscenza di base dei crittosistemi simmetrici, della crittografia a blocchi e dell’architettura dello switch. Dovresti anche aver visto la conferenza al 34C3 !

Traduzione in italiano (alcuni errori sono presenti)

Quello che segue è un resoconto di come ho inizialmente realizzato l’esecuzione del codice TrustZone su Nintendo Switch, molto ispirato dai write up di hexkyz . Il lavoro completato è stato svolto nel corso di un paio di giorni dall’inizio alla fine all’inizio di dicembre 2017.

Lo sviluppo degli exploit è stato un processo collaborativo tra me e motezazer : insieme abbiamo trovato, sviluppato e sfruttato i difetti descritti di seguito. 🙂

Per ottenere il massimo da questo testo, è necessario avere almeno una conoscenza di base di: crittografia simmetrica, modalità di funzionamento a blocchi di codice e architettura generale del modello di sicurezza di Nintendo Switch. È consigliabile che i lettori guardino il 34C3  ” Console Security – Switch ” prima di continuare.

L’inizio

Verso la fine di novembre del 2017, sono riuscito a ottenere l’esecuzione arbitraria di codici a livello kernel su Nintendo Switch! Ma non volevo fermarmi qui: volevo continuare a cercare di rimuovere i livelli di sicurezza rimanenti e provare a ottenere l’ esecuzione del codice TrustZone sulla mia console 1.0.0. Per il contesto, 1.0.0 era un firmware “beta” chiamato internamente “Pilot”, che Nintendo doveva fornire con le prime console per rispettare le scadenze di produzione. Contiene molti problemi di sicurezza critici risolti nelle versioni successive del firmware.

Fortunatamente, non stavo partendo da zero. Grazie all’eccellente documentazione di naehrwert e qlutoo su SwitchBrew , sapevo che l’implementazione di TrustZone di Nintendo era un’API di crittografia senza stato e aveva le descrizioni di tutte le operazioni supportate. L’interfaccia sembrava avere una superficie di attacco molto piccola, tuttavia, e dalla documentazione del layout di memoria sapevo che l’intero codice si adattava a 56 KB. Questo suggeriva che era minuscolo, probabilmente apposta per assicurarsi che Nintendo potesse rimuovere tutti i suoi bug. Quindi, piuttosto che tentare di confondere l’API fornita, ho deciso che la migliore linea d’azione era cercare di trovare una sorta di canale laterale per attaccarlo.

Rileggendo il riferimento tecnico Tegra X1 manuale (TRM), vediamo i riferimenti a “Deep Sleep”, qualcosa che ho trovato anche accennato da naehrwert  nella documentazione’ di smcCpuSuspendsu SwitchBrew. Il TRM afferma che lo sleep viene immesso lasciando tutti i quattro core della CPU in modalità di sospensione, quindi passando l’esecuzione al processore di avvio e di gestione dell’alimentazione o al BPMP. Il BPMP è responsabile per l’attivazione dei registri del sistema per andare a effettuare lo sleep, a quel punto l’alimentazione viene interrotta per il SoC e tutti i contenuti di memoria diversi dalla memoria principale dello Switch (DRAM) e un blocco “always-on” dei registri (la gestione dell’alimentazione Controller o PMC) perdono il loro contenuto. Questo sembra davvero interessante, però: tra la memoria che perde il suo contenuto c’è la RAM sicura di Trust-Zone(TZRAM), in cui sono memorizzati tutti i codici e lo stato di TrustZone. TrustZone deve essere memorizzato su DRAM! È quasi certamente crittografato, ma il TRM ci dice che ci dovrebbe essere un breve firmware “warmboot” per ripristinarlo una volta che la console si sarà svegliata. Non abbiamo una copia di questo firmware, ma dal momento che il BPMP è l’ultima cosa sveglia, forse possiamo lavorare con quello per scaricarlo in qualche modo?

La cosa ovvia da fare è provare a vedere come il sistema operativo interagisce con il BPMP. Iniziamo a considerare i processi di sistema dello Switch (“moduli di sistema” o solo “sysmodules”) per vedere se qualcosa mappa nell’input / output mappato in memoria (MMIO) richiesto per interagire con esso. Ottimizziamo l’oro con il ammodulo di sistema su 1.0.0: si mappano nei vettori di eccezione del BPMP e l’MMIO richiesto per accendere il processore. Si scopre che amimposta un firmware da eseguire sul BPMP in fase di esecuzione mappando parte della sua memoria per il processore, impostando il RESETvettore in modo che punti al suo firmware e accendendo il BPMP. Il BPMP copia quindi il codice del firmware principale nella RAM interna (“IRAM”) e lo esegue lì. Individuare immediatamente alcune corde interessanti,firmware! È responsabile della gestione audio su 1.0.0; su 2.0.0, questo è stato spostato nel audiomodulo di sistema. Notiamo che il BPMP è talvolta chiamato anche “AVPC” (“Processore Audio / Video”) nel TRM. Nintendo inizialmente lo usava come tale, ma realizzato da 2.0.0 che farlo era pericoloso, e giustamente.

Reverse-engineering del firmware del piccolo kernel di più, possiamo individuare il codice responsabile per l’accesso al sonno profondo! Una volta che TrustZone va in stop, viene notificato il kernel piccolo e vengono eseguite le interazioni MMIO necessarie per salvare tutti gli stati rimanenti e disattivare il SoC. Possiamo immediatamente usare l’esecuzione del codice sotto amper modificare il firmware del piccolo kernel per modificare questa interazione, collegandola con il nostro codice. Questo ci consente di scaricare il contesto di MMIO appena prima che la console dorme, ottenendo ulteriori informazioni sul processo di sospensione profonda.


Un intermezzo:

Secondo il TRM, quando la console si risveglia dallo sleep, il BPMP viene effettivamente ripristinato, il che significa che il bootrom viene effettivamente eseguito da zero , seguendo un codepath diverso da quello eseguito su coldboot. Questo codepath viene attivato da un bit impostato in un registro PMC, che potrei osservare la scrittura di piccoli kernel prima di iniziare il sonno. Fortunatamente, ho avuto la fortuna di avere una copia scaricata del bootrom tegra da invertire: grazie al team hardware ReSwitched (e in particolare andeor e hedgeberg) lavorare per capire e glitching il processo di avvio della console all’inizio dell’anno. Con una copia del bootrom Tegra X1 in mano, possiamo invertire le funzioni che il bootrom utilizza per caricare il firmware di warmboot di Nintendo, ufficialmente chiamato warmboot.bin. Facendo ciò, vediamo che il bootrom si aspetta che il firmware di wakeup venga memorizzato nella DRAM su uno specifico indirizzo memorizzato nel MMIO PMC (dalla nostra copia di dumping dei registri appena prima di dormire, questo è 0x8000D000). Scaricando la memoria a quell’indirizzo, otteniamo una copia di warmboot.bin!


Uno sguardo da vicino su warmboot.bin

Guardando warmboot.bin, vediamo che è minuscolo. Ha una dimensione di circa 4KB ( 0xEF4). Questo è in realtà abbastanza piccolo da poter essere invertito rapidamente nella sua interezza! Facendo così, vediamo che in realtà non fa molto. Innanzitutto, accende l’hardware per alcuni dispositivi e ripristina il contesto del controller di memoria. Copia quindi il contesto TrustZone salvato in TZRAM, quindi utilizza il Security Engine per decrittografarlo sul posto utilizzando un keyslot fisso (keyslot # 2) e AES-256-CBCun IV tutto-zero. Quindi usa lo stesso keyslot per calcolare un AES-256-CMACblob decrittografato e verifica che questo MAC corrisponda a quello letto dai registri PMC, panic () – ing se la verifica fallisce. Quindi, legge il punto di accesso salvato per TrustZone e lo scrive nel vettore di avvio della CPU principale (SB_AA64_RESET_LOW_0). Alla fine, accende la CPU principale e si ferma. Questo è in realtà piuttosto interessante! L’utilizzo di AES-256-CBCè doppiamente sorprendente: Nintendo ha in gran parte abbandonato quella AES-CBC in favore di AES-CTR su 3DS / Wii U e, più recentemente sullo Switch, AES-XTS. Inoltre, tutti gli altri crypto sul sistema sono AES a 128 bit, non a 256 bit. Questo potrebbe indicare che nVidia ha dato a Nintendo alcuni suggerimenti (e qualche corda con cui attaccarsi). Inoltre, almeno su 1.0.0 warmboot è interamente in linea, con tutte le azioni che si verificano in una singola funzione monolitica. (In 2.0.0+, questo è cambiato per usare un progetto Saner).

Per quanto riguarda le attuali osservazioni sulla sicurezza, non c’è molto da lavorare. Tuttavia, osserviamo che warmboot.binnon inizializza il keyslot che decripta TrustZone con. Quel keyslot deve essere impostato prima del deep sleep e salvato (con il resto del contesto del Security Engine) in DRAM da qualche parte! Questo sembra un vettore potenziale davvero interessante per attaccare TrustZone, ma poiché warmboot.binnon reinizializza il Security Engine, dovremo tornare al bootrom per vedere come viene fatto.

Rivedendo il bootrom (e, sorprendentemente, le intestazioni e il codice di Android per i sistemi Linux basati su Tegra (non lo Switch)), vediamo che il bootrom in effetti ripristina il Security Engine da un BLOB di contesto archiviato in DRAM ( 0x8000F000sullo Switch) . Tuttavia, questo blob è, purtroppo, crittografato. Per decrittografarlo, il bootrom recupera prima una chiave AES a 128 bit da alcuni registri scratch PMC, carica quella chiave nel motore Security e cancella i registri da cui legge la chiave. Quindi, esegue una AES-128-CBCdecrittazione delle dimensioni 0x840sul BLOB con un IV tutto-zero. Quindi convalida che il blob è valido verificando che l’ultimo blocco decifra a 0102030405060708090a0b0c0d0e0fcui fanno riferimento intestazioni Android comeSE_CONTEXT_SAVE_KNOWN_PATTERN. Se il “pattern conosciuto” è presente, il bootrom carica nuovamente il contesto (chiavi, IV e flag keyslot) utilizzando un formato documentato interamente dalle intestazioni Android. Altrimenti, imposta i contenuti del motore completamente a zero.

Guardando più da vicino a quella procedura, controlla che il blob sia valido verificando l’ultimo blocco. In superficie, possiamo immediatamente vedere un modo per controllare la chiave utilizzata per decrittografare TrustZone: se corrompiamo l’ultimo blocco, TrustZone verrà decrittografato con una chiave all-zero! Ma possiamo fare anche meglio di così. La decrittografia AES-CBC è una cifra a accesso casuale; più specificamente, il contenuto in testo in chiaro del blocco K dipende solo dal testo cifrato per i blocchi K e K-1. (In particolare, il testo in chiaro K è uguale a decriptare (ciphertext K) XOR ciphertext K-1). Tuttavia, ciò significa che il motore di sicurezza non rileverà se i blocchi diversi dagli ultimi due vengono modificati, perché altre modifiche non corromperanno il modello noto! Poiché il penultimo blocco memorizza solo gli ultimi 16 byte di un modulo RSA-2048 che non viene utilizzato, possiamo modificare liberamente il blob crittografato .

All’inizio potrebbe sembrare che non ci sia un modo immediato per controllare il blob decrittografato, ma si scopre che possiamo essere intelligenti con i primitivi crittografici. Poiché il testo in chiaro di un blocco dipende solo da se stesso e dal blocco precedente, possiamo osservare che possiamo scambiare tuple di blocchi: se copiamo i blocchi da K-1 a K + M su N-1 a N + M, controlliamo il contenuto di blocchi da N a N + M , a costo di corrompere il contenuto del blocco N-1. (Un aiuto di visualizzazione per questa tecnica può essere trovato qui). Abbiamo anche un ovvio testo in chiaro noto nel blob da copiare: gli IV per il motore dovrebbero essere tutti zero perché TrustZone ci consente di toccare solo alcuni keyslots e, dalla documentazione, TrustZone utilizza ampiamente AES-ECB (che non ha IV o equivalente) internamente. Con la capacità di mescolare i contenuti del motore di sicurezza, un piano inizia a venire insieme …

Exfiltrating TrustZone

Un exploit inizia a venire fuori da questi diversi pezzi: iniziamo scrivendo il nostro MAC nei registri PMC da cui si warmboot.binlegge. Quindi, aggiungiamo un hook al codice pre-sleep in un piccolo kernel. Quando TrustZone segnala al BPMP di iniziare a prepararsi per andare a dormire, il nostro hook viene eseguito: eseguiamo il backup della vera chiave TrustZone in un keyslot inutilizzato e quindi azzeriamo la chiave TrustZone. Eseguiamo anche il backup del vero blob TrustZone in una posizione DRAM sicura e lo sostituiamo con il nostro blob personalizzato. Quindi, andiamo a dormire. Quando ci svegliamo, il bootrom deciderà che il contesto del Security Engine è valido, perché non abbiamo toccato gli ultimi due blocchi. Caricherà i nostri dati chiave nel Security Engine e verrà avviato warmboot.bin.warmboot.bindecodificherà il nostro blob personalizzato in TZRAM usando una chiave di tutti gli zero, e verificarlo con il MAC che controlliamo. Questa convalida avrà successo e warmboot.binaccenderà la CPU principale e inizierà a eseguire il nostro blob con noi controllando tutto il TZRAM .

Ho subito iniziato a testare questo piano. Ho subito notato due punti di incollaggio: non sapevo dove il codice riprendesse all’interno di TrustZone e il debugging era estremamente difficile. Tuttavia, ho notato warmboot.binche sebbene la dimensione usata per decriptare il blob di TrustZone fosse 0xFF00solo i 0xE000byte sono verificati . Quindi, sappiamo che il punto di accesso iniziale non salta agli ultimi 0x1F00byte, e devono essere sicuri per noi per scrivere il nostro carico utile. Una strategia facile era quella di riempire il primo 0xE000di TZRAM con una diapositiva NOPGarantire che, indipendentemente da dove è stata avviata l’esecuzione, si finirebbe per eseguire il mio codice. Ho anche trovato un trucco per eseguire il debug delle versioni iniziali (non funzionando molto bene) del payload: se facessi il mio payload a ciclo infinito, lo schermo rimarrebbe spento (e nero). Tuttavia, se avessi fatto il mio payload eseguire un riavvio, il bootrom avrebbe caricato il bootloader di stage 1. Il bootloader stage 1 dovrebbe quindi accendere lo schermo, illuminando la retroilluminazione! Controllando se lo schermo si illumina, posso dire se i payload sono riusciti o meno e dove (un po ‘di debugging!). Questo è stato inestimabile per far funzionare correttamente il mio codice.

Motezazer ha finito per progettare un semplice payload a due stadi: la fase 1 (situata in 0xE000) utilizza la chiave che abbiamo copiato in un safelot sicuro nel Security Engine per decrittografare il vero blob TrustZone nella DRAM dove possiamo esaminarlo in seguito. Lo Stage 1 copia quindi un piccolo stub Stage 2 da 0xF0000xFF00e lo salta. La fase 2 copia solo i 0xFF00byte dal blob TrustZone decrittografato in TZRAM e salta ad esso. Inizialmente ho provato a leggere il registro warmboot.binper capire dove fosse il punto di ingresso, ma in pratica non sembrava funzionare (probabilmente stavo sbagliando), ma alla fine ho eseguito una ricerca binaria per trovare il punto di accesso corretto di inserendo loop infiniti / ripristina in vari punti nella diapositiva NOP. Alla fine, ho scoperto che il punto di accesso era a 0x3000.

Quando finalmente il carico utile ha funzionato, la mia console 1.0.0 si è riavviata nel mondo degli utenti e ho riacquistato l’esecuzione. Ho usato PegaSwitch per scaricare i contenuti di DRAM dove il mio payload decifrato TrustZone, e ottenuto una copia del codice TrustZone reale! A questo punto, l’exploit è stato completato: avevamo tutto il necessario per installare TrustZone su patch per installare i nostri SMC personalizzati. Stranamente, TrustZone verifica in realtà se i dati chiave sono cambiati e, in caso affermativo, è panico (questo potrebbe suggerire che Nintendo fosse a conoscenza della possibilità di un motore di sicurezza anomalo). Il mio primo vero test di esecuzione del codice EL3 è semplicemente saltato sul codice di panico “keydata has changed”, che ho twittato una foto di quando ho scoperto che era il caso.

Come bonus, la nostra capacità di scambiare blocchi attorno al contesto del Security Engine ci permette di usare un altro trucco di primitiva crittografia-primitiva con AES-CBC. Possiamo copiare un keyslot che contiene una chiave importante nella IV di un altro keyslot ed eseguire una decrittografia usando quel keyslot di qualche testo cifrato che controlliamo. Poiché il primo blocco di una decrittografia AES-CBC tratta l’IV nello stesso modo in cui i blocchi successivi trattano i blocchi precedenti, ciò si tradurrà in un testo in chiaro di decrypted_ciphertext XOR key. Se conosciamo decrypted_ciphertext (che facciamo perché possiamo controllare la chiave usata e il testo cifrato usato), possiamo calcolare key. Questo ci permette di scaricare chiavi arbitrarie dal motore di sicurezza, anche da keyslots “di sola scrittura” !

Anche se la nostra capacità di scambiare blocchi di Security Engine è un errore logico di crittografia nel bootrom e quindi non può essere risolto su unità esistenti, Nintendo può e ha da allora jamais vupesantemente mitigato le tecniche. A partire dalla versione 2.0.0, i vettori di eccezione del BPMP (che determinano l’esecuzione del codice) vengono inseriti nella lista nera per essere mappati da userland. Infatti, il BPMP è completamente addormentato in fase di esecuzione! Nintendo ha anche creato i registri PMC, che memorizzano il MAC che serve per modificare solo Secure-World, quindi non possono essere toccati anche con kernelhax. Quando la preparazione del sonno profondo inizia nell’ultimo core della CPU, TrustZone controlla accuratamente un sistema che non funziona correttamente: verifica che il BPMP sia arrestato, gli altri tre core della CPU siano spenti e i controller DMA che hanno accesso alla memoria interna dove Il codice BPMP viene eseguito in reset (o disattivato). Se qualcuno di questi controlli fallisce, va in panico. Se passano, imposta il RESETvettore per il BPMP stesso, carica il proprio firmware per eseguirlo sul BPMP e solo dopo accende il BPMP. Queste sono mitigazioni molto, molto approfondite, lasciando il compito di capire come ottenere l’esecuzione di TrustZone su firmware più alti un compito per un altro giorno …

Applicazioni pratiche

  • Su 1.0.0, l’ esecuzione del codice al massimo livello di privilegio possibile . TrustZone è responsabile solo della crittografia, ma poiché si jamais vuverifica il controllo dell’intero contenuto di TZRAM quando il sistema viene riavviato, siamo nella posizione ideale per “riavviarlo” nella nostra versione del sistema operativo con patch.
  • Possiamo scaricare le chiavi da keyslots “write-only”. Il cryptosystem di Nintendo si basa su TrustZone che riceve solo due chiavi: una condivisa master keye una console unica device key. I firmware più recenti possono cambiare master keyquando viene bruciato un fusibile, ma possiamo scaricare la 1.0.0 master keye la nostra console device keyed eseguire tutta la crittografia che una console da 1.0.0 a 2.3.0 sa come eseguire in fase di esecuzione sui nostri PC .
  • Abbiamo rimosso un altro livello di sicurezza e possiamo analizzare e comprendere il sistema di crittografia di Nintendo. Questa è la vera vittoria 🙂
Ti è piaciuta la notizia? Supporta theheroGAC su Patreon!

theheroGAC

Nato negli anni 80 con la passione dei videogiochi e delle console.Il mio primo home computer è l'Amiga 600 regalato a 10 anni.Amo aiutare le persone in difficoltà e scrivere notizie sulle console.Studio all'università e il sito Games And Consoles è la mia passione.Per gli amici mi potete chiamare Ciccio

Ti potrebbe anche interessare

Rispondi