React Router のプロトタイプ汚染から RCE へ:CVE-2026-42211
turbo-stream v2 は TYPE_ERROR の復元でコンストラクタを動的に呼び出しながら、その出所を検証していません。アプリにプロトタイプ汚染が一つあればこの経路が Function に届き、中度の欠陥二つが RCE になります。
概要
| 項目 | 内容 |
|---|---|
| CVE | CVE-2026-42211 |
| CVSS | 8.1(High) |
| CWE | CWE-502 信頼できないデータのデシリアライズ |
| 影響版 | React Router 7.0.0 〜 7.14.1(Framework Mode のみ) |
| 修正版 | 7.14.2 |
| 公開日 | 2026-06-03 |
| 実悪用 | 公開報告なし |
React Router は React エコシステムで最も広く使われるルーティングライブラリで、週あたり 1 億 2 千万回以上ダウンロードされます。2026 年 6 月 3 日に公開されたこの脆弱性には決定的な特徴があります。単独では発火しません。プロトタイプ汚染と安全でないデシリアライズが、この順で揃う必要があります。
深刻さを決めるのは露出面です。Framework Mode は公式に推奨される既定のモードであり、2024 年 10 月以降に作られた多くの React フルスタックプロジェクトがこの攻撃面を既定で抱えています。公開後、CVE Intruder の注目一覧で一時的に首位に立ちました。
発見の経緯
GitHub Security Lab の研究者 @securityMB が、Framework Mode に組み込まれたシリアライズライブラリ turbo-stream v2 を監査しているときに見つけました。
通常の動作では TYPE_ERROR の分岐は TypeError オブジェクトを再構築するだけです。しかしこの分岐は、データを復元するためにコンストラクタを動的に呼び出し、その出所を検証していません。したがってアプリ内の任意の独立したプロトタイプ汚染によって Error.prototype 連鎖へプロパティを注入できれば、想定されたコンストラクタを環境内の任意のコンストラクタ、たとえば Function に置き換えられます。
そして Function は文字列引数を受け取ると JavaScript をそのまま実行します。連鎖の最後の環はそこで閉じます。
再現
以下はすべて、許可されたセキュリティテストに限ります。
公式のスキャフォールドで影響版を用意します。
npx create-react-router@7.14.1 vulnerable-app
cd vulnerable-app && npm install && npm run dev
第一段階:前提となる汚染を成立させる。 実験室では、古い jQuery や旧版 lodash の残骸を安全でない深いマージで再現します。
function deepMerge(t, s) {
for (const key in s) {
if (typeof s[key] === 'object' && s[key] !== null) {
t[key] = t[key] || {};
deepMerge(t[key], s[key]);
} else {
t[key] = s[key];
}
}
}
__proto__ や constructor.prototype を通常のキーとして扱い、そのまま書き込みます。まさに侵入経路です。
第二段階:デシリアライザを発火させる。 汚染が入った状態で、細工した turbo-stream データを Framework Mode のデータ端点へ送り、処理を TYPE_ERROR の分岐へ導きます。汚染されたコンストラクタが想定の型に取って代わり、Function が呼ばれ、渡された文字列がコードになります。公開された PoC はこの構造を Python で組み立て、一行で検証します。
python3 poc_cve_2026_42211.py "id"
第三段階:確認。 サーバープロセスの識別情報、たとえば uid=1000(node) のような出力が得られれば、偽造した応答ではなく実際にコードが実行されたことになります。
修正
修正版は 7.14.2 です。パッチは文書化されていなかった独自のエラーシリアライズ処理を取り除き、連鎖を根元で断ちます。
npm install react-router@7.14.2
npm list react-router
すぐに更新できない場合は、宣言型モード(<BrowserRouter>)やデータモード(createBrowserRouter / <RouterProvider>)へ退避します。どちらも影響を受けません。あわせて Framework Mode 端点の外部からの到達性を絞ります。
最後に汚染そのものを探します。依存スキャンをすべての深いマージ経路($.extend(true, ...)、_.merge など)に向け、マージ前に __proto__ constructor prototype を明示的に拒否します。前提条件を残せば、同種の問題は別の形で戻ってきます。
判断
CVE-2026-42211 は足し算です。プロトタイプ汚染は単独では低、安全でないデシリアライズは単独では中、そして連鎖すれば遠隔コード実行です。所見を一つずつ採点するだけでは足りません。二つを重ねられるかどうかもリスクの一部であり、その組み合わせは個々のスコアには決して現れません。

コメント
…