
Dans le cadre de cet article, nous considérerons de manière suffisamment détaillée le processus d'écriture d'un exploit pour une vulnérabilité dans Microsoft Edge, avec la sortie ultérieure du sandbox. Si vous souhaitez savoir à quoi ressemble ce processus, bienvenue sous cat!
Présentation
Lors du dernier Pwn2Own 2019
à Montréal, dans la catégorie des navigateurs, un exploit pour pirater Microsoft Edge
été démontré . Deux vulnérabilités ont été utilisées pour cela: double free
dans le moteur de rendu et une vulnérabilité logique pour quitter le bac à sable. Ces deux vulnérabilités ont récemment été corrigées et affectées au CVE
correspondant: CVE-2019-0940
et CVE-2019-0938
. Vous pouvez en savoir plus sur les vulnérabilités dans le blog: Pwn2Own 2019: Microsoft Edge Renderer Exploitation (CVE-2019-0940). Partie 1 et Pwn2Own 2019: Microsoft Eedge Sandbox Escape (CVE-2019-0938). 2e partie .
Dans le cadre de notre article, nous voulons montrer le processus d'écriture d'un tel exploit et combien de temps et de ressources sont nécessaires pour cela en utilisant l'exemple de Microsoft Edge
sur Windows 10
utilisant CVE-2017-0240
et CVE-2016-3309
. L'une des différences sera que si l'exploit démontré sur Pwn2Own
utilisé une vulnérabilité logique pour quitter le bac à sable, alors dans notre scénario, la vulnérabilité dans le noyau Windows 10
sera utilisée pour quitter le bac à sable. Comme Microsoft
montrent les correctifs de Microsoft
, il y a beaucoup plus de vulnérabilités dans le noyau que de vulnérabilités dans l'implémentation du sandbox. En conséquence, une telle chaîne de vulnérabilités est beaucoup plus susceptible d'être rencontrée, et il sera utile de la connaître pour les employés du SI dans les entreprises.
Données source
Cet article couvrira le processus d'écriture d'un exploit d'une journée pour le navigateur Microsoft Edge
. CVE-2017-0240
sera exploité. La première étape de l'opération sera effectuée sur la base de matériaux provenant de la source [1], nous obtiendrons une primitive de arbitrary address read/write
, et nous nous familiariserons également avec diverses techniques qui peuvent être utiles lors de l'exploitation de telles vulnérabilités. Ensuite, nous vous présenterons l'outil pwn.js
, qui vous aidera à obtenir un appel à des fonctions arbitraires basées sur la lecture et l'écriture arbitraires, et examinera également diverses mitigations
et façons de les contourner. À la dernière étape, la vulnérabilité du noyau Windows CVE-2016-3309
sera exploitée pour augmenter les privilèges, contourner les restrictions AppContainer
et obtenir un contrôle complet sur la machine attaquée.
L'opération sera effectuée sur le stand avec Microsoft Windows 10 Pro 1703 (10.0.15063)
et le navigateur Microsoft Edge (40.15063.0.0)
.
Étape 1. Obtention d'une primitive de arbitrary address read/write
Description de la vulnérabilité et obtention d' OOB
Une vulnérabilité de type use-after-free
présente dans la méthode copyFromChannel de l'objet Audio Buffer .
AudioBuffer est l'interface d'un court élément audio situé en mémoire et créé à partir d'un fichier audio à l'aide de la méthode AudioContext.decodeAudioData (), ou à partir des données source à l'aide de la méthode AudioContext.createBuffer (). Les données audio placées dans AudioBuffer peuvent être lues dans AudioBufferSourceNode.
La présentation de The Advanced Exploitation of 64-bit Edge Browser Use-After-Free Vulnerability on Windows 10
fournit une analyse détaillée de la vulnérabilité et du correctif. Lorsque la méthode copyFromChannel
est copyFromChannel
, le contenu du canal de tampon audio est copié dans le tampon de destination
spécifié par le premier argument. La méthode accepte également le numéro de canal ( channelNumber
) et le décalage dans le tampon audio ( startInChannel
), à partir desquels la copie est nécessaire. Avant de copier directement les données vers la destination
dans la fonction CDOMAudioBuffer::Var_copyFromChannel
, le tampon de destination
est mis en cache (l'adresse et la taille du tampon sont stockées dans les variables de fonction locales sur la pile) et les valeurs d'objet channelNumber
et startInChannel
sont startInChannel
en type Int
, pour lequel la méthode valueOf
des objets convertis est appelée. La vulnérabilité est qu'un tampon mis en cache peut être libéré au moment de la conversion de type dans la méthode substituée de l'objet valueOf
. Pour la vérification, nous utilisons le code suivant:
Ce code utilise la technologie Web Workers
pour libérer le tampon. Après avoir créé un Worker
vide, nous pouvons lui envoyer un message en utilisant la méthode postMessage
. Le deuxième argument de transfer
facultatif de cette méthode prend un tableau d'objets Transferable
( ArrayBuffer
, MessagePost
ou ImageBitmap
), les droits sur l'objet seront transférés vers Worker
et l'objet ne sera plus disponible dans le contexte actuel, il peut donc être supprimé. Après cela, un appel à sleep
se produit - une fonction qui arrête temporairement l'exécution d'un programme (il est implémenté indépendamment). Cela est nécessaire pour que le système de collecte des ordures ( GC
, Garbage Collector
) parvienne à libérer le tampon, dont les droits ont été transférés.
Les travailleurs Web fournissent un moyen simple d'exécuter des scripts dans le thread d'arrière-plan. Le thread de travail peut effectuer des tâches sans interférer avec l'interface utilisateur. En outre, ils peuvent effectuer des E / S à l'aide de XMLHttpRequest (bien que les attributs responseXML et channel seront toujours nuls). Un Worker existant peut envoyer des messages JavaScript au code créateur via le gestionnaire d'événements spécifié par ce code (et vice versa).
En exécutant ce code dans Edge sous le débogueur, vous pouvez obtenir le plantage suivant.

Par conséquent, l'appel à copyFromChannel
tente de copier le contenu du tampon audio dans la zone de mémoire non allouée. Pour exploiter la vulnérabilité, il est nécessaire de réaliser l'allocation des objets dans cette zone mémoire. Dans ce cas, le segment de matrice est parfait.
Les tableaux dans Chakra
(le moteur JS
utilisé dans le navigateur Edge
) sont organisés comme suit: l'objet tableau a une taille fixe, les pointeurs vers les objets tableau (ou les valeurs, dans le cas d' IntArray
) sont stockés dans une zone de mémoire distincte - le segment, le pointeur vers lequel est contenu dans l'objet tableau. L'en-tête de segment contient diverses informations, notamment la taille du segment, qui correspond à la taille du tableau. La taille du tableau est également présente dans l'objet tableau lui-même. Schématiquement, cela ressemble à ceci:

Ainsi, si nous parvenons à sélectionner le segment de tableau dans l'espace précédemment libéré, nous pouvons alors remplacer l'en-tête du segment de tableau par le contenu du tampon audio. Pour ce faire, nous modifions le code ci-dessus en ajoutant les lignes suivantes après sleep(1000);
:
... arr = new Array(128); for(var i = 0; i < arr.length; i++) { arr[i] = new Array(0x3ff0); for(var j = 0; j < arr[i].length; j++) arr[i][j] = 0x30303030; } ...
La taille des matrices est sélectionnée de telle manière que la taille du segment de matrice occupe un segment de segment de mémoire entier (la partie minimale indivisible de la mémoire de segment de mémoire, dont la taille est 0x10000 octets). Exécutez ce code en spécifiant la fonction memcpy
comme point d'arrêt (il y aura de nombreux appels memcpy
, il est donc logique de s'arrêter d'abord à edgehtml!WebCore::AudioBufferData::copyBufferData
), dans lequel le crash s'est produit. On obtient le résultat suivant:

Super! Maintenant, nous pouvons remplacer l'en-tête du segment de tableau avec nos propres valeurs. Les valeurs les plus intéressantes dans ce cas sont la taille du tableau, dont le décalage peut être vu dans la capture d'écran ci-dessus. Modifiez le contenu du tampon audio comme suit:
... var t = audioBuf.getChannelData(0); var ta2 = new Uint32Array(t.buffer); ta2[0] = 0; ta2[1] = 0; ta2[2] = 0xffe0; ta2[3] = 0; ta2[4] = 0; ta2[5] = 0; ta2[6] = 0xfba6; ta2[7] = 0; ta2[8] = 0; ta2[9] = 0x7fffffff - 2; ta2[10] = 0x7fffffff; ta2[11] = 0; ta2[12] = 0; ta2[13] = 0; ta2[14] = 0x40404040; ta2[15] = 0x50505050; ...
Faites attention aux valeurs de ta2[14]
et ta2[15]
- elles se réfèrent déjà non pas à l'en-tête de segment, mais aux valeurs du tableau elles-mêmes. Avec cela, nous pouvons déterminer le tableau dont nous avons besoin dans le tableau arr
global comme suit:
... for(var i = 0; i < arr.length; i++) { if(arr[i][0] == 0x40404040 && arr[i][1] == 0x50505050) { alert('Target array idx: ' + i); target_idx = i; target_arr = arr[i]; break; } }
Si par conséquent un tableau a été trouvé, dont les deux premiers éléments ont été modifiés d'une certaine manière, alors tout va bien. Nous avons maintenant un tableau dont la taille de segment est plus grande qu'elle ne l'est réellement. Les tableaux restants peuvent être libérés.
Ici, il faut se rappeler que la taille du tableau existe en deux entités: dans l'objet tableau, où il est resté inchangé, et dans le segment du tableau, où nous l'avons augmenté. Il s'avère que la taille de l'objet tableau est ignorée si le code est exécuté en mode JIT
et a été optimisé. Ceci est facilement réalisé, par exemple, comme suit:
function arr_get(idx) { return target_arr[idx]; } function arr_set(idx, val) { target_arr[idx] = val; } for(var i = 0; i < 0x3ff0; i++) { arr_set(i, arr_get(i)); }
Après cela, en utilisant les fonctions arr_get
et arr_set
vous pouvez aller au-delà des limites du tableau ( OOB
, out-of-bound
).
Utiliser OOB
pour obtenir la primitive de lecture et d'écriture à une adresse arbitraire
Dans cette section, nous considérons une technique qui vous permet de lire et d'écrire sur une adresse arbitraire en utilisant OOB
. La méthode par laquelle nous obtenons ceci sera similaire à celle utilisée dans la source [1], mais il y aura également des changements importants.
Dans la version utilisée d' Edge
les blocs de mémoire pour le tas sont alloués séquentiellement, en raison de quoi, lors de l'allocation d'un grand nombre d'objets, tôt ou tard, ils se trouveront après le segment de tableau, au-delà duquel nous pouvons aller.
Tout d'abord, il nous donne la possibilité de lire un pointeur vers une table virtuelle de méthodes objet ( vftable
), afin que nous puissions contourner la randomisation de l'espace d'adressage du processus ( ASLR
). Mais l'accès à quels objets nous aidera à réaliser une lecture et une écriture arbitraires? Quelques objets DataView
sont parfaits pour cela.
Le DataView fournit une interface de bas niveau pour lire et écrire plusieurs types numériques dans un ArrayBuffer binaire, quel que soit l'ordre des octets de la plate-forme.
La structure interne du DataView
contient un pointeur vers un tampon. Pour lire et écrire dans une adresse arbitraire, par exemple, nous pouvons construire une chaîne de deux DataView
( dv1
et dv2
) comme suit: en tant dv1
tampon dv1
spécifiez l'adresse dv2
en accédant au tableau. Maintenant, en utilisant dv1
nous pouvons changer l'adresse du tampon dv2
, grâce à laquelle une lecture et une écriture arbitraires sont réalisées. Schématiquement, cela peut être représenté comme suit:

Pour utiliser cette méthode, vous devez apprendre à déterminer les adresses des objets en mémoire. Pour ce faire, la technique suivante existe: vous devez créer un nouveau Array
, utiliser OOB
pour enregistrer son vftable
et typeId
(les deux premiers champs 64 bits de la structure) et attribuer l'objet auquel l'adresse est intéressante au premier élément du tableau. Ensuite, vous devez restaurer les typeId
vftable
et typeId
précédemment enregistrées. Maintenant, le double mot junior et senior de l'adresse de l'objet peut être obtenu en se référant au premier et au deuxième élément du tableau. Le fait est que, par défaut, le nouveau tableau est IntArray
, et les valeurs à 4 octets du tableau sont stockées dans son segment telles quelles. Lors de l'affectation d'un objet à un tableau, le tableau est converti en un ObjectArray
et son segment est utilisé pour stocker les adresses des objets. La conversion change vftable
et typeId
. Par conséquent, si nous vftable
valeurs d'origine vftable
et typeId
, grâce aux éléments de ce tableau, nous pouvons accéder directement au segment. Le processus décrit schématiquement peut être représenté comme suit:

La fonction pour obtenir l'adresse ressemblera à ceci:
function addressOf(obj) { var hdr_backup = new Array(4);
Une question ouverte reste la création des objets nécessaires et leur recherche en utilisant OOB
. Comme mentionné précédemment, lors de l'allocation d'un grand nombre d'objets, tôt ou tard, ils commenceront à se démarquer après le segment de tableau, au-delà duquel nous pouvons aller. Pour trouver les objets nécessaires, il vous suffit de parcourir les index en dehors du tableau à la recherche des objets nécessaires. Parce que tous les objets du même type sont situés dans un segment du tas, vous pouvez optimiser la recherche et parcourir les segments du tas par incréments de 0x10000
, et vérifier uniquement les premières valeurs du début de chaque segment du tas. Pour identifier les objets, vous pouvez leur attribuer des valeurs uniques pour certains paramètres (par exemple, pour DataView
il peut s'agir de byteOffset
) ou, en utilisant les constantes déjà connues dans la structure de l'objet (par exemple, dans la version utilisée d' Edge
dans IntArray
, la valeur 0x10005
se trouve toujours à 0x18
).
En combinant toutes les techniques ci-dessus, vous pouvez lire et écrire sur une adresse arbitraire. Ci-dessous, une capture d'écran de la lecture des objets de mémoire DataView
.

Étape 2. Exécution de fonctions API arbitraires
À ce stade, nous avons pu lire et écrire sur une adresse arbitraire dans le processus d'affichage du contenu Edge
. Considérez les principales technologies qui devraient interférer avec le fonctionnement ultérieur de l'application et leurs solutions de contournement. Nous avons déjà écrit une courte série d'articles app specific security mitigation
( partie 1, introduction , partie 2, Internet Explorer et Edge , partie 3, Google Chrome ), mais gardez à l'esprit que les développeurs ne restent pas immobiles et ajoutent de nouveaux outils à leurs produits protection.
Randomisation de l'espace d'adressage ( ASLR
)
ASLR (randomisation de la disposition de l'espace d'adressage en anglais) est une technologie utilisée dans les systèmes d'exploitation qui modifie de manière aléatoire l'emplacement des structures de données importantes dans l'espace d'adressage du processus, à savoir: images de fichiers exécutables, bibliothèques chargées, tas et empiler.
Ci-dessus, nous avons appris à lire les adresses des tables de classes virtuelles, en les utilisant, nous pouvons facilement calculer l'adresse de base du module Chakra.dll
, donc ASLR
ne présente pas de problèmes pour une opération ultérieure.
Protection de l'exécution des données ( DEP
, NX
)
La prévention de l'exécution des données (DEP) est une fonctionnalité de sécurité intégrée à Linux, Mac OS X, Android et Windows qui empêche une application d'exécuter du code à partir d'une zone de mémoire marquée comme «données uniquement». Cela empêchera certaines attaques, qui, par exemple, enregistrent du code dans une telle zone en utilisant des dépassements de tampon.
Une façon de contourner cette protection consiste à appeler VirtualAlloc
aide de chaînes ROP
. Mais dans le cas d' Edge
cette méthode ne fonctionnera pas en raison de l' ACG
(voir ci-dessous).
Control Flow Guard ( CFG
)
CFG
est un mécanisme de protection visant à compliquer le processus d'exploitation des vulnérabilités binaires dans les applications utilisateur et en mode noyau. Le travail de ce mécanisme consiste à valider les appels indirects, ce qui empêche un attaquant d'intercepter le thread d'exécution (par exemple, en écrasant le tableau des fonctions virtuelles)
Cette technologie contrôle uniquement les appels indirects, par exemple, les appels de méthode à partir de la table virtuelle des fonctions d'objet. Les adresses de retour sur la pile ne sont pas contrôlées, et cela peut être utilisé pour créer des chaînes ROP
. L'utilisation future des chaînes ROP/JOP/COP
peut être entravée par Intel
nouvelle technologie d' Intel
technologie de Control-flow Enforcement Technology
( CET
). Cette technologie se compose de deux parties:
Shadow Stack
(shadow stack) - utilisé pour contrôler les adresses de retour et protège contre les chaînes ROP
;Indirect Branch Tracking
est une méthode de protection contre les chaînes JOP/COP
. Il s'agit d'une nouvelle instruction ENDBRANCH
, qui marque toutes les adresses de transition valides pour call
instructions call
et jmp
.
Garde de code arbitraire ( ACG
)
ACG
est une technologie qui empêche la génération de code dynamique (il est interdit d'allouer des zones de mémoire VirtaulAlloc
aide de VirtaulAlloc
) et ses modifications (il est impossible de VirtaulAlloc
zone de mémoire disponible en exécutable)
Cette protection, comme CFG
, n'empêche pas l'utilisation de chaînes ROP
.
Isolement AppContainer
AppContainer est une technologie Microsoft qui vous permet d'isoler un processus en l'exécutant dans un environnement en bac à sable. Cette technologie restreint l'accès d'un processus aux informations d'identification, aux appareils, à un système de fichiers, à un réseau, à d'autres processus et fenêtres et vise à minimiser les capacités des logiciels malveillants capables d'exécuter du code arbitraire dans un processus.
Cette protection complique grandement le processus de fonctionnement. À cause de cela, nous ne pouvons pas appeler des fichiers exécutables tiers ou accéder à des informations utilisateur sensibles en mémoire ou sur des disques. Cependant, cette protection peut être surmontée en utilisant des vulnérabilités dans la mise en œuvre du sandbox AppContainer ou en augmentant les privilèges en exploitant les vulnérabilités dans le noyau du système d'exploitation.
Il convient de noter que Microsoft
dispose d'un programme de récompense distinct pour les techniques de contournement security mitigation
technologies d' security mitigation
. Le programme indique que la réutilisation de code exécutable (la création de chaînes ROP
est une variante de cette technique) ne relève pas du programme, car est un problème architectural.
Utilisation de pwn.js
À partir d'une analyse de toutes les technologies de sécurité, il s'ensuit que pour pouvoir exécuter du code arbitraire, vous devez contourner le sandbox AppContainer
. Dans cet article, nous décrivons une méthode utilisant une vulnérabilité dans le noyau Windows
. Dans ce cas, nous ne pouvons utiliser que du code JS
et des chaînes ROP
. Écrire un exploit pour le noyau en utilisant uniquement des chaînes ROP
peut être très difficile. Pour simplifier cette tâche, vous pouvez trouver un ensemble de gadgets avec lesquels nous pourrions appeler les méthodes WinAPI
nécessaires. Heureusement, cela est déjà implémenté dans la bibliothèque pwn.js
En l'utilisant, après avoir décrit uniquement les fonctions de lecture et d'écriture pour la lecture et l'écriture arbitraires, vous pouvez obtenir une API
pratique pour trouver les fonctions WinAPI
nécessaires et les appeler. pwn.js
fournit également un outil pratique pour travailler avec des valeurs 64 bits et des pointeurs et des outils pour travailler avec des structures.
Prenons un exemple simple. À l'étape précédente, nous avons obtenu une chaîne de deux DataView
associés. Pour préparer l'exploit, vous devez créer la classe suivante:
var Exploit = (function() { var ChakraExploit = pwnjs.ChakraExploit; var Integer = pwnjs.Integer; function Exploit() { ChakraExploit.call(this); ...
, MessageBoxA
:
function run() { with (new Exploit()) {
:

3.
WinAPI
. . CVE-2016-3309
. [7] [8], pwn.js
[2] , GDI
-. [9], [10] [11]. . , . , AppContainer
, pwn.js
. cmd.exe
SYSTEM
.
GDI — Windows , , .
, . JS
- 64- kernel_read_64
kernel_write_64
, . Windows. BITMAP
, . pwn.js
. BITMAP
, , :
var BITMAP = new StructType([ ['poolHeader', new ArrayType(Uint32, 4)],
Tid
KTHREAD
, , , EmpCheckErrataList
, . , :
... var nt_EmpCheckErrataList_ptr = worker_bitmap_obj.Tid.add(0x2a8); var nt_EmpCheckErrataList = kernel_read_64(nt_EmpCheckErrataList_ptr); var ntoskrnl_base_address = nt_EmpCheckErrataList.sub( g_config.nt_empCheckErrataList_offset); ...
, AppContainer
. AppContainer
IsPackagedProcess
( Process Environment Block
, PEB
), . Access Token
, AppContainer
. Access Token
, . Access Token
, . EPROCESS
ActiveProcessLinks
, . PEB
EPROCESS
. PsInitialSystemProcess
, , ActiveProcessLinks
.
Edge
: , Edge
. SYSTEM
. , , winlogon.exe
.
pwn.js
:
:

YouTube , Microsoft Edge.
Résumé
:
- ,
Edge
Windows
, 13 , CVE-2017-0240
, . CVE-2016-3309
. JS
- 666
JS
- :
cmd.exe
SYSTEM
,
, , . , , . .
Matériaux
- Liu Jin — The Advanced Exploitation of 64-bit Edge Browser Use-After-Free Vulnerability on Windows 10
- Andrew Wesie, Brian Pak — 1-Day Browser & Kernel
Exploitation - Natalie Silvanovich — The ECMA and the Chakra. Hunting bugs in the Microsoft Edge Script Engine
- Natalie Silvanovich — Your Chakra Is Not Aligned. Hunting bugs in the Microsoft Edge Script Engine
- phoenhex team — cve-2018-8629-chakra.js
- Quarkslab — Exploiting MS16-145: MS Edge TypedArray.sort Use-After-Free (CVE-2016-7288)
- Exploiting MS16-098 RGNOBJ Integer Overflow on Windows 8.1 x64 bit by abusing GDI objects
- Siberas — Kernel Exploitation Case Study — "Wild" Pool Overflow on Win10 x64 RS2 (CVE-2016-3309 Reloaded)
- Saif El-Sherei — Demystifying Windows Kernel Exploitation by Abusing GDI Objects
- Diego Juarez — Abusing GDI for ring0 exploit primitives
- Nicolas A. Economou — Abusing GDI for ring0 exploit
primitives: Evolution - pwn.js