React2Shell(CVE-2025-55182):未文档化的协议如何导致 React 严重 RCE
📅 2026年5月9日 ⏱ 阅读时间 15 分钟 🏷️ React, 安全, RCE, CVE
什么是 React2Shell?
React2Shell 是 React 核心中的一个严重 RCE 漏洞——具体来说,在于 Flight 协议,即支撑 React Server Components(RSC)和 React Server Functions(原名 Server Actions)的内部传输机制。
攻击者通过向漏洞服务器发送特制的 Flight 载荷,可以诱骗 React 在服务器上执行任意 JavaScript。这意味着服务器完全沦陷:读取环境变量、访问数据库、窃取机密信息、渗透内部基础设施。
- CVE: CVE-2025-55182
- CVSS: 严重(10.0)——远程代码执行
- 披露: 2025年11月30日报告给 Meta
- 修复: 2025年12月3日(React 19.1.1,Next.js 14.2-sdk-63)
- 影响: 所有使用 React Server Components 的 Next.js 应用
发现故事
Lachlan Davidson 是一名专业安全研究员,他最初并不是为了找 React 的漏洞。他想理解 Flight 协议——React Server Functions 使用的奇怪消息格式——以便更有效地渗透测试现代 Web 应用。
未文档化的协议
React 中的 Server Actions 使用一种类似 JSON 但"加了料"的格式在浏览器和服务器之间传输数据:
0=[{ "a": "$$undefined", "b": "$1:foo:bar" }]&1=...
这种格式名为"Flight",是 React 的内部序列化协议。它支持 JSON 无法原生表示的数据类型——Date、BigInt、Map、Set、类型化数组、Promise、循环引用和跨块引用。但关键的是,没有规范文档——只有源代码。
原型属性枚举——第一个危险信号
Flight 允许使用 $x:y 语法引用对象的属性。Davidson 发现这包括原型链中的继承属性。例如,这样的 Flight 载荷:
0 = { "foo": "$1:toString" }
&1 = 123
会解析为:
0 = { "foo": Number.prototype.toString }
Next.js 创建者、Vercel 创始人 Guillermo Rauch 称此为"安全检查中一个明显的疏忽。"
将 Flight 武器化——从好奇到利用
类型强制攻击
考虑这个看似无害的 Server Function:
async function sayHello(name: string): string {
'use server'
return '你好, ' + name + '!'
}
TypeScript 说 name 是字符串。但 TypeScript 注解在运行时不会被强制执行。攻击者可以发送一个 Flight 载荷,其中 name 是一个带有恶意 toString 方法的对象——当服务器拼接 "你好, " + name 时,它会调用攻击者的函数。
Thenable 突破
真正的突破来自理解 await 的工作原理。React 使用 await decodeReply(...) 解码传入的 Flight 载荷。在底层,await 调用 Promise.resolve(),后者会自动展开thenable——任何带有 .then 方法的对象。
Davidson 意识到他可以构造一个 Flight 载荷,解析结果是一个带有攻击者控制的 .then 函数的 thenable 对象:
{
then: Array.prototype.push
}
当 await decodeReply() 解析这个对象时,它会自动调用 .then(resolve, reject)——用两个参数调用攻击者的函数。
链式调用——无限函数调用
通过链接 thenable(一个 thenable 解析到另一个 thenable),攻击者可以触发无限次连续函数调用,每次都能控制参数。这是完整利用的基础。
最终利用——Chunk.prototype.then
在 React 的 Flight 实现中,每个块由继承 Promise 的 Chunk 对象表示。Davidson 发现他可以:
- 使用
$@x语法创建 Promise 引用 - 通过原型属性枚举提取
Chunk.prototype.then - 注入到攻击者控制的对象中
- 当
await解析该对象时,它调用 React 自己的内部.then处理器——但作用于攻击者的伪造块而非真实块
0 = { "then": "$1:then" }
&1 = "$@2"
这使攻击者完全控制了 React 的块内部状态——包括服务器清单,即告诉 Flight 对给定 Server Function ID 执行哪个模块和函数的映射。
通过覆盖服务器清单将 Server Function ID 映射到 child_process 或类似模块,攻击者可以在服务器上实现任意代码执行。
时间线
| 日期 | 事件 |
|---|---|
| 2025年11月24日(周一) | Davidson 开始逆向工程 Flight 协议 |
| 11月25日(周二) | 发现原型属性枚举——开始在应用层面将 Flight 武器化 |
| 11月27日(周四) | 在 decodeReply 中发现 thenable 漏洞 |
| 11月28日(周五) | 突破:发现 Chunk.prototype.then 注入向量 |
| 11月29日(周六) | 发现 globalThis.__FLIGHT_SERVER_MANIFEST__ 篡改路径 |
| 11月30日(周日) | 实现完整 RCE——报告给 Meta |
| 2025年12月3日 | Meta 发布修复并公开 CVE-2025-55182 |
| 2025年12月至2026年5月 | 社区发布完整的披露和详细分析 |
谁受影响?
每个使用 React Server Components 或 Server Functions 的 Next.js 应用都可能受影响。包括:
- 所有使用 Next.js App Router 的网站(自 Next.js 13 以来的默认模式)
- 任何直接使用 React Flight 协议的框架
- 自托管部署和 Vercel 部署(Vercel 在服务端应用了修复)
npm list react next 验证版本。
行业教训
1. TypeScript 不是安全边界
TypeScript 的类型注解仅限编译时。任何接受来自不受信任来源(网络、用户、其他服务)输入的函数都必须在运行时验证类型。这对 Server Actions 尤其关键,因为"客户端"可以发送任意 JavaScript 对象。
2. 协议设计者:文档化和沙箱化
Flight 协议没有规范说明,导致安全审查极其困难。接受来自不受信任客户端的结构化数据的协议需要正式规范、输入验证以及严格的白名单允许类型和操作。
3. Promise/Thenable 安全性
await 关键字和 Promise.resolve() 隐式展开任何 thenable——包括攻击者控制的 thenable。任何对用户可控数据执行 await 的代码路径都可能被利用。仅在验证解析值后再使用 Promise.resolve(x).then(v => ...)。
4. 安全研究中的认知偏差
Davidson 本人都注意到一个"荒谬的循环"——因为 React 有如此强大的安全记录,他和合作者 Sylvie Mayer 都假设这个漏洞一定不存在。这个盲点差点让他们完全错过这个利用方式。
结论
React2Shell 是有史以来发现的最重要的 React 漏洞之一——框架核心传输协议中的严重 RCE,影响数百万网站。这是一个关于未文档化协议、类型安全的假象以及框架内部机制隐式信任危险的警示故事。
如果你在运行 Next.js 网站,请确认使用的是已修复版本。如果你在构建接受结构化输入的框架,请记住:每个反序列化边界都是潜在的 RCE 表面。文档化你的协议。验证一切。永远不要假设经过实战检验的框架不会出现严重漏洞。