このリポジトリには、React Server Components(RSC)における未認証の任意コード実行を可能にする重大なセキュリティ脆弱性 CVE-2025-55182 のPoC再現が含まれています。
この脆弱性は、React Server Componentsがクライアントリクエストから「Server Actions」をデシリアライズする方法に存在します。具体的には、requireModule 関数が、要求されたエクスポート名がモジュールの直接のプロパティであることを検証できていませんでした。これにより、攻撃者はエクスポートされた関数の constructor プロパティにアクセスし、任意のコードを実行するために使用できるグローバルな Function コンストラクタへの参照を取得できました。
このPoCは、react-server-dom-webpack ライブラリ内の脆弱性を隔離するために最小限のNode.js環境を使用します。これは、エクスプロイトがフレームワークの設定ミスではなく、ライブラリ自体のバグを実証することを確認するためです。
npmnpm install
[!NOTE]
package.jsonは脆弱性のあるバージョン19.0.0に固定されています。
このスクリプトは、脆弱なReactランタイムを使用してリクエストをデコードする素のHTTPサーバーをセットアップします。
# tty1
node --conditions react-server server.js
別のターミナルで、エクスプロイトを実行します。これにより、悪意のあるFlightペイロードがサーバーに送信されます。
# tty2
node exploit.js id
応答としてコマンドの出力が返されるはずです:
期待される出力:
Response: uid=0(root) gid=0(root) groups=0(root)
なぜこの脆弱性が発生したのか?
ReactFlightDOMServerNode.js 内の requireModule 関数は、基本的にクライアントが送信した name をそのまま信頼していました。そのプロパティが実際に公開されることを意図されているかどうかを確認せずに、moduleExports[metadata[NAME]] を実行していました。つまり、クライアントが「このプロパティが欲しいんだけど」と言えば、サーバーは「いいよ!はい、どうぞ」と返すだけでした。
任意のプロパティへのアクセスを許可することがなぜ悪いアイデアなのか?
なぜなら、基本的に誰でもプロトタイプチェーンに到達できるようになり、非常に危険な constructor にすらアクセスできてしまうからです。モジュールがたまたま関数をエクスポートしている場合(module.exports = () => {} など)、その constructor は文字通りグローバルな Function コンストラクタになります。
Function コンストラクタを取得することがなぜRCEを意味するのか?
攻撃者は Function コンストラクタを取得すると、「Bound Server Action」機能を悪用できます。悪意のあるJavaScriptを含む文字列をそれにバインドします(基本的に new Function("evil code") に変換します)。そしてそれが実行されると、サーバーは攻撃者が仕込んだ任意のコードを実行します。
なぜReactは実際にその悪意のある関数を実行するのか?
Server ActionsはIDによってトリガーできるからです。攻撃者が自分の module#constructor 参照を指すAction IDを含むペイロードを構築すると、Reactはそれを通常のアクションとして解決し、実行します。その「アクション」は実際には攻撃者の悪意のある関数です。
なぜこれらは一切検証されていなかったのか?
システムは、Server Referenceメタデータの id と name が常に開発者によって定義された有効なエクスポートを参照するものと想定していました。要求されたプロパティが実際のエクスポートであり、プロトタイプチェーンから継承されたものではないことを確認するための hasOwnProperty のような安全チェックはありませんでした。
server.js なのか?素の server.js(およびヘルパーの webpack-runtime.js)を使用して、React Server Componentsランタイムを手動で構成しています。これにより、以下のことが可能になります:
module.exports = fn)にのみ機能します。実際のバンドラーは、その設定によってエクスポートのラップ方法を変更する可能性がありますが。react-server-dom-webpack の内部にあることを示せます。react-server-dom-webpack はWebpackバンドル内で実行されていることを前提としています。私たちの webpack-runtime.js は、それが期待するグローバル(__webpack_require__、__webpack_chunk_load__)を提供します。これは脆弱性をモックしているのではなく、ライブラリが実際に動作するために必要な最低限のランタイムを提供しているだけです。
開発者が child_process.exec などの危険なものを意図的に公開した場合にのみ機能する「無効なPoC」についての議論がありました。
このPoCはそのようなものではありません。通常の安全なセットアップで機能します。
updateProfile 関数を公開します。これは単に文字列を返すだけで、怪しいものもシェルコマンドもありません。Function コンストラクタである updateProfile.constructor に直接ジャンプできます。.constructor へのアクセスを許可すべきではありませんでした。開発者は Function コンストラクタを公開する意図はありませんでしたが、安全でないデシリアライゼーションが代わりにそれを公開してしまいました。唯一の実際の要件は、モジュールが関数を直接エクスポートしていること(module.exports = fn)です。これはCommonJSや多くのバンドラー設定で非常に一般的です。
exploit.js 内のペイロードは、3つのチャンクでReact Flightメッセージを構築します:
id:"user-profile-action#constructor"、つまり「constructorをくれ」という意味です。bound:引数を含むチャンク2を指します。["console.log('nice try, diddy!')"]:悪意のあるコード文字列です。Reactがこれをデシリアライズすると:
user-profile-action を解決します。.constructor プロパティを読み取ります => グローバルな Function を取得します。new Function("console.log('nice try, diddy!')")これがRCEです!
直ちに修正済みバージョンにアップグレードしてください:
react-server-dom-webpack >= 19.0.1react-server-dom-parcel >= 19.0.1react-server-dom-turbopack >= 19.0.1このパッチは、継承されたプロパティへのアクセスを防ぐための hasOwnProperty チェックを導入し、base64 ファイルのアップロードを制限します。
修正済みバージョンに対してこのPoCを実行すると、サーバーは次のようにクラッシュまたはエラーになります:
$ node --conditions react-server server.js
Listening on http://localhost:3000
/path/to/CVE-2025-55182/node_modules/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js:2726
resolvedValue = resolvedValue.bind.apply(
^
TypeError: Cannot read properties of undefined (reading 'bind')
at /path/to/CVE-2025-55182/node_modules/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js:2726:43
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Node.js v20.19.3
これにより、エクスプロイトが constructor プロパティへのアクセスに失敗したこと(Function の代わりに undefined が返されたこと)、その結果、後続の .bind 呼び出しが失敗したことが確認できます。
このコードは教育およびテスト目的のみです。所有していないシステムや、明示的なテスト許可を得ていないシステムに対して、このエクスプロイトを使用しないでください。
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE の下で公開されています。