
Firefox の RTCEncodedFrameBase コンポーネントにおける Use-After-Free(UAF)脆弱性の PoC です。WebRTC Encoded Transforms を介して悪用可能です。
CVE-2025-14321: RTCEncodedFrameBase のデストラクタは、フレームのネイティブメモリを解放する際に DetachArrayBuffer() を呼び出しません。その結果、JavaScript の ArrayBuffer は解放済みメモリへのポインタ(dangling pointer)を保持し続け、Firefox の content プロセスのヒープに対する任意の読み書きが可能になります。
この PoC は Mozilla Firefox 145.0.1 および Windows 10 でテストされています。
Firefox は、エンコードされた音声・動画フレームを NewArrayBufferWithUserOwnedContents() を介して JavaScript の ArrayBuffer として公開します。ネイティブフレーム(C++)が破棄されると ArrayBuffer のバッキングメモリは解放されますが、JavaScript 側の ArrayBuffer はデタッチ(detach)されません。これにより所有権の不変条件が破られ、dangling pointer が生成されます。
脆弱なコード(dom/media/webrtc/jsapi/RTCEncodedFrameBase.cpp):
// ❌ デストラクタは ArrayBuffer をデタッチしない
RTCEncodedFrameBase::~RTCEncodedFrameBase() = default;
パッチ適用後のコード:
// ✅ ネイティブメモリを解放する前に ArrayBuffer をデタッチする
RTCEncodedFrameBase::~RTCEncodedFrameBase() {
DetachData();
}
new Uint8Array(buf) により、ヒープ上で他のオブジェクトに再割り当てされたメモリのデータを読み取るnew Uint8Array(buf).fill(0x41) により、ヒープ上の隣接オブジェクト(vtable、DOM 構造など)を破壊するこれらのプリミティブは脆弱性の実証に十分です。追加のヒープ整形(heap shaping)を行うことで、リモートコード実行(RCE)に昇格させる可能性があります。
media.peerconnection.scripttransform.enabled = true
media.peerconnection.ice.loopback = true
media.peerconnection.ice.no_host = false
media.peerconnection.ice.relay_only = false
media.peerconnection.ice.proxy_only = false
⚠ 設定変更後は Firefox を再起動してください。
PoC は getUserMedia() 経由でカメラへのアクセスを要求しようとします。ICE の設定が正しく行われていれば、これは必須ではありません。この機能は、pref だけでは不十分なシナリオで Firefox を RFC IP handling mode 1 に昇格させ、ICE host 候補の生成を保証するためのものです。
実際には:
getUserMedia() はプロンプトを表示せずに静かに失敗します(NotFoundError)。media.peerconnection.ice.loopback = true により、ICE は同様に接続します。Web Worker は file:// からは動作しません。HTTP で配信してください:
python3 -m http.server 8080
脆弱な Firefox で開く: http://localhost:8080/poc-cve-2025-14321.html
PoC はカメラへのアクセスを要求しようとします。カメラが利用可能でユーザーが許可した場合、Firefox はセッションを RFC IP handling mode 1 に昇格させます。getUserMedia() が失敗した場合(カメラのない VM、権限拒否)、PoC はフレームソースとして canvas を使用して続行します。media.peerconnection.ice.loopback = true が有効であれば、ICE は同様に接続します。
captureStream(30) を使用して、30fps でアニメーションする 320×240 の <canvas> を作成します。これにより、予測可能なエンコード済みビデオフレームが生成されます。ICE が接続されカメラが使用された後、PoC は replaceTrack() でカメラトラックを canvas トラックに置き換え、カメラを解放します。
標準的な SDP ネゴシエーションと trickle ICE を使用して、2 つのローカル RTCPeerConnection(pc1 ↔ pc2)を作成します。候補はピア間で直接交換されます。
SDP ネゴシエーション前に、RTCRtpScriptTransform を pc1 の sender に割り当てます。ワーカーは送信パイプラインの各エンコード済みフレームをインターセプトします。
最初の 200 フレームそれぞれについて、ワーカーは:
frame.data(ネイティブメモリを指す ArrayBuffer)を抽出leaks[])に保存new Uint8Array(buf).fill(0x41)再送しないことで、GC が C++ ラッパーを破棄し、ネイティブメモリを解放します。しかし、JavaScript の ArrayBuffer はポインタを保持し続けます — dangling pointer の完成です。
201 フレーム目以降、3 フレームごとにワーカーは:
0x41 と異なる場合、アロケータがそのメモリを再利用したことを意味する → UAF 確認0x41 を再書き込みする(他者のヒープへの書き込みプリミティブ)解放済みメモリへのこの読み書きサイクルにより、ヒープが徐々に破壊され、最終的に Firefox のタブがクラッシュします(advisory で報告された SIGSEGV に相当)。
[0.4s] ✓ Track inicial: canvas (fallback)
[0.4s] ✓ ¡CONEXIÓN ICE ESTABLECIDA!
[0.5s] ▶▶▶ ¡PRIMER FRAME ENCODED RECIBIDO! ◀◀◀
[0.5s] Leak #0: 1219B → 41414141414141414141414141414141
[2.7s] Leak #49: 1497B → 41414141414141414141414141414141
[9.8s] Leak #199: 1870B → 41414141414141414141414141414141
[9.9s] 200 ArrayBuffers retenidos → memoria nativa YA liberada
[9.9s] → Dangling pointers activos, esperando reuso…
[ CRASH DE PESTAÑA — heap corruption]
注記: カメラが利用可能で権限が許可された場合、ログには
✓ getUserMedia concedido — ICE modo 1 activoと表示され、ICE 接続後に✓ Track reemplazado: cámara → canvasと表示されます。カメラのない VM では、PoC はプロンプトなしで canvas を直接使用します。
タブのクラッシュはヒープ破壊を確認します: 他のオブジェクト(vtable、DOM ノード)に再割り当てされたメモリへの 0x41 の書き込みにより、content プロセスで無効なメモリアクセスが発生します。
✓ Buffer[0] detached correctamente — fix activo
ArrayBuffer detached — versión parcheada ✓
デストラクタ内の DetachData() は、ネイティブメモリを解放する前に ArrayBuffer を無効化し、dangling pointer を排除します。
├── poc-cve-2025-14321.html # PoC funcional (código fuente legible)
└── README.md # Este archivo
Mozilla は Firefox 146 で、RTCEncodedFrameBase のすべてのデストラクタおよびティアダウンパスに DetachData() を追加して脆弱性を修正しました。
コミット: https://hg-edge.mozilla.org/mozilla-central/rev/1051067f6e83
⚠ 教育および研究目的のみ
この PoC は、ヒープ破壊(クラッシュ)の時点まで脆弱性の存在を実証します。コード実行に必要なヒープ整形、ASLR バイパス、ハイジャック技術は実装していません。その目的は、管理された環境での CVE の検証と理解のみです。