本文档描述了我对 CVE-2025-55182 的个人研究过程,包括已确认的发现和实验。这代表了我调查的真实记录,包括最初的错误假设和最终的突破。
经过对 CVE-2025-55182(CVSS 10.0)的广泛调查,我最初的结论是:自动 RCE 尚未被公开演示,且利用需要特定于应用的 gadgets。这个结论是错误的。
2025年12月5日,在收到另一位独立研究员(@maple3142,X 平台)的额外见解后,我成功在纯 vanilla Next.js 上复现了完全未经身份验证的 RCE,无需任何特定于应用的代码漏洞。
我最初关注的是 React 19.0.1 补丁中的第一处更改:
漏洞版本(19.0.0):
return fn.bind.apply(fn, [null].concat(_ref));
已修补版本(19.0.1):
if (Array.isArray(promiseValue)) {
promiseValue = promiseValue.slice(0);
} else {
promiseValue = [];
}
我假设攻击路径是通过 fn.bind.apply() 使用恶意对象而非数组。我能够使用攻击者控制的 bound 通过 $ACTION_REF_ 演示参数注入到 Server Actions:
curl -X POST http://localhost:9000/ \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"<ACTION_ID>","bound":["; id #","/etc/passwd"]}'
结果: 参数成功注入到 Server Action。然而,这仅在目标函数不安全地使用这些参数时才导致 RCE。
对补丁的更深入检查揭示了 getOutlinedModel() 中的另一个重要更改:
漏洞版本:
for (key = 1; key < reference.length; key++)
parentObject = parentObject[reference[key]];
已修补版本:
if (hasOwnProperty.call(value, name)) {
value = value[name];
}
这种易受攻击的行为允许使用如下引用进行原型链遍历:
$1:__proto__:constructor:constructor
在研究过程中,我测试了一个包含 .then 属性的 thenable 对象:
{"then": "$1:__proto__:constructor:constructor"}
当 JavaScript 通过 await 处理此对象时:
.then 属性并将对象视为 Promiseobj.then(resolve, reject)then 解析为 Function.constructor,JavaScript 尝试执行 Function(resolve, reject)观察到的结果:
SyntaxError: Unexpected token 'function'
at Object.Function [as then] (<anonymous>)
当 Function.constructor 被调用为:
Function(resolve, reject)
// resolve.toString() = "function () { [native code] }"
// Function 尝试将其解析为代码 → SyntaxError
参数 resolve 和 reject 始终是原生 Promise 函数。Function 尝试将第一个参数解释为源代码,这是无效的 JavaScript。
我的研究在此停滞。 我得出结论,如果没有应用程序特定的 gadget,不可能控制 Function.constructor 的参数。
在发布我的初步发现后,另一位独立研究员向我指出了一个我遗漏的关键点:$B(Blob)反序列化接收器。
在编译后的 React Flight 服务器代码中(在 TypeScript 源码中不可见),存在:
case "B":
return response._formData.get(response._prefix + id);
位置:
[email protected]cjs/react-server-dom-webpack-server.node.unbundled.development.js[email protected]/dist/compiled/react-server-dom-webpack/这段代码允许 React 使用源自攻击者控制输入的值调用 response._formData.get(),未经任何验证。
| 方法 | 传给 Function.constructor 的参数 | 结果 |
|---|---|---|
| Thenable(第三阶段) | resolve, reject(原生函数) | ❌ SyntaxError |
| Blob + 投毒后的 Response 对象 | _prefix(攻击者控制的字符串) | ✅ RCE |
通过组合:
$1:__proto__:then → Chunk.prototype.then)_response 对象,其中包含:
_formData.get 设置为 Function.constructor_prefix 设置为任意 JavaScript 代码$B 引用的内部模型case "B": 处理器执行:
Function.constructor("<攻击者代码>" + id)
这完全绕过了参数绑定的限制。
声称能实现 RCE 的热门 GitHub PoC 使用了以下 Action ID:
"child_process#execSync""vm#runInThisContext"这些是伪造的。 Next.js 只接受由应用程序定义的 Action ID。无效 ID 会报错:
TypeError: Cannot read properties of undefined (reading 'workers')
然而,真正的利用不需要伪造的 Action ID。任何有效的 Server Action ID 都可以工作。
我在一个仅包含以下内容的最小 Next.js 15.0.3 + React 19.0.0 应用程序上测试了这一利用链:
async function myAction(data) {
"use server";
console.log("Server Action called with:", data);
return { success: true, received: data };
}
结果: 确认完全 RCE。该应用程序不包含任何不安全代码,没有 eval,没有 execSync,没有 gadgets。
截至2025年12月5日,$B 接收器仍然存在于 Next.js 15.0.5 中(被认为是已修补的版本)。
验证:
$ npm pack [email protected]
$ tar -xzf next-15.0.5.tgz
$ grep -A5 'case "B":' package/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js
结果:
case "B":
return response._formData.get(response._prefix + obj);
该代码与易受攻击的版本完全相同。
由于发现该漏洞可能并未在声称“已修复”的版本中被完全修补,我将扣留完整的概念验证载荷,等待与 Vercel 和 Meta 安全团队的验证。
本文档中提供的技术细节足以理解漏洞机制,但有意不完整,以防止立即被利用。
| 技术 | 状态 |
|---|---|
通过 bound 进行参数注入 | ✅ 已确认(影响有限) |
| 原型遍历 | ✅ 已确认 |
通过 thenable 访问 Function.constructor | ✅ 已确认(但单独不可利用) |
| 问题 | 影响 |
|---|---|
$B(Blob)反序列化接收器 | ❌ 严重 - 可实现参数控制 |
| 检查编译后代码 vs 源代码 | ❌ 该接收器仅存在于编译输出中 |
| Response 对象投毒机制 | ❌ 可绕过所有保护 |
CVE-2025-55182 可在纯 vanilla Next.js 应用程序上实现完全未经身份验证的 RCE。
$B 反序列化的见解$B 反序列化是否仍然接受任意的 _response 对象Next-Action 头部$@、__proto__、$B 模式的复杂 multipart 载荷$B 反序列化的突破性见解由 X 平台上的研究员(@maple3142)提供最后更新: 2025年12月5日
| 方面 | 发现 |
|---|
| 是否需要身份验证? | ❌ 否 |
| 是否需要应用程序 gadget? | ❌ 否 |
| 是否在纯 vanilla Next.js 上工作? | ✅ 是 |
| 所需请求数量 | 1 个 POST |
| 受影响版本 | Next.js ≤15.0.4 + React 19.0.0 |
| CVSS 评分 | 10.0(合理) |
| 检测易受攻击的版本 |
| ✅ 已确认 |