
En el marco de este artículo, consideraremos con suficiente detalle el proceso de escribir un exploit para una vulnerabilidad en Microsoft Edge, con la posterior salida del sandbox. Si estás interesado en saber cómo es este proceso, ¡bienvenido con cat!
Introduccion
En el último Pwn2Own 2019
en Montreal, en la categoría de navegadores, se demostró una vulnerabilidad para hackear Microsoft Edge
. Para esto se utilizaron dos vulnerabilidades: double free
en el renderizador y una vulnerabilidad lógica para salir del sandbox. Estas dos vulnerabilidades se cerraron recientemente y se les asignó el CVE
correspondiente: CVE-2019-0940
y CVE-2019-0938
. Puede leer más sobre vulnerabilidades en el blog: Pwn2Own 2019: Microsoft Edge Renderer Exploitation (CVE-2019-0940). Parte 1 y Pwn2Own 2019: Microsoft Eedge Sandbox Escape (CVE-2019-0938). Parte 2 .
Como parte de nuestro artículo, queremos mostrar el proceso de escritura de un exploit y cuánto tiempo y recursos se necesitan para esto usando el ejemplo de Microsoft Edge
en Windows 10
usando CVE-2017-0240
y CVE-2016-3309
. Una de las diferencias es que si el exploit demostrado en Pwn2Own
usó una vulnerabilidad lógica para salir del sandbox, entonces, en nuestro escenario, la vulnerabilidad en el kernel Windows 10
se usará para salir del sandbox. Como muestran los parches de Microsoft
, hay muchas más vulnerabilidades en el kernel que vulnerabilidades en la implementación del sandbox. Como resultado, es más probable que se encuentre una cadena de vulnerabilidades de este tipo, y será útil saber para los empleados de IS en las empresas.
Datos de origen
Este artículo cubrirá el proceso de escribir un exploit de 1 día para el navegador Microsoft Edge
. CVE-2017-0240
será operado. La primera etapa de operación se llevará a cabo sobre la base de materiales de la fuente [1], obtendremos una arbitrary address read/write
primitiva, y también nos familiarizaremos con varias técnicas que pueden ser útiles al explotar tales vulnerabilidades. A continuación, le presentaremos la herramienta pwn.js
, que lo ayudará a obtener una llamada a funciones arbitrarias basadas en lectura y escritura arbitrarias, y también discutiremos varias mitigations
y formas de evitarlas. En la última etapa, se aprovechará la vulnerabilidad del kernel de Windows CVE-2016-3309
para aumentar los privilegios, eludir AppContainer
restricciones de AppContainer
y obtener el control completo sobre la máquina atacada.
La operación se realizará en el stand con Microsoft Windows 10 Pro 1703 (10.0.15063)
y el navegador Microsoft Edge (40.15063.0.0)
.
Paso 1. Obtener una arbitrary address read/write
primitiva
Descripción de la vulnerabilidad y obtención de OOB
Una vulnerabilidad de tipo use-after-free
presente en el método copyFromChannel del objeto Audio Buffer .
AudioBuffer es la interfaz de un recurso de audio breve ubicado en la memoria y creado a partir de un archivo de audio utilizando el método AudioContext.decodeAudioData (), o de los datos de origen utilizando el método AudioContext.createBuffer (). Los datos de audio colocados en AudioBuffer se pueden reproducir en AudioBufferSourceNode.
La presentación de The Advanced Exploitation of 64-bit Edge Browser Use-After-Free Vulnerability on Windows 10
proporciona un análisis detallado de la vulnerabilidad y el parche. Cuando se copyFromChannel
método copyFromChannel
, el contenido del canal del búfer de audio se copia en el búfer de destination
especificado por el primer argumento. El método también acepta el número de canal ( channelNumber
) y el desplazamiento en el búfer de audio ( startInChannel
), a partir del cual es necesaria la copia. Antes de copiar directamente los datos al destination
en la función CDOMAudioBuffer::Var_copyFromChannel
, el búfer de destination
se almacena en caché (la dirección y el tamaño del búfer se almacenan en variables de función local en la pila) y los valores de objeto channelNumber
y startInChannel
se startInChannel
al tipo Int
, para lo cual se llama al método valueOf
de los objetos convertidos. La vulnerabilidad es que un búfer en caché se puede liberar en el momento de la conversión de tipos en el método anulado del objeto valueOf
. Para la verificación, utilizamos el siguiente código:
Este código utiliza la tecnología de Web Workers
para liberar el búfer. Después de haber creado un Worker
vacío, podemos enviarle un mensaje utilizando el método postMessage
. El segundo argumento de transfer
opcional de este método acepta una matriz de objetos Transferable
( ArrayBuffer
, MessagePost
o ImageBitmap
), los derechos del objeto se transferirán a Worker
y el objeto ya no estará disponible en el contexto actual, por lo que se puede eliminar. Después de esto, se produce una llamada a la sleep
, una función que detiene temporalmente la ejecución de un programa (se implementa de forma independiente). Esto es necesario para que el sistema de recolección de basura ( GC
, Garbage Collector
) logre liberar el búfer, cuyos derechos fueron transferidos.
Los trabajadores web proporcionan un medio simple para ejecutar scripts en el hilo de fondo. El subproceso de trabajo puede realizar tareas sin interferir con la interfaz de usuario. Además, pueden hacer E / S usando XMLHttpRequest (aunque los atributos responseXML y channel siempre serán nulos). Un trabajador existente puede enviar mensajes JavaScript al código creador a través del controlador de eventos especificado por este código (y viceversa).
Al ejecutar este código en Edge bajo el depurador, puede obtener el siguiente bloqueo.

Como resultado, la llamada a copyFromChannel
intenta copiar el contenido del búfer de audio en el área de memoria no asignada. Para aprovechar la vulnerabilidad, es necesario lograr la asignación de cualquier objeto en esta área de memoria. En este caso, el segmento de matriz es perfecto.
Las matrices en Chakra
(el motor JS
utilizado en el navegador Edge
) se organizan de la siguiente manera: el objeto de la matriz tiene un tamaño fijo, los punteros a los objetos de la matriz (o valores, en el caso de IntArray
) se almacenan en un área de memoria separada: el segmento, el puntero que está contenido en el objeto matriz. El encabezado del segmento contiene información diversa, incluido el tamaño del segmento, que corresponde al tamaño de la matriz. El tamaño de la matriz también está presente en el objeto de la matriz en sí. Esquemáticamente, se ve así:

Por lo tanto, si logramos seleccionar el segmento de matriz en el espacio previamente liberado, entonces podemos sobrescribir el encabezado del segmento de matriz con el contenido del búfer de audio. Para hacer esto, modificamos el código anterior agregando las siguientes líneas después de 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; } ...
El tamaño de las matrices se selecciona de modo que el tamaño del segmento de la matriz ocupe todo el segmento de almacenamiento dinámico (la pieza mínima indivisible de memoria de almacenamiento dinámico, cuyo tamaño es 0x10000 bytes). Ejecute este código, especificando la función memcpy
como punto de interrupción (habrá muchas llamadas memcpy
, por lo que tiene sentido detenerse primero en edgehtml!WebCore::AudioBufferData::copyBufferData
), en el que ocurrió el bloqueo. Obtenemos el siguiente resultado:

Genial Ahora podemos sobrescribir el encabezado del segmento de matriz con nuestros propios valores. Los valores más interesantes en este caso son el tamaño de la matriz, cuyo desplazamiento podemos ver en la captura de pantalla anterior. Cambie el contenido del búfer de audio de la siguiente manera:
... 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; ...
Preste atención a los valores de ta2[14]
y ta2[15]
; ya no se refieren al encabezado del segmento, sino a los valores de la matriz en sí. Con esto, podemos determinar la matriz que necesitamos en la matriz arr
global de la siguiente manera:
... 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, como resultado, se encontró una matriz, cuyos dos primeros elementos se modificaron de cierta manera, entonces todo está bien. Ahora tenemos una matriz cuyo tamaño de segmento es más grande de lo que realmente es. Las matrices restantes se pueden liberar.
Aquí es necesario recordar que el tamaño de la matriz existe en dos entidades: en el objeto de matriz, donde permaneció sin cambios, y en el segmento de matriz, donde lo aumentamos. Resulta que el tamaño en el objeto de matriz se ignora si el código se ejecuta en el modo JIT
y se ha optimizado. Esto se logra fácilmente, por ejemplo, de la siguiente manera:
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)); }
Después de eso, utilizando las arr_set
arr_get
y arr_set
puede ir más allá de los límites de la matriz ( OOB
, out-of-bound
).
Usando OOB
para obtener la primitiva de leer y escribir en una dirección arbitraria
En esta sección, consideramos una técnica que le permite leer y escribir en una dirección arbitraria utilizando OOB
. El método por el cual obtenemos esto será similar al utilizado en la fuente [1], pero también habrá cambios significativos.
En la versión utilizada de Edge
los bloques de memoria para el montón se asignan secuencialmente, debido a lo cual, al asignar una gran cantidad de objetos, tarde o temprano aparecerán después del segmento de matriz, más allá de los cuales podemos ir.
Primero, nos da la capacidad de leer un puntero a una tabla virtual de métodos de objetos ( vftable
), para que podamos evitar la aleatorización del espacio de direcciones del proceso ( ASLR
). ¿Pero el acceso a qué objetos nos ayudará a lograr una lectura y escritura arbitrarias? Un par de objetos DataView
son geniales para esto.
DataView proporciona una interfaz de bajo nivel para leer y escribir múltiples tipos numéricos en un ArrayBuffer binario, independientemente del orden de los bytes de la plataforma.
La estructura interna de DataView
contiene un puntero a un búfer. Para leer y escribir en una dirección arbitraria, por ejemplo, podemos construir una cadena de dos DataView
( dv1
y dv2
) de la siguiente manera: como el búfer dv1
especifique la dirección dv2
accediendo a la matriz. Ahora usando dv1
podemos cambiar la dirección del búfer dv2
, debido a lo cual se logra una lectura y escritura arbitrarias. Esquemáticamente, esto se puede representar de la siguiente manera:

Para usar este método, debe aprender a determinar las direcciones de los objetos en la memoria. Para hacer esto, existe la siguiente técnica: debe crear una nueva Array
, usar OOB
para guardar su vftable
y typeId
(los dos primeros campos de 64 bits de la estructura) y asignar el objeto al que la dirección interesa al primer elemento de la matriz. Luego, debe restaurar los typeId
vftable
y typeId
previamente guardados. Ahora la palabra doble junior y senior de la dirección del objeto se puede obtener haciendo referencia al primer y segundo elemento de la matriz. El hecho es que, por defecto, la nueva matriz es IntArray
, y los valores de 4 bytes de la matriz se almacenan en su segmento tal como están. Al asignar un objeto a una matriz, la matriz se convierte en una ObjectArray
y su segmento se usa para almacenar las direcciones de los objetos. La conversión cambia vftable
y typeId
. En consecuencia, si restauramos los valores originales vftable
y typeId
, a través de los elementos de esta matriz podemos acceder al segmento directamente. El proceso descrito esquemáticamente se puede representar de la siguiente manera:

La función para obtener la dirección se verá así:
function addressOf(obj) { var hdr_backup = new Array(4);
Una pregunta abierta sigue siendo la creación de los objetos necesarios y su búsqueda usando OOB
. Como se mencionó anteriormente, al asignar una gran cantidad de objetos, tarde o temprano comenzarán a destacarse después del segmento de matriz, más allá del cual podemos ir. Para encontrar los objetos necesarios, solo necesita pasar por los índices fuera de la matriz en busca de los objetos necesarios. Porque todos los objetos del mismo tipo se encuentran en un segmento del montón, puede optimizar la búsqueda y recorrer los segmentos del montón en incrementos de 0x10000
, y verificar solo los primeros valores desde el comienzo de cada segmento del montón. Para identificar objetos, puede establecerles valores únicos para algunos parámetros (por ejemplo, para DataView
puede ser byteOffset
) o, utilizando las constantes ya conocidas en la estructura del objeto (por ejemplo, en la versión utilizada de Edge
en IntArray
, el valor 0x10005
siempre se encuentra en 0x18
).
Al combinar todas las técnicas anteriores, puede leer y escribir en una dirección arbitraria. A continuación se muestra una captura de pantalla de lectura de objetos de memoria DataView
.

Paso 2. Realizar funciones arbitrarias de API
En esta etapa, pudimos leer y escribir en una dirección arbitraria dentro del proceso de visualización del contenido de Edge
. Considere las principales tecnologías que deberían interferir con el funcionamiento posterior de la aplicación y sus soluciones. Ya escribimos una breve serie de artículos sobre app specific security mitigation
( parte 1, introducción , parte 2, Internet Explorer y Edge , parte 3, Google Chrome ), pero tenga en cuenta que los desarrolladores no se quedan quietos y agregan nuevas herramientas a sus productos protección
Aleatorización del espacio de direcciones ( ASLR
)
ASLR (asignación aleatoria del diseño del espacio de direcciones en inglés) es una tecnología utilizada en sistemas operativos que cambia aleatoriamente la ubicación de estructuras de datos importantes en el espacio de direcciones del proceso, a saber: imágenes de archivos ejecutables, bibliotecas cargadas, montones y apilar.
Arriba, aprendimos a leer las direcciones de las tablas de clases virtuales, utilizándolas, podemos calcular fácilmente la dirección base del módulo Chakra.dll
, por lo que ASLR
no presenta problemas para una operación adicional.
Protección de ejecución de datos ( DEP
, NX
)
La Prevención de ejecución de datos (DEP) es una función de seguridad integrada en Linux, Mac OS X, Android y Windows que impide que una aplicación ejecute código desde un área de memoria marcada como "solo datos". Evitará algunos ataques que, por ejemplo, guardan código en dicha área utilizando desbordamientos de búfer.
Una forma de evitar esta protección es llamar a VirtualAlloc
usando cadenas ROP
. Pero en el caso de Edge
este método no funcionará debido a ACG
(ver más abajo).
Control de flujo de CFG
( CFG
)
CFG
es un mecanismo de protección destinado a complicar el proceso de explotación de vulnerabilidades binarias en aplicaciones de usuario y en modo kernel. El trabajo de este mecanismo consiste en validar llamadas indirectas, lo que evita que un atacante intercepte el hilo de ejecución (por ejemplo, sobrescribiendo la tabla de funciones virtuales)
Esta tecnología controla solo llamadas indirectas, por ejemplo, llamadas a métodos de la tabla virtual de funciones de objeto. Las direcciones de retorno en la pila no están controladas, y esto se puede usar para construir cadenas ROP
. El uso futuro de ROP/JOP/COP
cadenas ROP/JOP/COP
puede verse obstaculizado por Intel
nueva tecnología de Intel
: Control-flow Enforcement Technology
( CET
). Esta tecnología consta de dos partes:
Shadow Stack
sombra (pila de sombra): se utiliza para controlar las direcciones de retorno y protege contra cadenas ROP
;Indirect Branch Tracking
es un método de protección contra cadenas JOP/COP
. Es una nueva instrucción ENDBRANCH
, que marca todas las direcciones de transición válidas para instrucciones de call
y jmp
.
Guardia de código arbitrario ( ACG
)
ACG
es una tecnología que impide la generación dinámica de código (está prohibido asignar áreas de memoria VirtaulAlloc
usando VirtaulAlloc
) y sus modificaciones (es imposible VirtaulAlloc
área de memoria disponible como ejecutable)
Esta protección, como CFG
, no impide el uso de cadenas ROP
.
AppContainer Isolation
AppContainer es una tecnología de Microsoft que le permite aislar un proceso ejecutándolo en un entorno de espacio aislado. Esta tecnología restringe el acceso del proceso a las credenciales, los dispositivos, el sistema de archivos, la red, otros procesos y ventanas y tiene como objetivo minimizar el potencial del software malicioso que tiene la capacidad de ejecutar código arbitrario en el proceso.
Esta protección complica enormemente el proceso de operación. Por eso, no podemos llamar a archivos ejecutables de terceros ni acceder a información confidencial del usuario en la memoria o en los discos. Sin embargo, esta protección se puede superar mediante el uso de vulnerabilidades en la implementación del entorno limitado de AppContainer o aumentando los privilegios mediante la explotación de vulnerabilidades en el núcleo del sistema operativo.
Vale la pena señalar que Microsoft
tiene un programa de recompensa por separado para técnicas para eludir security mitigation
tecnologías de security mitigation
. El programa indica que la reutilización de código ejecutable (la construcción de cadenas ROP
es una variación de esta técnica) no se incluye en el programa, porque Es un tema arquitectónico.
Usando pwn.js
De un análisis de todas las tecnologías de seguridad, se deduce que para poder ejecutar código arbitrario, debe omitir el AppContainer
limitado de AppContainer
. En este artículo, describimos un método que utiliza una vulnerabilidad en el kernel de Windows
. En este caso, solo podemos usar el código JS
y las cadenas ROP
. Escribir un exploit para el kernel usando solo cadenas ROP
puede ser muy difícil. Para simplificar esta tarea, puede encontrar un conjunto de gadgets con los que podríamos llamar los métodos WinAPI
necesarios. Afortunadamente, esto ya está implementado en la biblioteca pwn.js
Utilizándolo, habiendo descrito solo las funciones de lectura y escritura para lectura y escritura arbitrarias, puede obtener una API
conveniente para encontrar las funciones necesarias de WinAPI
y llamarlas. pwn.js
también proporciona una herramienta conveniente para trabajar con valores de 64 bits y punteros y herramientas para trabajar con estructuras.
Considere un ejemplo simple. En el paso anterior, obtuvimos una cadena de dos DataView
relacionadas. Para preparar el exploit, debe crear la siguiente clase:
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.
Resumen
:
- ,
Edge
Windows
, 13 , CVE-2017-0240
, . CVE-2016-3309
. JS
- 666
JS
- :
cmd.exe
SYSTEM
,
, , . , , . .
- 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