mcpField
B
browser-exploitation-v8

browser-exploitation-v8

github.com/yaklang安全2.6k3.7k 次安装6 周前更新
代码仓库

关于 browser-exploitation-v8

browser-exploitation-v8 是研究 V8 与 Chrome 利用技术的安全技能,讲解 JIT 漏洞、内存原语、指针压缩限制,以及从渲染器失陷到 Chrome 沙箱逃逸的过程。

主要功能

  1. 01V8 编译流程与对象表示
  2. 02JIT 漏洞类型及触发方式
  3. 03addrof 与 fakeobj 原语
  4. 04ArrayBuffer 与 WASM 内存技术
  5. 05V8 沙箱与 Chrome 逃逸概述

适用场景

  1. 01学习 V8 利用相关概念
  2. 02分析 JIT 类型混淆漏洞
  3. 03理解指针压缩的限制
  4. 04梳理渲染器漏洞到沙箱逃逸的路径

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/yaklang/hack-skills --skill browser-exploitation-v8

安装后在 Claude Code 中输入/browser-exploitation-v8确认已加载。

附带文件V8_EXPLOITATION_PATTERNS.md

详细介绍

技能:浏览器 / V8 利用——专家攻击手册

AI 加载指令:专家级 V8/Chrome 利用技术。涵盖 V8 编译流水线、JIT 类型混淆、addrof/fakeobj 原语、ArrayBuffer 损坏、WASM RWX 页面、V8 沙箱(指针压缩)以及 Chrome 沙箱逃逸概述。内容提炼自 ctf-wiki 浏览器章节、Project Zero 研究和 CTF 竞赛中的常见模式。基础模型经常混淆 V8 对象表示细节,并忽略指针压缩带来的限制。

0. 相关路由

高级参考

在需要以下内容时,加载 V8_EXPLOITATION_PATTERNS.md:

  • 详细的利用模式和代码模板
  • 堆布局操纵和 GC 交互
  • V8 沙箱绕过技术
  • 对象 map 混淆模式

1. V8 架构

编译流水线
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
V8 核心概念
概念描述
带标签的指针SMI(小整数):value << 1,HeapObject:ptr | 1
指针压缩V8 ≥ 8.0:通过相对于 cage 基址的 32 位偏移量寻址对象(4GB 沙箱)
Map(隐藏类)定义对象形状:属性名称、类型、偏移量
Elements kinds内部数组类型:PACKED_SMI_ELEMENTS、PACKED_DOUBLE_ELEMENTS、PACKED_ELEMENTS 等
写屏障写入堆指针时进行 GC 记账
垃圾回收Orinoco GC:次要回收(Scavenge)和主要回收(Mark-Compact)
对象表示(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

2. 常见 V8 漏洞类别

漏洞类别描述示例
JIT 类型混淆TurboFan 在优化后假定类型错误推测类型检查被消除,导致应用了错误操作
错误的边界检查消除JIT 基于错误的范围分析移除数组边界检查CheckBounds 节点被消除 → 越界访问
原型链混淆优化假定原型稳定,但原型变更会使该假定失效优化后原型发生变化 → 属性访问错误
Turbofan 归约错误强度归约或常量折叠错误范围分析中的整数溢出
竞态条件SharedArrayBuffer 与工作线程发生竞态通过并发修改导致类型混淆
内置函数中的差一错误内置函数实现中的边界错误字符串 / 数组边界
Typer 错误TurboFan 中类型范围计算错误Typer 认为值在 [0, N] 范围内,但实际可能是 N+1
触发 JIT 优化
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);

3. 利用原语

addrof — 泄露对象地址
// 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
}
fakeobj — 创建伪造的对象引用
// 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
}
通过 addrof + fakeobj 构建任意读 / 写
// 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;
}

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

当 TurboFan 错误地消除边界检查时:

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
V8 堆中相邻的是什么?

对象在 V8 的新生代(new space)中按顺序分配。通过控制分配顺序:

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

5. ArrayBuffer 任意读 / 写

ArrayBuffer 的 backing store 是指向已分配内存的原始指针。破坏它即可实现绝对内存读 / 写。

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
V8 沙箱(指针压缩)的影响

由于 V8 ≥ 8.0(指针压缩)以及 V8 沙箱(≥ 11.x):

  • ArrayBuffer.backing_store 是一个沙箱指针(位于 V8 cage 的 4GB 区域内)
  • 无法直接指向 V8 cage 之外
  • 需要逃逸沙箱才能访问完整的进程内存

6. WASM RWX 页面

在某些平台上,WebAssembly JIT 代码会放置在 RWX(可读、可写、可执行)页面中。

// 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

现代 Chrome:W^X 强制机制意味着 WASM 页面只能是 RW 或 RX,不能同时为 RWX。JIT 代码先以 RW 模式写入,再切换为 RX。要利用这一点,需要找到可写窗口或使用 JIT 喷射。


7. V8 沙箱

架构(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               │
└──────────────────────────────────────┘
沙箱逃逸途径
途径方法
外部指针表篡改外部指针表中的条目,使其指向任意地址
WASM 代码指针覆盖 WASM 函数入口,使其跳转到受控的 shellcode
JIT 代码破坏通过竞争条件或混淆指针写入 JIT 代码页面
Mojo IPC(Chrome)利用 Chrome IPC,从受攻击的渲染器攻击浏览器进程
后备存储封印绕过利用类型混淆获取未受沙箱限制的指针

8. Chrome 沙箱逃逸(概述)

通过 V8 漏洞利用实现渲染器 RCE 后,进程仍处于沙箱中。要实现完全攻陷,还需要:

阶段目标示例
渲染器漏洞利用V8 / Blink DOM类型混淆 → shellcode
IPC/Mojo 漏洞Chrome IPC 层Mojo 接口中的释放后使用漏洞
浏览器进程漏洞利用特权浏览器进程在沙箱外执行代码

Mojo 接口(Chrome 的 IPC)暴露了攻击面:寻找 Mojo 消息处理程序中的 UAF 或类型混淆。


9. 工具

# V8 debugging
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

# Turbolizer: visual TurboFan IR graph
# Chrome DevTools Memory panel: heap snapshots

# Build V8 for debugging
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. 决策树

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)

评论

支持 Markdown