
Un writeup per la versione di Sleirsgoevy dell'implementazione dell'exploit CVE-2018-4386 di Fire30, chiamata Bad_Hoist.
[!Note] Informazioni di base sulla PS4:
La console PlayStation 4 è dotata di una CPU AMD x86-64 personalizzata (8 core), il suo sistema operativo Orbis è basato su FreeBSD (v9.0) con parti di NetBSD. Include inoltre un'ampia varietà di software open source aggiuntivo, come Mono VM e WebKit.
Il browser Internet utilizzato dalla PS4 è in realtà costruito con il progetto open source WebKit. Questo è il motore di layout open source che renderizza le pagine web nei browser per iOS, Wii U, 3DS, PS Vita e PS4.
Il browser Internet della PS4 è in realtà composto da 2 processi separati. Quello che dirottiamo per l'esecuzione di codice è il WebKit Core Process (che gestisce, ad esempio, il parsing di HTML e CSS, la decodifica delle immagini e l'esecuzione di JavaScript). L'altro gestisce tutto il resto: visualizzazione della grafica, ricezione degli input del controller, gestione di cronologia e segnalibri, ecc.
Il browser WebKit della PS4 impiega molteplici allocatori di heap, ciascuno al servizio di componenti diversi. Questi sono i seguenti:
Il cuore di CVE-2018-4386 è un difetto logico nel motore JavaScriptCore (JSC) di WebKit (v605.1.15), la versione utilizzata nel firmware 6.XX della PS4. Il difetto risiede nella funzione BytecodeGenerator::hoistSloppyModeFunctionIfNecessary e comporta una gestione impropria dell'hoisting delle variabili in JavaScript in modalità sloppy, in particolare all'interno dei cicli for-in.
Componente vulnerabile (ForInContext):
La cosa che prendiamo di mira principalmente è ForInContext. Questa è una struttura interna utilizzata da JavaScriptCore per gestire lo stato di un ciclo for-in, tenendo traccia della variabile di iterazione corrente e dell'insieme delle proprietà enumerate.
Quando una dichiarazione di funzione viene issata (hoisted) all'interno di un ciclo for-in, il motore dovrebbe invalidare l'oggetto ForInContext associato se la variabile di iterazione viene sovrascritta. Tuttavia, a causa del bug, questa invalidazione non avviene. Ciò consente di sostituire la variabile di iterazione con un oggetto arbitrario. Nonostante ciò, il motore continua a trattare la variabile come un nome di proprietà stringa.
Quando in seguito viene invocato il gestore bytecode op_get_direct_pname, esso utilizza la variabile di iterazione direttamente come un oggetto stringa, senza controllo di tipo.
Passando un oggetto appositamente costruito invece di una stringa, provocando una type confusion, siamo in grado di sfruttare questo bug per ottenere corruzione della memoria e persino primitive di exploitation utili come addrof, fakeobj e lettura/scrittura arbitraria.
Structure ID: Ogni oggetto in JavaScriptCore, incluse le rappresentazioni interne come WTF::StringImpl, ha uno structure ID (o tag di tipo) che indica al motore che tipo di oggetto è e come interpretarne i campi.
Type Confusion: Il nostro exploit abusa del bug CVE-2018-4386 per far interpretare un oggetto JavaScript come un StringImpl. Questo è lo scopo della funzione create_impl(), che restituisce un oggetto confuso dal punto di vista del tipo (Type-confused) di tipo WTF::StringImpl, che può poi essere passato alla funzione trigger() come l'oggetto arbitrario di cui abbiamo parlato prima nella parte relativa al meccanismo della vulnerabilità di questo writeup.
Tuttavia, perché questo funzioni, il layout di memoria e lo structure ID devono essere "sufficientemente vicini" a ciò che il motore si aspetta da un vero oggetto stringa.
JSString::toIdentifier() internamente?Esiste un metodo nel motore JavaScriptCore WebKit del browser Internet della PS4 il cui nome è JSString::toIdentifier(). Questo metodo converte un oggetto stringa JavaScript (JSString) in una rappresentazione Identifier interna.
Questo Identifier viene utilizzato in tutto il motore per confrontare, memorizzare e cercare in modo efficiente nomi di proprietà, nomi di variabili e altre stringhe a cui il motore JavaScript deve fare riferimento rapidamente e frequentemente.
Verifica che l'oggetto sia una stringa valida e che il suo structure ID corrisponda a ciò che il motore si aspetta per un oggetto stringa. Se la stringa è una rope (una concatenazione di stringhe), può appiattirla prima di convertirla. Quindi recupera un Identifier esistente per la stringa oppure ne crea uno nuovo se non esiste.
Questo Identifier viene quindi utilizzato internamente per ricerche rapide di proprietà e variabili.
JSString::toIdentifier()Quando l'exploit entra nel ciclo for che itera 1024 volte, ogni iterazione crea un nuovo oggetto WTF::StringImpl confuso dal punto di vista del tipo, con 32 nuovi Structure ID restituiti dalla funzione create_impl(). Quando questo oggetto confuso viene utilizzato e passato a trigger() come oggetto arbitrario, JSC chiama JSString::toIdentifier() su di esso.
JSString::toIdentifier() controlla alcuni bit nello structure ID per confermare che l'oggetto sia una stringa valida o che possa essere trattato come tale. Generando molti oggetti con layout e structure ID diversi, l'exploit aumenta le probabilità che almeno uno abbia uno structure ID che superi i controlli interni di JSString::toIdentifier().