文章总结: Next.js发布了一个严重的安全漏洞公告,该漏洞可在默认配置下导致远程代码执行(RCE),无需任何先决条件。漏洞根源在于ReactServerComponents(RSC)使用冒号分隔对象属性的处理方式。文章提供了一个高置信度的HTTP请求检测方法,通过构造特殊请求触发500错误来确认漏洞存在。建议使用文章提供的检测方法或GitHub上的扫描工具检查系统是否存在此漏洞,并及时更新到已打补丁的版本。
综合评分: 91
文章分类: 漏洞分析,WEB安全,漏洞预警,安全工具,应用安全
从核弹到水弹,再到炸弹,Reactshell 漏洞又反转了?来看看漏洞详情
润霖@闪石星曜
闪石星曜CyberSecurity
2025年12月5日 12:35
天津
翻译自:https://slcyber.io/research-center/high-fidelity-detection-mechanism-for-rsc-next-js-rce-cve-2025-55182-cve-2025-66478
今天上午,Next.js 发布了一份关于一个漏洞的安全公告 [2],该漏洞可在默认配置下导致远程代码执行(RCE),且无需任何先决条件。问题的根源在于 Next.js 所使用的 React Server Components (RSC)。
在过去的一天里,我们注意到 GitHub 上出现了大量不正确的 PoC(概念验证),它们无法以高置信度真实地确认此漏洞的存在。
我们观察到的一些 GitHub PoC 完全错误地诊断了漏洞的根本原因,也忽略了关键事实:该漏洞可以在 Next.js 上被利用,而无需任何先决条件,例如上下文中存在某些特定函数。
虽然我们在下文提供了 HTTP 请求和预期的响应,但如果您更倾向于使用工具来检测一系列主机上的此问题,请查看我们针对此漏洞的 GitHub 仓库:https://github.com/assetnote/react2shell-scanner [3]。
此漏洞的原始作者证实,GitHub 上的这些 PoC 与分享给 React 和 Next.js 维护者的实际漏洞利用方式不同。请参阅作者在 https://react2shell.com/ [4] 上的消息。
虽然有多种机制可以确定资产是否正在运行 React Server Components (RSC),但仅仅验证 RSC 的存在不足以作为确定资产是否真正容易受到此 RCE 攻击的高置信度信号。
因此,我们的安全研究团队进行了调查,以确定一个可用于安全且自信地确认 Next.js 应用程序中存在此 RCE 漏洞的 HTTP 请求。
漏洞确认的 HTTP 请求
以下 HTTP 请求可用于确认漏洞的存在:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linePOST / HTTP/1.1Host: hostnameUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36 Assetnote/1.0.0Next-Action: xX-Nextjs-Request-Id: b5dce965Next-Router-State-Tree: %5B%22%22%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%2Cnull%2Cnull%5D%7D%2Cnull%2Cnull%2Ctrue%5DContent-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3SadX-Nextjs-Html-Request-Id: SSTMXm7OJ_g0Ncx6jpQt9Content-Length: 232
------WebKitFormBoundaryx8jO2oVc6SWP3SadContent-Disposition: form-data; name="1"
{}------WebKitFormBoundaryx8jO2oVc6SWP3SadContent-Disposition: form-data; name="0"
["$1:a:a"]------WebKitFormBoundaryx8jO2oVc6SWP3Sad--
当发送上述请求时,来自易受攻击版本的 Next.js 的 HTTP 响应将如下所示:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineHTTP/1.1 500 Internal Server ErrorDate: Thu, 04 Dec 2025 06:16:39 GMTContent-Type: text/x-componentConnection: keep-aliveCache-Control: no-store, must-revalidate, no-cache, max-age=0Vary: rscContent-Length: 76
0:{"a":"$@1","f":"","b":"yd-J8UfWl70zwtaAy83s7"}1:E{"digest":"2971658870"}
检查响应中是否存在 E{"digest"以及500 Internal Server Error 状态码,将能够可靠地返回您环境中易受攻击的主机。
检测机制原理分析
此检查能够区分易受攻击主机和非易受攻击主机的原因在于 React-Server 依赖项使用冒号 : 来分隔对象属性的方式。请看下面的代码片段:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linefunction getOutlinedModel<T>( response: Response, reference: string, parentObject: Object, key: string, map: (response: Response, model: any, parentObject: Object, key: string) => T,): T { const path = reference.split(':');// ... snip ... for (let i = 1; i < path.length; i++) { value = value[path[i]]; }
例如,如果我们在多部分请求中传递 JSON,如下所示:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line------WebKitFormBoundaryx8jO2oVc6SWP3SadContent-Disposition: form-data; name="0"
["$1:a:b"]------WebKitFormBoundaryx8jO2oVc6SWP3SadContent-Disposition: form-data; name="1"
{"a":{"b":"foo"}}------WebKitFormBoundaryx8jO2oVc6SWP3Sad--
这将转换为以下操作:
"$1:a:b" -> {"a":{"b":"foo"}}.a.b -> "foo"
在易受攻击的 React Server 版本中,我们可以通过以下多部分请求强制触发 500 错误:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line------WebKitFormBoundaryx8jO2oVc6SWP3SadContent-Disposition: form-data; name="0"
["$1:a:a"]------WebKitFormBoundaryx8jO2oVc6SWP3SadContent-Disposition: form-data; name="1"
{}------WebKitFormBoundaryx8jO2oVc6SWP3Sad--
导致异常的原因是它最终映射到以下操作:
"$1:a:a" -> {}.a.a -> (undefined).a -> 500 错误
补丁后的变化
React Server Components 的已打补丁版本在此冒号表示法中添加了一个额外的检查,从而防止了崩溃的发生:
ounter(lineounter(lineounter(lineounter(line const name = path[i]; if (typeof value === 'object' && hasOwnProperty.call(value, name)) { value = value[name]; }
这意味着如果 : 语法引用的属性不存在,它将被忽略。因此,在打补丁后,我们将不再收到 500 错误。
Assetnote 的攻击面管理平台 (ASM) 客户已于今天早些时候通过我们安全研究团队开发的此高保真检查,收到了关于其整个攻击面上此漏洞的通知。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:闪石星曜CyberSecurity 润霖@闪石星曜《从核弹到水弹,再到炸弹,Reactshell 漏洞又反转了?来看看漏洞详情》