
ここ数日、Reactの逆シリアル化RCE脆弱性が話題になっています。公式のCVSSは直接10.0点で、当時のLog4jと同レベルです。これを「現代のフロントエンドにおけるLog4j」と称する噂が一時期広がり、多くの企業の開発者にパニックを引き起こし、朝起きてすぐに文献を調べたりパッチを当てたりする事態になりました…。同時に、ネット上では疑問の声も上がっており、テストを行った人の中には「この脆弱性は宣伝されているほどではなく、逆に利用には一定の条件が必要だ」と指摘する人もいました。そこで、私は時間を取ってこの脆弱性を詳しく調査することにしました。
react-server-dom-webpack < 19.2.0、react-server-dom-turbopack < 19.2.0この脆弱性が発生する原因は以下の通りです。[email protected] において、サーバーが Server Action を解析する鍵となる関数は requireModule(疑似コード)です:
function requireModule(metadata) {
var moduleExports = __webpack_require__(metadata[0]);
// ...
return "*" === metadata[2]
? moduleExports
: "" === metadata[2]
? moduleExports.__esModule
? moduleExports.default
: moduleExports
: moduleExports[metadata[2]]; // ← 脆弱性ポイント
}
脆弱性の核心は moduleExports[metadata[2]] の部分にあり、metadata[2] に対する検証が行われていないため、攻撃者はモジュール自身のエクスポートプロパティだけでなく、プロトタイプチェーン上のプロパティ(constructor、__proto__ など)にもアクセスできます。攻撃者が metadata[0] を構築した後(例えばそれを vm に指定)、さらに metadata[2] を構築することで、指定されたモジュール内の危険なメソッド(例:vm.runInThisContext)をエクスポートさせ、脆弱性を悪用することが可能になります。
分析にあたり、ejpir が提供したテスト環境と脆弱性悪用コードを参考にし、vm_runInThisContext という Code Execution gadget を例に分析を行いました。プロセスは以下の通りです(注意:実際の環境では悪用手順は多少異なります)。
まず、ペイロードを含むリクエストを送信した後、ブレークポイントでリクエスト取得位置を確認します:


続いて、プログラムは const formData = parseMultipart(buffer, boundaryMatch[1]); の位置に到達します。parseMultipart にステップインします:

parseMultipart はリクエストボディデータを抽出し、formData に返します:

const actionFn = await decodeAction(formData, serverManifest); 脆弱性発生位置 にステップインします:

loadServerReference にステップインします:


ここから脆弱性の核心コード位置 requireModule に到達します。ステップイン:


id の値を # の前後でモジュールメソッドとして分割し、bound パラメータ値をメソッドの引数として返します:




actionFn にステップインし、最終的なペイロードを実行します:



以上で脆弱性の悪用は終了です!
この脆弱性自体は、入力の検証が不十分であることが原因であり、この点は Log4j や fastjson とまったく同じです。上記のテストでは vm_runInThisContext を使用しましたが、実際にはこの脆弱性には複数のガジェットが利用可能です。例えば:
vm#runInThisContextvm#runInNewContextchild_process#execSyncchild_process#execFileSyncchild_process#spawnSyncfs#readFileSyncfs#writeFileSync#constructor#__proto__#prototype攻撃者はこの脆弱性を利用して以下を実現できます:
vm#runInThisContext または child_process#execSync による任意のシステムコマンド実行fs#readFileSync、fs#writeFileSync による任意のファイル読み取り/書き込み.bashrc の改ざん、アプリケーションファイルの上書きなど.env、秘密鍵、データベース認証情報など)の読み取りこれに基づき、以下の防御策を提示します。
一時的な防御としては、以下の観点が考えられます。WAF上でこれらの危険なフィールドをブロックするルールを設定し、悪意ある攻撃を即座に阻止できます。また、nginxでマッチングブロックを設定することも可能です。以下に例を示します:
# Nginx 設定例
location /formaction {
# 危険なモジュール参照を含むリクエストをブロック
if ($request_body ~* "(vm#|child_process#|fs#|module#)") {
return 403;
}
# プロトタイプ汚染の試行をブロック
if ($request_body ~* "(#constructor|#__proto__|#prototype)") {
return 403;
}
}
現在、公式はセキュリティアップデートを公開しています。直ちに安全なバージョンにアップグレードしてください!
# react-server-dom-webpack をアップグレード
npm install react-server-dom-webpack@>=19.2.0
# react-server-dom-turbopack をアップグレード
npm install react-server-dom-turbopack@>=19.2.0
# Next.js ユーザー
npm install next@>=15.0.5
修正バージョン:
react-server-dom-webpack: >= 19.2.0react-server-dom-turbopack: >= 19.2.0next.js: >= 15.0.5上記の脆弱性を分析する際、whiteov3rflow の関連エクスプロイトを参考にし、さらにテスト環境向けの脆弱性検出ツールを作成して GitHub リポジトリに公開しました:

自己チェックしたい方はアクセスして入手してください(注意:原作者のテスト環境の都合上、現時点では元のテスト環境にのみ適用可能です。今後改善予定ですので、必要な方はご自身で修正してお使いください…)。なお、正規の許可を得て使用し、不正な破壊行為は禁止します!
本日(2025.12.5)時点で、ネット上の状況を見ると、この脆弱性の「風評」はジェットコースターのように上下しています。ある時は「核弾頭」、ある時は「水の穴」、すぐにまた「核弾頭」に戻る…。関連する悪用方法も次々と現れています。現在の情報によれば、この「核弾頭」は事実となる可能性が高いですが、影響範囲はLog4jよりも小さいとのことです。とはいえ、関係する全ての方は速やかに更新し、後患を断つべきです!!!
また、開発者の皆さんにセキュリティ上の忠告です。決してユーザーの入力を信頼してはいけません。Log4j、fastjson、そして現在の React RCE も、この点が原因で問題を引き起こしました。実際の業務開発では、危険な箇所に対して必ずサンドボックスやホワイトリストなどの方法で厳格な検証を行い、悲劇を防いでください!!!
「紙上で得るは終に浅し、このことを知るには慎んで行うべし。」