CVE-2025-55182 (React2Shell) 的技术分析
2025-12-8
| 2025-12-9
字数 2567阅读时长 7 分钟
beizhu
type
Post
status
Published
date
Dec 8, 2025
slug
summary
漏洞概述名称:React2Shell (CVE-2025-55182)。 • 影响:React Server Components (RSC) 的服务端使用。 • 原理:RSC 有效载荷处理逻辑中存在不安全的反序列化问题。 • 后果:攻击者可以通过恶意的 Payload 在服务器上执行任意代码。
tags
Next.js
React
category
技术
icon
password
利用 JavaScript 语言特性(Thenables)和 React 内部机制(Flight 协议 + 原型链污染)的组合拳。
  • React Flight 协议:RSC 使用一种特殊的序列化格式(称为 "Flight")将组件流式传输到客户端。它使用标记(如 $, $@)来表示引用和块。
  • Thenables 与 Await:JS 运行时会自动调用具有 .then 方法的对象(即 "Thenables")。如果攻击者能构造一个带有 .then 属性的对象,并让服务器对其进行 await 操作,就能劫持执行流。
  • 漏洞成因:Next.js/React 在处理传入的数据块流时,期望特定的块(如 Chunk 0)是数组。但代码未验证这一点。攻击者可以将 Chunk 0 替换为包含 .then 键的对象,从而触发 JavaScript 的 Promise 解析机制,进而执行函数。
 
简单来说,攻击者并没有直接发送恶意脚本文件,而是发送了一串经过精心构造的 JSON 数据,欺骗 React 服务器把这串数据“误以为”是内部的一个组件块(Chunk),并让 React 自己去执行初始化逻辑,最终导致代码执行。
下面我将分步骤为您详细拆解其技术原理:

1. 核心前置知识

要理解这个漏洞,需要了解三个关键点:
  • Thenables 与 await 的特性:
    • 在 JavaScript 中,如果一个对象拥有名为 .then 的方法,JS 就会认为它是一个 Promise(称为 "Thenable")。当你对这个对象使用 await 时,JS 运行时会自动执行这个 .then 方法。
    • 危险点:如果攻击者能构造一个带有 .then 的对象,并让服务器去 await 它,攻击者就能控制程序的执行流。
  • React "Flight" 协议与引用:
    • React Server Components (RSC) 使用一种叫 Flight 的文本协议来传输数据。它允许通过特殊符号引用其他数据。
    • 例如:$1:key 表示“去引用 ID 为 1 的块,并获取它的 key 属性”。
    • 危险点:这允许攻击者访问服务器内存中对象的原型链(__proto__),从而获取到某些敏感的内部函数(比如构造函数 constructor)。
  • React 内部的 Chunk 对象:
    • React 内部处理流式数据时,会创建 Chunk 对象。这些对象内部也有一个 .then 方法(用于处理流的异步加载)。

2. 攻击链详细步骤

攻击者构造了一个复杂的“Gadget Chain”(利用链),步骤如下:

第一步:构造“伪造的 Chunk”(特洛伊木马)

攻击者发送一个 multipart 请求,其中包含一个恶意的 JSON 对象(我们称之为 Chunk 0)。Next.js/React 本来期望接收一个数组,但没有做校验,接收了这个对象。
这个对象被设计得极其像 React 内部的 Chunk 类:
JSON

第二步:劫持 .then (关键点 A)

React 服务器在解析数据时,会尝试 await 这个 Chunk 0。
  • 攻击者通过 Flight 协议的引用功能($1:__proto__:then),将这个伪造对象的 .then 属性,指向了 React 真实的 Chunk.prototype.then 函数
  • 结果:当 JS 运行时尝试执行 await chunk0 时,它实际上运行了 React 内部核心代码 Chunk.prototype.then.call(chunk0)
  • 为什么要这么做? 因为攻击者不能直接发送 JS 代码去执行,他必须借用服务器上已有的“合法代码”来干坏事。

第三步:触发 initializeModelChunk

一旦进入 React 的 Chunk.prototype.then 方法,代码会检查 this.status
  • 攻击者在 JSON 里已经把 status 设为 "resolved_model"
  • React 看到状态已完成,于是调用内部函数 initializeModelChunk(this) 来处理这个块。

第四步:上下文投毒 (Context Poisoning - 关键点 B)

现在,代码执行进入了 initializeModelChunk。这个函数会读取当前 Chunk 的 _response 属性来获取环境信息。
  • 正常情况下,_response 包含合法的 React 上下文。
  • 但这里的 this 是攻击者伪造的对象,所以 _response 也是攻击者完全控制的恶意对象。
JavaScript

第五步:利用 reviveModel 完成 RCE

initializeModelChunk 内部会调用 reviveModel,试图解析数据。在解析过程中,React 逻辑中有一段代码(原本用于处理文件上传或表单数据)会尝试调用:
JavaScript
由于攻击者已经把 _formData.get 替换成了全局的 Function 构造函数(类似于 new Function()),且 key(或前缀)被替换成了恶意代码字符串。
最终执行变成了:
JavaScript
这就在服务器端直接执行了攻击者提供的任意 JavaScript 代码。

总结

这个漏洞的精妙之处在于:
  1. 欺骗:让数据伪装成 Promise(Thenable)。
  1. 借刀杀人:利用 React 自身的引用机制,偷取了 Chunk.prototype.thenFunction 构造函数。
  1. 偷梁换柱:在 React 以为自己在处理正常的内部 Chunk 初始化时,实际上是在用攻击者提供的恶意参数(代码字符串)去喂给 Function 构造函数。
这就是为什么它被评为 CVSS 10.0 分(最高危),因为它不需要身份验证,且能直接在服务器核心逻辑中执行任意代码。
 
 
 
“React2Shell 漏洞的本质,是攻击者利用 React 的‘引用机制’搭建梯子,爬上了‘原型链’的高墙,最后利用 JS 对‘Promise’的盲目信任,打开了执行任意代码的大门。”

Q&A

Q1:如果我们只用了 Next.js 做静态页面(SSG)或者只用了客户端渲染(CSR),还会受影响吗?
  • 回答思路:区分 RSC(服务端组件)和传统 React 的区别。
  • 参考答案: “这个漏洞特指 React Server Components (RSC)。如果我们的应用完全是静态导出(Static Export),或者还在使用老版本的 Pages Router 且没有启用 Server Actions/RSC 功能,那么受攻击面的风险会低很多。 但要注意,Next.js 的新版 App Router 默认就是开启 RSC 的。只要服务端有解析处理客户端传来的 Flight 协议数据流(特别是 Server Actions),就有风险。最稳妥的方式还是升级版本。”
Q2:这个漏洞是 React 的锅还是 Next.js 的锅?
  • 回答思路:厘清责任归属,核心在于 React 的底层库。
  • 参考答案: “根源在于 React 核心库(具体是 react-server 包)处理反序列化的逻辑。 但是,Next.js 是目前 RSC 落地最广泛的框架,它把请求体(Body)直接喂给了 React 的处理函数,所以 Next.js 应用是重灾区。简单说:漏洞在 React 源码里,但在 Next.js 项目中被‘引爆’了。”
Q3:JS 不是单线程且比较安全吗?为什么传一段 JSON 就能直接执行代码(RCE)?
  • 回答思路:解释 JS 动态特性的“双刃剑”——原型链和 Thenable。
  • 参考答案: “这正是 JS 灵活性的代价。漏洞利用了两个特性:
      1. 原型链污染:React 的 Flight 协议允许通过字符串引用(如 $1:__proto__:constructor)来访问内存中的任意对象构造器,这就把 Function 构造函数暴露出来了。
      1. Await 的机制:JS 的 await 很‘傻’,只要对象有个 then 方法,它就去执行。攻击者伪造了一个带有 then 的对象,骗过了服务器,让服务器把这段恶意 JSON 当作代码逻辑跑了起来。 这本质上是一次对象注入(Object Injection)导致的反序列化漏洞。”
Q4:为什么攻击者能绕过 WAF(防火墙)?
  • 回答思路:解释流式传输和 WAF 的检测瓶颈。
  • 参考答案: “因为 RSC 是流式传输(Streaming)的。攻击者可以在 Payload 前面填充大量的垃圾数据(例如 128KB 的随机字符)。 很多 WAF 为了性能,只检查请求体的前几 KB。当垃圾数据塞满了 WAF 的缓冲区后,真正的攻击载荷(Payload)就被放行了。而且,攻击载荷是经过序列化的 Flight 协议字符串,特征比较晦涩,传统的 SQL 注入或 XSS 规则很难匹配到。”
Q5:官方补丁是怎么修的?我们以后写代码要注意什么?
  • 回答思路:解释官方修复的逻辑(类型检查)。
  • 参考答案: “官方的修复逻辑其实很朴素但有效:加强类型检查。 React 现在在处理 Chunk 时,不再盲目相信传进来的对象。它会检查这个对象是否真的是内部创建的实例(比如检查 hasOwnProperty 或者验证原型链),防止外部传入的 JSON 伪装成内部 Promise。 给我们的启示是:在服务端处理任何序列化数据(JSON, XML, YAML)时,永远不要相信客户端传来的数据结构,特别是当涉及到类实例化或异步操作时。”
 
 
React2Shell (CVE-2025-55182)
MediumMediumReact2Shell(CVE-2025–55182): A Technical Deep Dive
Searchlight CyberSearchlight CyberHigh Fidelity Detection Mechanism for RSC/Next.js RCE (CVE-2025-55182 & CVE-2025-66478) › Searchlight Cyber
Tony AliceaTony AliceaUnderstanding React Server Components | Tony Alicea
GitHubGitHubGitHub - assetnote/react2shell-scanner: High Fidelity Detection Mechanism for RSC/Next.js RCE (CVE-2025-55182 & CVE-2025-66478)
 
  • Next.js
  • React
  • 21 Lessons From 14 Years at Google是什么,为什么,怎么做 —— 谈谈 DNS 泄漏、CDN 访问优化与 Fake IP
    Loading...