Skip to content

CVE-2025-29927: Verified Repro With Script Download

CVE-2025-29927: Next.js middleware authorization bypass via x-middleware-subrequest

CVE-2025-29927 is verified against next · npm. Affected versions: >=11.1.4 <12.3.5, >=13.0.0 <13.5.9, >=14.0 <14.2.25, >=15.0 <15.2.3. Fixed in 12.3.5, 13.5.9, 14.2.25, 15.2.3. Vulnerability class: Auth Bypass. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00198.

REPRO-2026-00198 next · npm Auth Bypass Jul 2, 2026 CVE entry ↗ .txt
Severity
CRITICAL
CVSS
9.1
Confidence
HIGH
Reproduced in
37m 24s
Tool calls
180
Spend
$2.62
01 · Overview

What Is CVE-2025-29927?

CVE-2025-29927 is a critical severity (CVSS 9.1) authorization bypass (CWE-285) in Next.js, where an external request that includes the internal x-middleware-subrequest header can cause Next.js middleware — and any authorization checks it performs — to be skipped entirely. Pruva reproduced it (reproduction REPRO-2026-00198).

02 · Severity & CVSS

CVE-2025-29927 Severity & CVSS Score

CVE-2025-29927 is rated critical severity, with a CVSS base score of 9.1 out of 10.

CRITICAL threat level
9.1 / 10 CVSS base
Weakness CWE-285 — Improper Authorization

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

Affected next Versions

next · npm versions >=11.1.4 <12.3.5, >=13.0.0 <13.5.9, >=14.0 <14.2.25, >=15.0 <15.2.3 are affected.

How to Reproduce CVE-2025-29927

$ pruva-verify REPRO-2026-00198
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00198/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2025-29927

Authorization bypass — reproduced
  • reached the target end-to-end
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

HTTP header x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware

Attack chain
  1. Next.js self-hosted /protected route via next start
How the agent worked 466 events · 180 tool calls · 37 min
37 minDuration
180Tool calls
140Reasoning steps
466Events
1Dead-ends
Agent activity over 37 min
Support
13
Hypothesis
2
Repro
89
Judge
27
Variant
231
Coding
99
0:0037:24

Root Cause and Exploit Chain for CVE-2025-29927

Versions: >= 11.1.4 < 12.3.5, >= 13.0.0 < 13.5.9, >= 14.0.0 < 14.2.25, >= 15.0.0 < 15.2.3

CVE-2025-29927 is an authorization-bypass vulnerability in self-hosted Next.js applications. Next.js uses the internal x-middleware-subrequest header to prevent recursive middleware execution when a middleware function internally re-issues a request. In vulnerable versions, this header is not stripped from externally originating HTTP requests, so an attacker can add x-middleware-subrequest with a value that matches the middleware name (e.g., middleware:middleware:middleware:middleware:middleware) and cause the middleware to be skipped entirely. When applications rely on middleware for authentication or authorization, this allows unauthenticated access to protected routes.

  • Package/component affected: next (npm package)
  • Affected versions: >= 11.1.4 < 12.3.5, >= 13.0.0 < 13.5.9, >= 14.0.0 < 14.2.25, >= 15.0.0 < 15.2.3
  • Patched versions: 12.3.5, 13.5.9, 14.2.25, 15.2.3
  • Risk level and consequences: Critical. An unauthenticated remote attacker can bypass middleware-based access controls, including authentication and authorization checks, on self-hosted Next.js deployments. This can lead to unauthorized access to protected resources, data disclosure, and privilege escalation. Vercel-hosted deployments are not affected because the edge filters the internal header.

Impact Parity

  • Disclosed/claimed maximum impact: The advisory and CVE describe an authorization bypass. The bundled ticket.json additionally claims library_api surface and code_execution impact, which does not match the actual vulnerability.
  • Reproduced impact from this run: We confirmed the middleware bypass on Next.js 14.2.24: a request to /protected without authentication is blocked with HTTP 401, while the same request with x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware returns HTTP 200 and the protected content. The fixed version 14.2.25 continues to block both requests with HTTP 401.
  • Parity: partial — the actual authorization bypass is fully reproduced, but the submitted library_api/convert_document/code_execution claim does not match the observed api_remote/authz_bypass surface and impact.
  • Not demonstrated: Code execution, memory corruption, or any other impact class beyond middleware authorization bypass and protected content disclosure.

Root Cause

The Next.js middleware runner (packages/next/src/server/web/sandbox/sandbox.ts) checks the incoming request headers for x-middleware-subrequest. It splits the header value on : and compares the entries against params.name (the registered middleware name). If the list already contains the middleware name, the runner assumes the request is an internal subrequest generated by the middleware itself and short-circuits the execution by returning NextResponse.next().

In vulnerable versions, filterInternalHeaders (packages/next/src/server/lib/server-ipc/utils.ts) strips many internal headers but does not strip x-middleware-subrequest from externally originating requests. Consequently, an attacker can inject this header and trick the server into skipping the middleware.

The fix (commits 52a078da3884efe6501613c7834a3d02a91676d2 and 5fd3ae8f8542677c6294f32d18022731eab6fe48) introduces a per-session random middlewareSubrequestId stored in globalThis[Symbol.for('@next/middleware-subrequest-id')]:

  1. The router server generates the random ID at startup.
  2. When the middleware runner makes an internal fetch, it copies x-middleware-subrequest and also sets x-middleware-subrequest-id to the session ID.
  3. filterInternalHeaders now deletes x-middleware-subrequest unless the accompanying x-middleware-subrequest-id matches the current session ID.

This ensures that any externally injected x-middleware-subrequest header is removed before the middleware runner sees it, preventing the bypass.

Reproduction Steps

The full reproduction is implemented in bundle/repro/reproduction_steps.sh. At a high level, the script:

  1. Creates a minimal Next.js application in the prepared project cache directory (<project_cache_dir>/repo) with middleware.js and app/protected/page.js.
  2. Installs the vulnerable Next.js version 14.2.24.
  3. Builds the app and starts the production server with next start on 127.0.0.1.
  4. Performs a health check on the root route, then issues two requests to /protected:
    • Normal request without authentication headers → expected 401.
    • Malicious request with x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware → expected 200 on vulnerable, 401 on fixed.
  5. Repeats the same procedure for the fixed version 14.2.25 as a negative control.
  6. Writes the runtime evidence to bundle/repro/runtime_manifest.json.

Expected evidence of reproduction:

  • Vulnerable 14.2.24: normal request returns 401, bypass request returns 200 with body containing secret-data.
  • Fixed 14.2.25: both normal and bypass requests return 401.

Evidence

Key artifacts written by the script:

  • bundle/logs/vuln-1-summary.txt and bundle/logs/vuln-2-summary.txt: [vuln attempt N] normal=401 bypass=401 bypass_poly=200
  • bundle/logs/fixed-1-summary.txt and bundle/logs/fixed-2-summary.txt: [fixed attempt N] normal=401 bypass=401 bypass_poly=401
  • bundle/logs/vuln-1-bypass-poly-body.html and bundle/logs/vuln-2-bypass-poly-body.html: contain the rendered protected page with <div>secret-data</div>, proving the protected route was reached without authentication.
  • bundle/logs/nextjs-vuln-*.log and bundle/logs/nextjs-fixed-*.log: server logs for each attempt.
  • bundle/repro/runtime_manifest.json: runtime evidence manifest with entrypoint_kind: api_remote and the list of proof artifacts.
  • bundle/logs/vuln-*-build.log and bundle/logs/fixed-*-build.log: build logs for each version.

Environment details:

  • Node.js v24.15.0
  • Next.js 14.2.24 (vulnerable) and 14.2.25 (fixed)
  • React 18.3.1
  • Server bound to 127.0.0.1 on ports 3000–3003

Recommendations / Next Steps

  1. Upgrade to the patched version: 12.3.5, 13.5.9, 14.2.25, or 15.2.3 (depending on the major release in use).
  2. Edge mitigation until patched: configure the reverse proxy or edge layer to drop the x-middleware-subrequest header from all incoming client requests. Vercel already does this automatically.
  3. Do not rely solely on middleware for critical authorization without additional defense-in-depth controls at the route or backend layer.
  4. Testing recommendations: Add an integration test that sends x-middleware-subrequest with a polyglot value to every middleware-protected route and asserts that the response is still blocked (e.g., 401 or 307 to login). The Next.js upstream test suite added exactly this test in the fix commits.

Additional Notes

  • Idempotency confirmation: The reproduction script was run twice consecutively in a clean environment. Both runs produced identical results: vulnerable 14.2.24 allowed the bypass (200), and fixed 14.2.25 blocked it (401).
  • Claim metadata mismatch: The bundled ticket.json claims claimed_surface: library_api and required_entrypoint_kind: convert_document with expected_impact: code_execution. This does not match the actual ticket (ticket.md) or the real vulnerability, which is a Next.js web-server middleware authorization bypass (api_remote surface) with authz_bypass impact. The reproduction and verdict therefore treat the actual vulnerability as confirmed at the api_remote surface while marking the claim as partial due to scope_mismatch/impact_mismatch with the submitted metadata.
  • Limitations: The reproduction uses a minimal auth-cookie check. Real-world applications may enforce more complex middleware logic, but the bypass mechanism (skipping the middleware entirely) is independent of the specific middleware implementation.

CVE-2025-29927 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:002:14
0:00
session startedaccounts/fireworks/models/kimi-k2p7-code · CVE-2025-29927 · REPRO-20
0:06
0:07
0:08
0:10
0:11
web search
0:33
0:33
extract_facts
no facts extracted
0:34
0:34
0:34
supportrepro
0:56
0:58
0:58
0:58
1:03
1:04
1:04
1:04
1:20
1:20
1:39
1:39
$node --version && npm --version && jq --version && curl --version | head -1
0.2s
v24.15.0
11.12.1
jq-1.8.1
curl 8.18.0 (x86_64-pc-linux-gnu) libcurl/8.18.0 OpenSSL/3.5.5 zlib/1.3.1 brotli/1.2.0 zstd/1.5.7 libidn2/2.3.8 libpsl/0.21.2 libssh2/1.11.1 nghttp2/1.68.0 librtmp/2.3 mit-krb5/1.22.1 OpenLDAP/2.6.10
2:09
2:09
#!/bin/bash
set -euo pipefail

ROOT="${PRUVA_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
LOGS="$ROOT/logs"
REPRO_DIR="$ROOT/repro"
mkdir -p "$LOGS"
mkdir -p "$REPRO_DIR"

# Determine project cache directory / app directory
CACHE_CONTEXT="$ROOT/project_cache_context.json"
APP_DIR="$ROOT/artifacts/nextjs-app"
if [ -f "$CACHE_CONTEXT" ]; then
  CACHE_DIR=$(jq -r '.project_cache_dir // empty' "$CACHE_CONTEXT")
  if [ -n "$CACHE_DIR" ] && [ -d "$CACHE_DIR" ]; then
    APP_DIR="$CACHE_DIR/repo"
  fi
fi
mkdir -p "$APP_DIR"
2:11
2:11
$chmod +x bundle/repro/reproduction_steps.sh && ls -la bundle/repro/
total 16
drwxr-xr-x 2 vscode vscode 4096 Jul  1 22:14 .
drwxr-xr-x 3 vscode vscode 4096 Jul  1 22:14 ..
-rwxr-xr-x 1 vscode vscode 6715 Jul  1 22:14 reproduction_steps.sh
2:14

Artifacts and Evidence for CVE-2025-29927

Scripts, logs, diffs, and output captured during the reproduction.

08 · How to Fix

How to Fix CVE-2025-29927

Upgrade next · npm to 12.3.5, 13.5.9, 14.2.25, 15.2.3 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2025-29927 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2025-29927

How does the CVE-2025-29927 middleware bypass work?

An attacker sends a request that includes the x-middleware-subrequest header, which Next.js normally reserves for internal use to prevent infinite middleware recursion. Because the framework does not verify that the header originates internally, the external request is treated as an internal subrequest and the middleware — including any auth checks it implements — is skipped, letting the request reach protected routes directly.

Which Next.js versions are affected by CVE-2025-29927, and where is it fixed?

Next.js versions >=11.1.4 <12.3.5, >=13.0.0 <13.5.9, >=14.0 <14.2.25, and >=15.0 <15.2.3 are affected. It is fixed in 12.3.5, 13.5.9, 14.2.25, and 15.2.3 — upgrade to the corresponding fixed release for your major version line.

How severe is CVE-2025-29927?

It is rated critical with a CVSS score of 9.1, since it can bypass authorization/authentication middleware entirely with a single crafted header.

How can I reproduce CVE-2025-29927?

Download the verified script from this page and run it in an isolated environment against an affected Next.js version with middleware-based authorization. It sends a request carrying the x-middleware-subrequest header to a protected route and shows the middleware — and its auth check — being skipped, then confirms a fixed version enforces the check.
11 · References

References for CVE-2025-29927

Authoritative sources for CVE-2025-29927 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.