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

● CVE-2024-4947 · CVSS 9.8 · 严重
安全研究 · FlawOpen

深度技术拆解:CVE-2024-4947: Chrome V8 JIT Compiler Type Confusion Teardown

CVE-2024-4947 源代码级技术深度解析与系统加固工程指南:深入剖析漏洞触发条件、攻击利用链条与加固补丁的具体实现。

💡 通俗易懂的原理解析 (ELI5)

通俗原理解析: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.

核心概念与专有名词

Turbofan JIT
技术概念 (Turbofan JIT):The optimizing compiler in Google's V8 engine that converts JavaScript bytecode into high-speed architecture-specific machine code.
Hidden Class / Map
技术概念 (Hidden Class / Map):V8's internal metadata object tracking the layout, property names, and element types of JavaScript objects in memory.
Type Confusion
技术概念 (Type Confusion):A memory corruption flaw occurring when code treats a memory region initialized as Type A as if it were Type B.
Map Deprecating Transition
技术概念 (Map Deprecating Transition):When dynamic modification of an object property causes its internal layout to mutate, requiring previously compiled JIT code to deoptimize.

根本原因剖析 (Root Cause)

根本原因在于开源系统中未经验证的边界参数,导致状态不同步并绕过安全控制。

攻击执行流程分解

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.

源代码对比:漏洞与安全实现

✕ 存在漏洞的实现
// 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();
}
✓ 加固后的安全修复
// 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();
}

工程与系统安全加固清单