
Une analyse de la version de Sleirsgoevy de l'implémentation d'exploit de CVE-2018-4386 par Fire30 appelée Bad_Hoist
[!Note] Informations générales sur la PS4 :
La console PlayStation 4 est équipée d'un processeur AMD x86-64 personnalisé (8 cœurs), son système d'exploitation Orbis est basé sur FreeBSD (v9.0) avec également des parties de NetBSD. Elle inclut également une grande variété de logiciels open source supplémentaires, tels que Mono VM et WebKit.
Le navigateur Internet utilisé par la PS4 est en réalité construit avec le projet WebKit open source. Il s'agit du moteur de rendu open source qui affiche les pages web dans les navigateurs pour iOS, Wii U, 3DS, PS Vita et la PS4.
Le navigateur Internet de la PS4 se compose en réalité de 2 processus distincts. Celui que nous détournons pour exécuter du code est le processus principal de WebKit (qui gère par exemple l'analyse HTML et CSS, le décodage des images et l'exécution de JavaScript). L'autre gère tout le reste : affichage graphique, réception des entrées de la manette, gestion de l'historique et des marque-pages, etc.
Le navigateur WebKit de la PS4 utilise plusieurs allocateurs de tas, chacun servant différents composants. Voici les suivants :
Le cœur de CVE-2018-4386 est un défaut logique dans le moteur JavaScriptCore (JSC) de WebKit (v605.1.15), qui est la version utilisée dans le firmware PS4 6.XX.
Le défaut réside dans la fonction BytecodeGenerator::hoistSloppyModeFunctionIfNecessary et implique une gestion incorrecte du hoisting de variables en mode sloppy de JavaScript, en particulier dans les boucles for-in.
Composant vulnérable (ForInContext) :
Ce que nous ciblons principalement est ForInContext. Il s'agit d'une structure interne utilisée par JavaScriptCore pour gérer l'état d'une boucle for-in, en suivant la variable d'itération courante et l'ensemble des propriétés énumérées.
Lorsqu'une déclaration de fonction est hoisted à l'intérieur d'une boucle for-in, le moteur devrait invalider l'objet ForInContext associé si la variable d'itération est écrasée. Cependant, à cause du bogue, cette invalidation n'a pas lieu. Cela permet de remplacer la variable d'itération par un objet arbitraire. Malgré cela, le moteur continue de traiter la variable comme un nom de propriété de type chaîne.
Lorsque le gestionnaire de bytecode op_get_direct_pname est invoqué plus tard, il utilise la variable d'itération directement comme un objet chaîne, sans vérification de type.
En passant un objet contrefait au lieu d'une chaîne, menant à une confusion de type, nous pouvons exploiter cela de manière à provoquer une corruption mémoire, et même des primitives d'exploitation utiles telles que addrof, fakeobj, et lecture/écriture arbitraire.
Identifiant de structure : Chaque objet dans JavaScriptCore, y compris les représentations internes comme WTF::StringImpl, possède un identifiant de structure (ou type tag) qui indique au moteur de quel type d'objet il s'agit et comment interpréter ses champs.
Confusion de type : Notre exploit abuse du bogue CVE-2018-4386 pour qu'un objet JavaScript soit interprété comme un StringImpl. C'est le but de la fonction create_impl(), qui retourne un objet WTF::StringImpl en confusion de type, qui peut ensuite être passé à la fonction trigger(), comme l'objet arbitraire dont nous avons parlé plus tôt dans la partie mécanisme de vulnérabilité de cette analyse.
Cependant, pour que cela fonctionne, la disposition mémoire et l'identifiant de structure doivent être « suffisamment proches » de ce que le moteur attend pour un véritable objet chaîne.
JSString::toIdentifier() en interne ?Il existe une méthode dans le moteur JavaScriptCore de WebKit du navigateur Internet de la PS4 nommée JSString::toIdentifier(). Cette méthode convertit un objet chaîne JavaScript (JSString) en une représentation interne d'identifiant.
Cet identifiant est utilisé dans tout le moteur pour comparer, stocker et rechercher efficacement les noms de propriétés, les noms de variables et autres chaînes qui doivent être référencées rapidement et fréquemment par le moteur JavaScript.
Il vérifie que l'objet est une chaîne valide et que son identifiant de structure correspond à ce que le moteur attend pour un objet chaîne. Si la chaîne est une rope (une concaténation de chaînes), il peut l'aplatir avant de la convertir. Ensuite, il récupère soit un identifiant existant pour la chaîne, soit en crée un nouveau s'il n'existe pas.
Cet identifiant est ensuite utilisé en interne pour des recherches rapides de propriétés et de variables.
JSString::toIdentifier()Lorsque l'exploit entre dans la boucle for qui itère 1024 fois, chaque itération crée un nouvel objet WTF::StringImpl en confusion de type avec 32 nouveaux identifiants de structure retournés par la fonction create_impl(). Lorsque cet objet confus est utilisé et passé à trigger() comme objet arbitraire, JSC appelle JSString::toIdentifier() dessus.
JSString::toIdentifier() vérifie certains bits dans l'identifiant de structure pour confirmer que l'objet est une chaîne valide ou peut être traité comme tel. En générant de nombreux objets avec différentes dispositions et identifiants de structure, l'exploit augmente les chances qu'au moins un ait un identifiant de structure qui passe les vérifications internes de JSString::toIdentifier().