## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-DWISISWANT0-CVE-2025-55182
# CVE-2025-55182
本仓库包含对 **CVE-2025-55182** 的概念验证(PoC)复现,这是一个 React Server Components (RSC) 中的严重安全漏洞,允许未经身份验证的任意代码执行。
## 描述
该漏洞存在于 React Server Components 从客户端请求反序列化 "Server Actions" 的方式中。具体来说,`requireModule` 函数未能验证请求的导出名称是否是模块的直接属性。这允许攻击者访问导出函数的 `constructor` 属性,从而获取全局 `Function` 构造函数的引用,进而可用于执行任意代码。
## 复现
本 PoC 使用一个最小的 Node.js 环境来隔离 `react-server-dom-webpack` 库中的漏洞,确保该利用演示的是库本身的问题,而非框架配置错误。
### 前置条件
* Node.js
* `npm`
### 安装
root@kitploit:~
npm install
> [!NOTE] `package.json` 已固定为漏洞版本 `19.0.0`。
### 概念验证
1. **启动存在漏洞的服务器**
该脚本设置了一个原始 HTTP 服务器,使用存在漏洞的 React 运行时解码请求。
root@kitploit:~
# tty1
node --conditions react-server server.js
2. **运行利用脚本**
在另一个终端中运行利用程序。该程序向服务器发送一个恶意的 Flight 负载。
root@kitploit:~
# tty2
node exploit.js id
你会看到命令输出在响应中返回:
**预期输出:**
root@kitploit:~
Response: uid=0(root) gid=0(root) groups=0(root)
## 分析
**漏洞为何会发生?**
`ReactFlightDOMServerNode.js` 中的 `requireModule` 函数基本直接信任了客户端发送的任何 `name`。它执行了 `moduleExports[metadata[NAME]]`,而没有检查该属性是否确实应该被暴露。因此,如果客户端说“老哥,我要这个属性”,服务器就直接回答“没问题!给你,老铁”。
**为什么允许访问任意属性是个坏主意?**
因为这基本上让任何人都能触及原型链,甚至包括 **`constructor`** ,这非常危险。如果模块恰好导出的是一个函数(比如 `module.exports = () => {}`),那么它的 `constructor` 就是全局的 `Function` 构造函数。
**获取`Function` 构造函数为何意味着 RCE?**
一旦攻击者获取了 `Function` 构造函数,他们就可以滥用“绑定的服务器动作”(Bound Server Action)功能。他们将包含恶意 JavaScript 的字符串绑定到该构造函数上(本质上将其转换为 `new Function("evil code")`)。一旦执行,服务器就会运行他们放入的任意代码。
**React 为何会实际执行那个恶意函数?**
因为服务器动作可以通过 ID 触发。如果攻击者构造的负载中包含一个指向其 `module#constructor` 引用的动作 ID,React 会像处理普通动作一样解析它并执行。那个“动作”实际上就是他们的恶意函数。
**为什么这些都没有被验证?**
系统默认假设服务器引用元数据中的 `id` 和 `name` 始终指向开发者定义的有效导出。没有使用类似 `hasOwnProperty` 的安全检查来确保请求的属性是真正的导出,而不是从原型链继承而来的。
### 为什么使用 `server.js` 而不是 Next.js?
我使用原始的 `server.js`(以及辅助文件 `webpack-runtime.js`)手动配置 React Server Components 运行时。这使我们能够:
1. **强制存在漏洞的配置:** 只有在模块以函数形式导出时(`module.exports = fn`),利用才能工作。真正的打包工具可能会根据其配置改变导出包装方式。
2. **隔离问题:** 这让我们能展示问题出在 `react-server-dom-webpack` 内部,而不是 Next.js。
3. **重建打包工具环境:** `react-server-dom-webpack` 假设它在 Webpack 包内运行。我们的 webpack-runtime.js 提供了它期望的全局变量(`__webpack_require__`, `__webpack_chunk_load__`)。 这不是在模拟漏洞,而是为库提供其运行所需的最小运行时。
### 备注
关于"无效的 PoC"已有讨论,这些 PoC 仅在开发者故意暴露危险内容(如 `child_process.exec`)时才有效。
本 PoC 不属于此类。它在正常、安全的配置下也能工作。
1. **暴露的函数是无害的** 应用程序暴露了一个简单的 `updateProfile` 函数,它只返回一个字符串,没有任何可疑内容,没有 shell 命令。
2. **利用完全跳过了该函数** 漏洞允许攻击者忽略安全的导出,直接跳转到 `updateProfile.constructor`,即全局 `Function` 构造函数。
3. **核心问题是属性访问** React 不应该允许访问 `.constructor`。开发者并没有打算暴露 `Function` 构造函数,而是不安全的反序列化替他们做了。
唯一真正的要求是模块直接导出一个函数(`module.exports = fn`),这在 CommonJS 和许多打包工具配置中非常常见。
### 负载
`exploit.js` 中的负载构建了一个包含三个块的 React Flight 消息:
* **块 0:** 指向块 1 中定义的服务器引用。
* **块 1:** 声明服务器引用:
* `id`:`"user-profile-action#constructor"`,意思是“给我构造函数”。
* `bound`:指向包含参数的块 2。
* **块 2:** `["console.log('nice try, diddy!')"]`:恶意代码字符串。
当 React 反序列化时:
1. 解析 `user-profile-action`。
2. 读取 `.constructor` 属性 => 获得全局 `Function`。
3. 将攻击者提供的字符串绑定到它。
4. 实际执行: `new Function("console.log('nice try, diddy!')")`
这就是 RCE!
## 缓解措施
立即升级到修复版本:
* `react-server-dom-webpack` >= 19.0.1
* `react-server-dom-parcel` >= 19.0.1
* `react-server-dom-turbopack` >= 19.0.1
该补丁引入了 `hasOwnProperty` 检查,以防止访问继承属性,并限制了 `base64` 文件上传。
如果针对修复版本运行此 PoC,服务器将崩溃或报错:
root@kitploit:~
$ node --conditions react-server server.js
Listening on http://localhost:3000
/path/to/CVE-2025-55182/node_modules/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js:2726
resolvedValue = resolvedValue.bind.apply(
^
TypeError: Cannot read properties of undefined (reading 'bind')
at /path/to/CVE-2025-55182/node_modules/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js:2726:43
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Node.js v20.19.3
这确认了利用未能访问 `constructor` 属性(返回了 `undefined` 而非 `Function`),因此后续的 `.bind` 调用失败了。
## 免责声明
此代码仅用于教育和测试目的。请勿将本利用用于您不拥有或未获得明确测试许可的系统。
## 许可证
以 `DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE` 发布。