Home News Il team Fail0verflow pubblica un nuovo articolo dove spiega come hackerare la...

Il team Fail0verflow pubblica un nuovo articolo dove spiega come hackerare la periferica PS VR

201
0

Il team Fail0verflow torna sulla scena pubblicando un nuovo e interessantissimo articolo sul proprio blog a distanza di quasi 4 anni, il DUT (Device Under Test) per come lo ha definito ps5_enthusiast, vede la periferica PS4 Virtual Reality: PSVR.

Il team Fail0verflow è riuscito a scovare alcuni difetti all’interno della periferica che hanno portato alla rottura dell’avvio protetto ed all’estrazione di tutto il suo materiale chiave.

L’hack potrebbe servire come possibile entrypoint sia sulla console PlayStation 4 che sulla PlayStation 5, tuttavia pare che non ci siano stati progressi su questo fronte.

Il team spera comunque che la superficie di attacco possa risultare utile in futuro, e in qualche altro modo.

Panoramica PSVR

La configurazione del visore PSVR include diversi componenti:

  • Scheda madre PSVR.
  • Cuffie PSVR.
  • PS camera.
  • Una sorta di input (PS Move, PS Aim, DS4, …).
  • Console PS4/PS5.
  • Uscita display HDMI.

Esistono diverse revisioni della scheda madre PSVR, tuttavia sono tutte dotate dello stesso SoC Marvell 88DE3214 e condividono tutto il materiale chiave (o almeno, le chiavi relative al PSVR).

La console host è collegata alla scheda madre PSVR tramite HDMI e USB, entrambi elaborati nel Marvell 88DE3214. In condizioni normali, viene eseguita una sequenza di autenticazione tramite USB prima che la console accetti il dispositivo come PSVR.

Quindi, l’obiettivo era quello di estrarre i segreti necessari per tale autenticazione e invertire il protocollo di comunicazione.

L’articolo prende in esame principalmente il SoC principale sulla scheda madre PSVR, questo perche il team Fail0verflow era più interessato all’utilizzo dei segreti contenuti all’interno del PSVR e intenzionata a raggiungere una potenziale superficie di attacco sulla console host.

Al giorno d’oggi è possibile trovare diversi teardown che offrono un’ampia panoramica di tutti i componenti utilizzati dalla periferica (tra i più interessanti vengono citati 4gamer e ifixit).

Il team ha inoltre dato una rapida occhiata alla scheda madre per individuare tutti i componenti più interessanti, la I/O e scaricare la eMMC. Le seguenti scansioni della scheda contengono alcune annotazioni:

Alcune cose che spiccavano:

  • UART è brevemente attivo durante l’avvio anticipato (solo output).
  • eMMC ha tracce facilmente accessibili su entrambi i lati della scheda.
  • Il contenuto eMMC appare per lo più crittografato (quello che c’è sembra Android-ish).
  • L’interferenza con le tracce eMMC durante l’avvio anticipato genera alcuni registri di errore aggiuntivi sull’UART.
  • Il Marvell 88E8080 viene utilizzato per la connessione USB alla console host ed è collegato all’88DE3214 tramite PCIe.

Accensione

La scheda si avvierà in modalità a basso consumo non appena viene fornita alimentazione. Per l’accensione completa, l’interruttore di alimentazione sul cavo di controllo dell’auricolare deve essere premuto momentaneamente. 

Questo è il pin 13 del connettore ed è anche un grande test pad sull’altro lato della scheda. La scheda può avviarsi e funzionare completamente senza dissipatore di calore, ma il SoC diventa relativamente caldo.

Dopo l’avvio completo, la scheda apparirà come un dispositivo USB.

Possibili entrypoint iniziali

Le porte HDMI sulla scheda madre PSVR per console e TV hanno CEC instradate tra di loro, presumibilmente anche al SoC Marvell. 

amp_devices_vpp_cec_isrnei sorgenti 88DE3214 sembra che abbia lo stesso bug del gestore PS4 EMC CEC. Non sono mai riuscito a verificare se l’hardware Marvell CEC può bufferizzare > 16 byte.

Il kernel ha il supporto USB(gadget) abilitato. Il dispositivo Marvell 88E8080 è configurato per fungere da controller USB DWC3 su PCIe (notare che Armada supporta solo PCIe 2.0 1x). 

Ciò significa che le cose USB e USB-gadget sono esposte, così come PCIe DMA (l’Armada non ha IOMMU). Il driver abilita anche il bus mastering.

Il kernel ha il supporto ethernet berlin abilitato e il dispositivo berlin-fe è abilitato per impostazione predefinita, è probabile che alcuni pad non popolati sulla scheda siano Ethernet. Per questo viene utilizzato il driver Marvel/geth.

EHCI sembra abilitato in kernel + dts, non sono sicuro se ci sono pad USB non popolati sulla scheda.

PCIe hax

Chiaramente, l’entrypoint più interessante è PCIe, ed ecco come si presentava la configurazione:

Dopo aver eseguito la connessione, era evidente che i dispositivi PCIe esterni hanno un livello sorprendente di accesso ai componenti interni del SoC.

Naturalmente, ha accesso quasi completo alla DRAM (a parte le regioni contrassegnate come Secure for TrustZone), tuttavia dà anche accesso alle SRAM utilizzate durante l’avvio dei vari core ARM, alle SRAM dei core periferici e alla capacità di leggere/scrivere qualsiasi registro MMIO esistente nello spazio degli indirizzi fisici del SoC. 

Ciò ha consentito di scaricare direttamente la bootrom e invertire l’implementazione dell’avvio sicuro.

Dopo aver creato alcuni strumenti per introspezione e iniettare il kernel Linux in esecuzione e scaricare i processi dello spazio utente, ecc.., era evidente che sarebbe stato necessario un compromesso di TrustZone per ottenere i segreti.

Rompere la TrustZone

L’ispezione dell’interfaccia TrustZone/linux ha rivelato molte vulnerabilità. Tuttavia, era necessario solo uno per eseguire il dump dei segreti utilizzati per l’autenticazione del PSVR.

Il comando libdrmtz.ta 0x22 sembra essere chiamato DrmDiag_LoadArmImge/o MV_IMAGETOOL_Decrypt_AES_FIGOQuesto prende l’indirizzo di caricamento di destinazione come parametro (tra gli altri) e gli consente di puntare nella memoria del mondo normale. 

Pertanto, le immagini ARM crittografate possono essere decrittografate e scaricate, fornendo testo in chiaro ai TA per cercare ulteriori bug, oltre a eventuali segreti che potrebbero essere trapelati dall’accesso di decrittazione Oracle ai binari di TrustZone.

Come si è scoperto, questo era già sufficiente per recuperare i segreti utilizzati dal PSVR, poiché sono contenuti all’interno di TA e non si basano su segreti hardware.

Tuttavia, è possibile andare oltre e scaricare tutti i segreti hardware e compromettere la radice della fiducia.

A tal fine, è interessante notare che libdrmtz.ta contiene anche il simbolo rom_figo_figoche sembra essere una versione non di produzione dell’IROM della “FIGO”.

Sembra una variabile che deve essere compilata nelle build di rilascio, ma il compilatore non l’ha effettivamente esclusa in questo caso.

Una delle chiavi FIGO DROM è anche nel binario (anch’esso non utilizzato): g_puROMKeyè la chiave memorizzata in FIGO_BASE+0x56d0cui viene utilizzata come KEK id 0x10.

FIGO

Il FIGO (“Flow Instructions Get Optimized”; che bel nome) è il coprocessore sicuro residente nel SoC.

Sebbene non controlli direttamente l’avvio protetto, autentica/decodifica le immagini caricate e può essere utilizzato per ospitare e ricavare materiale chiave all’interno della propria parte appartata del SoC. 

Sebbene abbia periferiche IROM, DROM, fusibili, crittografia, ecc.., ha anche DRAM (ovviamente) e IRAM per supportare il caricamento e l’esecuzione del firmware in fase di esecuzione.

Questi firmware tendono ad essere relativamente piccoli e gestiscono un insieme limitato di responsabilità per immagine.

La FIGO è piuttosto interessante in quanto esegue un set di istruzioni unico (bene per noi, documentato in alcuni dei codici disponibili pubblicamente da Chromecast, ecc..) e, naturalmente, perché nasconde segreti in qualche modo meglio protetti.

Un (dis)assemblatore per Il codice FIGO può essere trovato a questo indirizzo.

Un fatto architettonico importante da notare è che il blocco hardware FIGO contiene una MPU che consente alla FIGO di proteggere le sottoregioni del suo spazio MMIO dall’accesso esterno.

Le sottoregioni possono essere disabilitate, di sola lettura, di sola scrittura o di lettura-scrittura nel mondo esterno a FIGO. 

Normalmente, FIGO IROM e firmware lo usano per impedire che i segreti vengano letti direttamente dalle sue varie memorie e periferiche (altrimenti, potremmo semplicemente scaricare la maggior parte dei segreti FIGO direttamente dal dispositivo PCIe esterno!).

Invio del firmware in FIGO

Prima di entrare nelle attuali vulnerabilità di FIGO, vale la pena menzionare un altro difetto di progettazione nel codice TrustZone.

Normalmente, dopo l’inizializzazione di TrustZone e la configurazione di FIGO per l’utilizzo in runtime, è previsto che FIGO non sia direttamente accessibile a Normal World.

Invece, TrustZone racchiude le funzionalità che possono essere implementate nei moduli FIGO e carica/chiama dinamicamente i moduli secondo necessità. 

Questo caricamento dinamico chiama MV_DRMLIB_GetFIGOFW per recuperare un buffer contenente il firmware che verrà inviato alla FIGO IROM per l’autenticazione e l’esecuzione sulla FIGO.

Internamente, MV_DRMLIB_GetFIGOFW cerca un puntatore in un array precedentemente caricato di firmware FIGO che risiedono nella memoria Normal World.

Si potrebbe obiettare che questo è sicuro in quanto la FIGO dovrebbe in primo luogo autenticare le immagini, tuttavia espone inutilmente la superficie di attacco. Come mostreremo, può essere utilizzato per sfruttare la FIGO di Normal World.

DrmDiag_OTPReadespone un buon modo per abusare di questa funzionalità in quanto ha effetti collaterali minimi. 

In sostanza, l’immagine FIGO nella DRAM Normal World può essere aggiornata dal nostro dispositivo PCIe, quindi il dispositivo PCIe può causare la chiamata del kernel Normal World DrmDiag_OTPReadche tenterà di caricare ed eseguire i dati arbitrari forniti da Normal World come firmware FIGO. 

In combinazione con l’accesso del dispositivo PCIe a MMIO, questo elimina la necessità di assumere completamente Secure World per attaccare la FIGO.

Corsa alla FIGO

Durante il tentativo iniziale di capire come FIGO carica e autentica il firmware FIGO, è stato osservato che mentre l’IROM elabora una richiesta di caricamento dell’immagine, alcune parti della FIGO DRAM sarebbero state deprotette nella MPU e visibili al nostro dispositivo PCIe.

In particolare, il formato immagine elaborato dall’IROM ha un’intestazione contenente una chiave con wrapping rfc3394. 

L’IROM memorizza i dati utilizzati per eseguire l’unwrap AES in FIGO_BASE+0x1980cui è visibile nella mappatura MMIO sul nostro dispositivo PCIe mentre l’IROM lo elabora. 

Questa chiave scartata viene utilizzata per decrittografare il corpo dell’immagine del firmware FIGO, che ci ha permesso di decrittografare e iniziare a invertire alcuni firmware FIGO.

Pwning FIGO IROM

L’intestazione dell’immagine FIGO descrive 2 sezioni di carico, una per ciascuna IRAM e DRAM.

// @ +0x78 in FIGO image header
// count of 4byte instructions
u16 ins_cnt;
u16 pc_offset;
// count of 8byte data
u16 dat_cnt;
// DRAM load addr / 8
u16 dat_offset;

Mentre si presume che l’indirizzo di destinazione IRAM sia IRAM+0, l’offset DRAM è configurabile. 

Cosa succede se ci limitiamo a compensare la forza bruta nella DRAM fino a quando non raggiungiamo dove deve essere lo stack IROM? (A questo punto, non avevamo ancora un modo per vedere l’IROM o il contenuto dello stack).

Esatto, otteniamo ROP nel contesto IROM!

Invertendo i firmware FIGO che avevamo decifrato a questo punto, siamo stati in grado di costruire un piccolo codice shell per scaricare tutti i fusibili. 

Abbiamo anche riconfigurato l’MPU per eliminare tutte le protezioni, consentendo il dumping diretto di IROM e DROM completi (che memorizza anche alcune chiavi) da PCIe

FIGO IROM, ancora!

Presentiamo la nostra re-implementazione dell’autenticazione e decrittografia dell’immagine che corrisponde al comportamento di FIGO IROM. capisci il problema?

def img_decrypt_and_verify(data):
    kek_id, _, key_wrapped = struct.unpack_from('<II24s', data, 0)
    sign_id, sign_type, sig_len, msg_digest, bind_info = \
        struct.unpack_from('<IHH32s16s', data, 0x40)

    # hueristic using figo insn fields
    img_type = IMG_TYPE_ARM if all_zero(data[0x78:0x7c]) else IMG_TYPE_FIGO
    img_len, enc_len = 0, 0
    if img_type == IMG_TYPE_ARM:
        img_len = struct.unpack_from('<I', data, 0x7c)[0]
        enc_len = align_up(img_len, 0x10) - 0x300
    else:
        ins_cnt, pc_offset, dat_cnt, dat_offset = \
            struct.unpack_from('<HHHH', data, 0x78)
        ins_len = ins_cnt * 4
        dat_len = dat_cnt * 8
        dat_load_addr = dat_offset * 8
        enc_len = img_len = align_up(ins_len, 0x20) + align_up(dat_len, 0x10)

     key = None
     if kek_id in (0x80, 0x81):
         for kek in iter_customer_kek(kek_id):
             key = aes_unwrap(kek, key_wrapped)
             if key is not None: break
     else:
         kek = kek_id_to_kek(kek_id)
         if kek is None:
             print('kek lookup failed. kek_id %02x' % (kek_id))
             return None

         key = aes_unwrap(kek, key_wrapped)
     if key is None:
         print('unwrap failed. kek_id %02x wrapped %s' % (
             kek_id, key_wrapped.hex()))
         return None

     if sign_type != 2:
         print('unexpected sign type %d' % (sign_type))
         return None
     if sig_len != 0x100:
         print('unexpected sig len %d' % (sig_len))
         return None

hdr_signed = data[0x68:0x280]
payload = aes_cbc_iv_zero_decrypt(key, data[0x380:0x380 + enc_len])

     # signing key id 2 should match otp:28, but w/e
     #sha256(e+n).digest() == sign_key_digest
     e = data[0x80:0x180]
     n = data[0x180:0x280]
     sig = data[0x280:0x380]
     signed_img = hdr_signed + payload
     signed_len = len(signed_img) & 0xffff
     if not img_rsa_verify(signed_img[:signed_len], sig, e, n):
         return None

     return payload

… sì, la verifica RSA (RSA PSS w/SHA256) esegue il troncamento a 16 bit durante il calcolo della dimensione del payload.

Ciò significa che se esistono immagini (inclusi, ad esempio, ARM TrustZone TA) più grandi di 0xffff byte, per superare il controllo della firma sull’hardware FIGO, devono essere firmate in modo tale che probabilmente una grande percentuale del payload possa effettivamente essere liberamente modificato senza che il controllo della firma fallisca.

Ci sono davvero tali immagini nel firmware PSVR.

Appendice

Alcune note più vecchie che potrebbero essere utili.

Filesystem

ParteNomeChiave XTSNote
p1ftsArchiviazione valore-chiave in stile Android per l'ambiente.
p2factory_settingfactory_setting_key.bin Archivi chiavi globali e per dispositivo, tutti crittografati per dispositivo.
p3tzk_recoveryImmagine FIGO (recupero TZK e TZBP).
p4recoveryImmagine FIGO (ripristino bootimg).
p5tzk_normalImmagine FIGO (TZK e TZBP).
p6bootImmagine FIGO (boot).
p7rootfsrootfs_key.bin
p8firmwarefirmware_key.binAlbero del firmware linux vuoto.
p9trusted_storageta_storage_key.binInutilizzato, ma sembra avere spazzatura nei settori non allocati.
p10rootfs_2rootfs_key.binrootfs fallback.
p11boot_2Immagini FIGO (boot fallback).
p12cachecache_key.binVuoto.
p13tzk_normal_2Immagini FIGO (tzk_normal fallback).
p14firmware_2firmware_key.binfallback del firmware linux.
p15userdatauserdata_key.binContiene log di runtime persistenti.
p16factory_fwfactory_fw_key.bin Archivia i file DFU per il provisioning.

Fonte: twitter.com

Articolo precedente[Scena PS3] Rilasciato Custom Firmware 4.89.1 Evilnat Cobra 8.3 [CEX/noBT/noBD]
Articolo successivo[Scena PS3] Rilasciato firmware DualBoot 4.89 e DualBoot Hybrid 4.89
Sono Nato nel 1980 e da diversi anni coltivo la passione per tutto ciò che riguarda il mondo dei videogiochi, ho vissuto l'era degli home computer con il Commodore 64 e l'Amiga 500, da sempre appassionato di giochi di ruolo e di titoli di avventura.