← 返回 EasyTool.me

React2Shell(CVE-2025-55182):未文档化的协议如何导致 React 严重 RCE

📅 2026年5月9日 ⏱ 阅读时间 15 分钟 🏷️ React, 安全, RCE, CVE

速览: React 未文档化的 Flight 协议(React Server Components 和 Server Functions 背后的传输层)中发现了一个严重远程代码执行漏洞(CVE-2025-55182,又称 React2Shell)。安全研究员 Lachlan Davidson 于 2025年11月30日向 Meta 报告,12月3日完成修复。这是逆向工程未文档化协议导致影响数百万网站漏洞的完整故事。

什么是 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、循环引用和跨块引用。但关键的是,没有规范文档——只有源代码。

"没有文档,只有代码。" 在 React2Shell 披露之前,连协议名称"Flight"都出奇地难找。最好的信息来源不过是 X 上讨论 RSC 内部机制的几条帖子。

原型属性枚举——第一个危险信号

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 时,它会调用攻击者的函数。

⚠️ 关键教训: TypeScript 不在运行时验证类型。React Server Functions 接受原始 Flight 载荷,TypeScript 注解只提供编译时安全。

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 实现中,每个块由继承 PromiseChunk 对象表示。Davidson 发现他可以:

  1. 使用 $@x 语法创建 Promise 引用
  2. 通过原型属性枚举提取 Chunk.prototype.then
  3. 注入到攻击者控制的对象中
  4. 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 应用都可能受影响。包括:

🛡️ 如何确认是否受保护: 确保运行 React 19.1.1+、Next.js 14.2-sdk-63+ 或任何 2025年12月3日之后发布的版本。运行 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 表面。文档化你的协议。验证一切。永远不要假设经过实战检验的框架不会出现严重漏洞。