flawopen.com/Teardowns/cve-2024-4947-chrome-v8-type-confusion

● CVE-2024-4947 · CVSS 9.8 · Crítica
Investigación · FlawOpen

CVE-2024-4947: Chrome V8 JIT Compiler Type Confusion Teardown

Análisis técnico del código fuente y mitigaciones de ingeniería para vulnerabilidade: How property getter transitions in V8's Turbofan JIT compiler tricked optimized native code into reading raw pointers as floating-point numbers, yielding in-the-wild remote code execution.

💡 Explicación en Lenguaje Sencillo (ELI5)

Analogía práctica: V8 makes JavaScript super fast by making assumptions. If you pass an array of numbers to a function 1,000 times, V8 turns that function into machine code that skips all safety checks. An attacker creates a sneaky property that changes the array from 'numbers' to 'object pointers' right in the middle of execution. The machine code treats an object's memory address as a number, letting the attacker read and write raw computer memory.

Conceptos Clave y Términos

Turbofan JIT
El compilador optimizador del motor V8 de Google que convierte el código de bytes JavaScript en código máquina de alta velocidad específico para la arquitectura.
Hidden Class / Map
Objeto de metadados interno de V8 que rastrea el diseño, los nombres de propiedades y los tipos de elementos de los objetos JavaScript en memoria.
Type Confusion
Fallo de corrupción de memoria que ocurre cuando el código trata una región de memoria inicializada como Tipo A como si fuera de Tipo B.
Map Deprecating Transition
Cuando la modificación dinámica de una propiedad de objeto altera su estructura interna, lo que obliga a desoptimizar el código JIT compilado previamente.

Análisis de Causa Raíz

La causa raíz se debe a parámetros de límite no validados en sistemas de código abierto, lo que permite la desincronización de estado y la elusión de controles de seguridad.

Flujo de Ataque Paso a Paso

Step 1

1. JIT Warm-Up

The attacker runs a loop passing an array of floating-point numbers (PACKED_DOUBLE_ELEMENTS) to a function until Turbofan compiles it to unverified native assembly.

Step 2

2. Prototype Getter Interception

Inside a customized property getter, the attacker alters the array layout by storing an object reference, changing the map to PACKED_ELEMENTS.

Step 3

3. Unchecked Native Execution

Because Turbofan omitted a dynamic map transition check, the compiled native loop continues to read the array as raw 64-bit IEEE floats.

Step 4

4. Arbitrary Read/Write Primitive

The float values represent raw pointers. The attacker constructs an addrof (read address) and fakeobj (corrupt address) primitive to break out of the V8 sandbox.

Código Fuente: Vulnerable vs. Seguro

✕ IMPLEMENTACIÓN VULNERABLE
// VULNERABLE: Simplified C++ Turbofan Graph Optimization (v8/src/compiler/)
// The optimizer assumed Map state remained unchanged across property accesses!

Reduction JSNativeContextSpecialization::ReduceNamedAccess(Node* node) {
  MapRef receiver_map = GetReceiverMap(node);
  
  // VULNERABILITY:
  // If property access invokes a custom JS getter that mutates the object's Map,
  // Turbofan fails to emit an effect-dependent map verification node!
  if (receiver_map.is_stable()) {
    // Relies on static stability without guarding against dynamic in-getter mutation
    return BuildPropertyLoad(node, receiver_map); 
  }
  
  return NoChange();
}
✓ PARCHE SEGURO Y ROBUSTO
// SECURE: Explicit Map Transition Guard Insertion in Turbofan Graph
Reduction JSNativeContextSpecialization::ReduceNamedAccess(Node* node) {
  MapRef receiver_map = GetReceiverMap(node);

  // FIX: Verify map stability AND emit runtime type transition guard
  if (receiver_map.is_stable()) {
    dependencies()->DependOnStableMap(receiver_map);

    // Explicitly insert dynamic Map check node before unboxing properties
    Node* effect = NodeProperties::GetEffectInput(node);
    Node* check = graph()->NewNode(
        simplified()->CheckMaps(CheckMapsFlag::kNone, 
                               ZoneHandleSet<Map>(receiver_map.object())),
        receiver, effect, control);

    // If an in-flight getter changes the object layout, force JIT deoptimization!
    NodeProperties::ReplaceEffectInput(node, check);
    return BuildPropertyLoad(node, check, receiver_map);
  }

  return NoChange();
}

Lista de Verificación de Seguridad para Ingeniería