VOUS AVEZ OBTENU UN TROPHÉE - PS4 JAILBREAKÉ EXPLOITATION D'UNE VULNÉRABILITÉ WEBKIT SUR LA PLAYSTATION 4 - SSTIC
←
→
Transcription du contenu de la page
Si votre navigateur ne rend pas la page correctement, lisez s'il vous plaît le contenu de la page ci-dessous
Vous avez obtenu un trophée - PS4 jailbreaké Exploitation d’une vulnérabilité WebKit sur la PlayStation 4 2 Juin 2021 Synacktiv Quentin Meffre Mehdi Talbi
Table des matières 1 Introduction & motivation 2 État de l’art 3 Allocateur standard 6 Conclusion 4 Présentation de la vulnérabilité 5 Exploitation de la vulnérabilité
Qui sommes-nous ? Mehdi Talbi @abu_y0ussef Quentin Meffre @0xdagger Chercheur en sécurité @Synacktiv Recherche académique (dans une vie Chercheur en sécurité @Synacktiv antérieure) Recherche de vulnérabilités & Recherche de vulnérabilités & exploitation exploitation 3/48
Introduction Motivation Communauté active sur le hacking de consoles de jeux … Mais très peu d’exploits publics À quel point est difficile l’exploitation de vulnérabilités sur la PS4 ? Objectif Exploitation pas-à-pas d’une vulnérabilité 0-day dans WebKit sur la PS4 4/48
Table des matières 1 Introduction & motivation 2 État de l’art 3 Allocateur standard 6 Conclusion 4 Présentation de la vulnérabilité 5 Exploitation de la vulnérabilité
Surface d’attaque Browser Gamesaves Userland USB WIFI BLUETOOTH FreeBSD Kernel 6/48
Chaine d’exploitation Webkit Exploit Kernel Exploit Attaque du navigateur Navigateur basé sur WebKit Sandbox Pas de JIT Absence de certaines protections Gigacage Randomisation des StructureID ASLR : partiel ? Faible ? Absence de capacité de débogage 7/48
Exploits WebKit CVE-2018-4386 Identifié par Lokihardt (de Projet Zero) Exploité par @Fire30_ Dernier exploit public en date (“bad-hoist”) Primitive de lecture/écriture arbitraire Firmwares 6.00-6.72 CVE-2018-4441 Également identifié par Lokihardt Exploité par @SpecterDev Primitive de lecture/écriture arbitraire Firmwares 6.00-6.20 Autres exploits … Pour les firmwares plus anciens (< 6.xx) Exploits de @qwertyoruiopz, @SpecterDev, @CTurt, … 8/48
Exploits Kernel CVE-2020-9892 Identifié par @theflow0 Double Free dans la pile IPv6 Firmwares
Table des matières 1 Introduction & motivation 2 État de l’art 3 Allocateur standard 6 Conclusion 4 Présentation de la vulnérabilité 5 Exploitation de la vulnérabilité
Allocateurs WebKit Plusieurs allocateurs FastMalloc : allocateur standard IsoHeap : utilisé par le moteur DOM Tri des allocations en fonction du type GC (Garbage Collector) : utilisé par le moteur JavaScript IsoSubspace : utilisé par le moteur JavaScript Trie des allocations en fonction de leur taille GigaCage : protections contre les lectures/écritures OOB sur certains objets 11/48
Allocateur standard Présentation Heap constitué de chunks Chunk divisé en pages (4 kB) Page subdivisée en lignes (256 octets) Ligne : dessert des allocations d’objets de mêmes tailles Obj N Line N Obj 2 Page N Obj 1 Chunk N Line 2 Line 1 ... Page 2 Chunk 1 Page 1 12/48
Allocation (1/2) Allocations via le chemin rapide (fast path) Allocateur de type “bump-pointer” --m_remaining; char* result = m_ptr; m_ptr += m_size; return result; Allocations via le chemin lent (slow path) Réapprovisionnement de l’allocateur 1 À partir du cache BumpRangeCache (fast path) 2 À partir de l’allocation d’une nouvelle page (slow path) 13/48
Allocation (2/2) Réapprovisionnement de l’allocateur Free Line ... BumpRangeCache Allocation d’une nouvelle page 1 Depuis le cache CacheLine Free Line 2 Depuis le dernier chunk alloué Alimenter l’allocateur avec les Line lignes libres & contiguës Alimenter le cache avec les lignes Free Line libres restantes BumpAllocator Free Line released obj allocated obj released obj Page 14/48
Déallocation Déallocation Les objets libérés ne sont pas immédiatement mis à disposition → placés dans un vecteur dédié (m_objectLog). Traitement des objets du vecteur m_objectLog : Lorsqu’il atteint sa capacité maximale (512) Lors du réapprovisionnement de l’allocateur Chunks / Pages / Lignes disposent d’un compteur de référence (refcount) Chunks / Pages / Lignes libérés SI refcount == 0 15/48
Table des matières 1 Introduction & motivation 2 État de l’art 3 Allocateur standard 6 Conclusion 4 Présentation de la vulnérabilité 5 Exploitation de la vulnérabilité
Présentation Vulnérabilité Présente dans le moteur DOM de WebKit Trouvée via fuzzing Impacte tous les firmwares < 8.00 de la PS4 … et de la PS Vita aussi Remontée à Sony via leur programme de Bug Bounty (2500$) Corrigée le 11 Sept. 2020 17/48
Code vulnérable Code vulnérable Vulnérabilité présente dans la méthode WebCore::ValidationMessage::buildBubbleTree La fonction updateLayout met à jour la disposition de la page : Exécution de callbacks JS enregistrées par l’utilisateur Possibilité de destruction de l’instance ValidationMessage → UAF Erreur dans la définition d’un WeakPtr void ValidationMessage::buildBubbleTree() { /* ... */ auto weakElement = makeWeakPtr(*m_element); document.updateLayout(); // [1] call user registered JS events if (!weakElement || !m_element->renderer()) return; adjustBubblePosition(m_element->renderer()->absoluteBoundingBoxRect(), m_bubble.get()); /* ... */ } 18/48
Le bon et le mauvais correctif (1/2) Le mauvais correctif void ValidationMessage::buildBubbleTree() { /* ... */ + + auto weakElement = makeWeakPtr(*m_element); + document.updateLayout(); + if (!weakElement || !m_element->renderer()) + return; adjustBubblePosition(m_element->renderer()->absoluteBoundingBoxRect(), m_bubble.get()); /* ... */ } 19/48
Le bon et le mauvais correctif (2/2) Le bon correctif Ne pas permettre des mises à jour de la disposition de la page dans buildBubbleTree void ValidationMessage::buildBubbleTree() { /* ... */ - - auto weakElement = makeWeakPtr(*m_element); - - document.updateLayout(); - - if (!weakElement || !m_element->renderer()) - return; - - adjustBubblePosition(m_element->renderer()->absoluteBoundingBoxRect(), m_bubble.get()); /* ... */ + if (!document.view()) + return; + document.view()->queuePostLayoutCallback([weakThis = makeWeakPtr(*this)] { + if (!weakThis) + return; + weakThis->adjustBubblePosition(); + }); } 20/48
Chemin vulnérable onfocus html input reportValidity Instantiate ValidationMessage JS Callback Layout Update 0s buildBubbleTree Free ValidationMessage UAF ValidationMessage 5s deleteBubbleTree Destroy ValidationMessage 21/48
Tentatives de déclenchement du bug (1/2) Tentative initiale 1 Enregistrer un événement JS (e.g. onfocus) sur un champ de texte HTML 2 Instancier l’objet ValidationMessage → Déclenchement d’un minuteur pour exécuter buildBubbleTree → Exécution de la callback JS 3 Détruire l’instance ValidationMessage dans la callback JS 4 Pas de crashs !! reportValidity positionne le focus sur l’élément HTML cible Exécution prématurée de la callbask JS PS4 exploit https://pwnme.org/ onfocus input onload input.setCustomValidity("pwn"); input.reportValidity(); document.body.delete(input) ; input.autocus = true; ValidationMessage Instanciation ValidationMessage destruction 22/48
Tentatives de déclenchement du bug (2/2) Solution 1 Enregistrer un événement JS handler1 sur un champ HTML input1 2 Instancier l’objet ValidationMessage (sur input1) Le focus est positionné sur input1 → handler1 est exécuté handler1 positionne le focus ailleurs (input2) 3 Définir un nouveau handler handler2 sur input1 4 Détruire l’instance ValidationMessage dans le handler2 PS4 exploit https://pwnme.org/ onfocus (1) input1 onfocus (2) onload input2 input1.setCustomValidity("pwn"); input1.reportValidity(); input1.setAttribute("onfocus","handle2()"); input1.autocus = true; function handle1() { function handle2() { ValidationMessage Instanciation input2.focus(); document.body.delete(input) ; } } ValidationMessage Destruction 23/48
Crash ! 24/48
Débogage Problème Absence de capacité de débogage Solution 1 - Installation d’un environnement similaire Installer un FreeBSD Compiler les sources WebKit récupérées depuis doc.dl.playstation.net → Utile Mais un exploit fonctionnel sur notre environnement n’implique pas un portage assuré sur la PS4 Solution 2 - Débogage d’une 0-day avec une 1-day Utilier l’exploit “bad-hoist” (+) Primitives de lecture/écriture (+) Primitives addrof/fakeobj (-) Fonctionne uniquement sur les firmwares 6.xx (-) Peut influencer notre stratégie de shaping de la mémoire (-) Peu fiable 25/48
Anatomie de l’objet vulnérable Objet ValidationMessage Instancié par reportValidity() (via fastMalloc()) Accédé par buildBubbleTree() Détruit par deleteBubbleTree() m_messageBody m_messageHeading HTMLElement m_bubble m_timer m_message instantiated after layout update HTMLElement m_element accessed after layout update ValidationMessage 26/48
Survivre au crash initial (1/3) Situation après destruction de l’objet vulnérable Déréférencement d’un pointeur NULL (m_bubble) → crash du navigateur Situation : Inconfortable m_messageBody m_messageHeading m_bubble m_timer m_message Set to NULL m_element Deleted. Reference not cleared ValidationMessage Exploitabilité Nécessite 1 Un leak Mémoire, ou bien 2 … un bypass de l’ASLR 27/48
Survivre au crash initial (2/3) Bypass de l’ASLR Heap Spray → adresses prédictibles Nécessite une connaissance préalable du mapping mémoire Survivre au crash Allocation de plusieurs milliers objets HTMLElement (e.g. HTMLTextAreaElement) UAF de l’objet ValidationMessage HTMLTextAreaElement HTMLTextAreaElement m_messageBody m_messageHeading ... m_bubble m_timer m_message m_element HTMLTextAreaElement ValidationMessage 28/48
Survivre au crash initial (3/3) Épilogue de la fonction vulnérable void ValidationMessage::deleteBubbleTree() { if (m_bubble) { m_messageHeading = nullptr; m_messageBody = nullptr; m_element->userAgentShadowRoot()->removeChild(*m_bubble); m_bubble = nullptr; } m_message = String(); } Ré-évaluation de la situation ValidationMessage de nouveau détruit Surcharge de l’opérateur “=” Affectation de nullptr sur un objet “refcounté” → décrément du compteur de référence Décrément d’un compteur de références sur plusieurs objets contrôlés UAF → Décrément arbitraire Exploitable 29/48
Table des matières 1 Introduction & motivation 2 État de l’art 3 Allocateur standard 6 Conclusion 4 Présentation de la vulnérabilité 5 Exploitation de la vulnérabilité
Réutiliser l’objet cible (1/3) Contrôler le tas 1 Allouer N/2 d’objets dit O sizeof(O) == sizeof(ValidationMessage) 2 Instancier un objet ValidationMessage 3 Allouer N/2 d’objets dit O More Obj. O ... Obj. O Obj. O Obj. O Obj. O Line ValidationMessage Obj. O Obj. O Obj. O Obj. O More Obj. O ... 31/48
Réutiliser l’objet cible (2/3) Contrôler le tas 1 Libérer les objets dit O se trouvant autour de l’objet alloué ValidationMessage 2 Libérer l’objet ValidationMessage La ligne associée est libérée La page associée peut être ajoutée dans le cache ... Line Target ... 32/48
Réutiliser l’objet cible (3/3) Contrôler le tas Allouer de nouveaux objets pour réutiliser l’objet ValidationMessage libéré La taille de l’objet alloué doit être égale à celle de l’objet ValidationMessage Le tampon de données d’un objet ArrayBuffer est un bon candidat More ArrayBuffer' Contents ... ArrayBuffer' Contents m_messageBody ValidationMessage m_messageHeading ArrayBuffer' Contents m_bubble m_timer m_message m_element ArrayBuffer' Contents More ArrayBuffer Contents ... 33/48
Fuite de données initiale Fuite de données m_messageBody, m_messageHeading et m_timer sont alloués après avoir réutilisé l’objet ValidationMessage m_timer est alloué par FastMalloc Permet d’inférer l’adresse d’objets alloués dans ce tas m_messageBody m_messageHeading m_bubble m_timer m_message m_element ValidationMessage 34/48
Primitive de décrément arbitraire Exploitation Corrompre le pointeur m_messageHeading Cibler les attributs length des objets permettant de manipuler des données Confondre la taille d’un objet avec le compteur de références de l’objet m_messageHeading Aligner l’objet visé de manière à élargir son attribut length et non le réduire L’objectif est d’obtenir une primitive de lecture et écriture relative m_messageBody m_messageHeading m_bubble m_timer Data m_message Length 00 00 00 XX m_element ValidationMessage refcount field 35/48
Stratégie d’exploitation ASLR Bypass Obj. Reuse Relative Read Relative R/W Primitive Primitive Arbitrary R/W Primitive Code Execution 36/48
Primitive de lecture relative (1/3) Objectif Obtenir l’adresse d’un objet JSArrayBufferView alloué par le Garbage Collector Comment ? More StringImpl ... 1 Allouer beaucoup d’objets StringImpl sur data le tas StringImpl Avant/après l’allocation de Timer flags sizeof(Timer) == data ptr sizeof(StringImpl) length refcount 2 Utiliser le décrément arbitraire sur l’attribut length d’un objet StringImpl Timer 3 Il est possible de lire au-delà des limites fixées par l’objet StringImpl More StringImpl ... 37/48
Primitive de lecture relative (2/3) Fuiter l’adresse d’un objet JSArrayBufferView (1/2) Les objets du DOM et les objets JavaScript utilisent deux allocateurs différents On ne peut pas atteindre un JSObject à partir de notre primitive de lecture relative La méthode JavaScript native Object.defineProperties alloue deux objets qui permettent de stocker des références vers des JSObject Les objets Vector et MarkedArgumentBuffer sont nos cibles static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties) { Vector descriptors; MarkedArgumentBuffer markBuffer; /* ... */ JSValue prop = properties->get(exec, propertyNames[i]); /* ... */ PropertyDescriptor descriptor; toPropertyDescriptor(exec, prop, descriptor); /* ... */ descriptors.append(descriptor); // [1] Stocke une JSValue sur le tas de FastMalloc /* ... */ markBuffer.append(descriptor.value()); // [2] Stocke une seconde fois une JSValue sur le tas de FastMalloc } 38/48
Primitive de lecture relative (3/3) Fuiter l’adresse d’un objet JSArrayBufferView (2/2) 1 1 Allouer plusieurs objets JSArrayBufferView JSArrayBufferView 2 Ajouter des références vers ces objets à l’aide de la méthode ... Object.defineProperties Les deux objets stockant les références vers JSArrayBufferView ptr les JSObject sont libérés à la fin de Object.defineProperties Il ne faut pas réutiliser ces allocations sous ... peine de perdre nos références 3 Utiliser la primitive de lecture relative pour data StringImpl chercher ces références flags L’objet JSArrayBufferView doit être alloué data ptr après l’objet utilisé par la primitive de lecture length relative afin de pouvoir lire son contenu refcount 1. Merci @qwertyoruiopz pour la technique utilisant Object.defineProperties 39/48
Primitive de lecture/écriture relative Comment ? 1 Utiliser à nouveau la primitive de décrément arbitraire 2 Corrompre la taille d’un JSArrayBufferView à l’aide de l’adresse obtenue précédemment 3 L’objet JSArrayBufferView corrompu permet de lire et écrire relativement en mémoire m_butterfly m_mode m_length m_vector m_structure 40/48
Primitive de lecture/écriture arbitraire Comment ? Primitive de lecture et écriture relative depuis un premier objet JSArrayBufferView Corrompre le pointeur du tampon de données d’un second JSArrayBufferView Primitive de lecture et écriture arbitraire à partir du second JSArrayBufferView m_butterfly m_butterfly m_mode m_length m_mode m_length JSArrayBufferView 1 JSArrayBufferView 2 m_vector m_vector m_structure m_structure ... ArrayBuffer 41/48
Exécution de code Comment ? La PS4 ne permet pas de mapper une page RWX dans le contexte du processus de rendu HTML Nous pouvons contrôler le registre d’instruction RIP Nous avons l’adresse d’un objet HTMLElement Corrompre la vtable d’un HTMLElement Appeler la méthode JavaScript associée afin de faire un appel utilisant la vtable corrompue L’étape suivante peut-être implémentée à l’aide d’une chaine de ROP/JOP Méthode couramment utilisée dans le jailbreak PS4 42/48
Démonstration 2 2. image credit : TheRegisti 43/48
Porter l’exploit sur la version 7 (1/3) Problématique L’adresse utilisée pour contourner l’ASLR sur la version 6 ne fonctionne pas sur la version 7 de la PS4 Le navigateur crash lors de l’étape de la réutilisation de l’objet libéré ValidationMessage Des connaissances préalables sur le mapping mémoire sont nécessaires 44/48
Porter l’exploit sur la version 7 (2/3) Solution Bruteforcer l’ASLR Deviner l’adresse de l’un des HTMLElement alloué Brancher un Raspberry Pi (détecté comme un clavier) Appuyer sur la touche “Entrée” à une fréquence régulière (5s) Redémarrer le navigateur après un crash Pas de résultats :-( 45/48
Porter l’exploit sur la version 7 (3/3) … Quelques jours après la publication de l’exploit Publication d’adresses valides pour la version 7.02 Adaptation de l’exploit (modifications mineures) Enchainement avec l’exploit kernel → Premier Jailbreak de la PS4 en version 7 46/48
Table des matières 1 Introduction & motivation 2 État de l’art 3 Allocateur standard 6 Conclusion 4 Présentation de la vulnérabilité 5 Exploitation de la vulnérabilité
Conclusion Conclusion Exploitation d’une vulnérabilité 0-day WebKit sur la PS4 L’exploit est disponible sur le github de Synacktiv Contribution au premier jailbreak sur le firmware 7.xx Perspectives 48/48
AVEZ-VOUS DES QUESTIONS ? MERCI DE VOTRE ATTENTION
Vous pouvez aussi lire