之前写过 Next.js CVE-2025-29927(middleware 鉴权绕过),那是 Next.js 的”门卫”问题。React2Shell 是另一个量级的东西——React Server Components(RSC)链路的 RCE,前端框架写出了后端级别的洞。这篇整理自一套五篇连载课件(作者参与应急并拿下 Vercel 官方 WAF 绕过活动的奖金),按原课程顺序讲:前置知识 → 调试环境 → 协议 → 原理 → 绕过。
0x01 前置:从回调地狱到 Thenable
看懂这个洞要先懂 JS 异步的演进史:
- 回调地狱:古早 JS 大量 API 靠回调,
登录拿token → 查用户id → 查订单的流程层层嵌套,可读性差且难复用; - Promise(ES6):把”嵌套”变”链式调用”,
then()让回调回到同一上下文——函数式编程思想的落地; - Thenable:任何实现了
then()方法的对象。React 内部处理数据时会检查”这个值是不是 Thenable,是就调用它的then”——记住这句话,漏洞的引信就是它。
0x02 SSR vs RSC:前端怎么写出了后端漏洞
Next.js Conf 2023 有个著名场面:团队成员演示用 React 19 的 Server Components + Server Actions 解决客户端 bundle 越来越大的问题,推特上吵翻天——嘲讽者说这是”历史的倒退”,Web 开发回到了 90 年代 PHP/JSP 的模式,“前端开发也可以写后端漏洞了”。
一语成谶。两者区别:
- SSR:首渲染在后端执行返回 HTML,但浏览器仍要下载全部组件代码,靠 Hydration 重新绑定交互 → JS 体积大、可交互时间(TTI)长;
- RSC:组件真正留在服务端,客户端只收序列化结果——序列化协议就是 React Flight,数据包在服务端解析执行。
攻击面由此而来:Flight 协议的解析端是服务端代码,处理的是”可被伪造的客户端数据”。
0x03 应急视角:为什么 SCA 扫不到这个组件
漏洞通告出来当天,作者的应急流程值得抄作业:
- 按通告排查
react-server-dom-webpack等漏洞组件——查 npm 日志和 SCA(软件成分分析),结果寥寥; - 但公司明明有 Next.js 业务。猜想:RSC 对 Next.js 是可选依赖,开发没装;
- 拿 Vulhub 源码直接调试验证 → 发现真相:Next.js 不像传统 JS 项目单独装依赖,而是把第三方依赖直接打包进
compiled目录——所以 SCA 依赖清单里根本看不到react-server-dom-webpack。
教训一句话:框架自打包依赖会绕过你的成分分析,SCA 说”没有”不等于”不受影响”,要以调试为准。
0x04 Flight 协议:Chunk 是怎么被解析的
RSC 数据包由一个个 Chunk 组成,服务端解析流程:
遍历 multipart 字段
→ 每个字段 JSON.parse() 成对象
→ 递归遍历所有子孙结构
→ 发现 $ 开头的值 → 按规则互相"引用"(扩展 JSON 不支持的类型:函数/组件/Thenable…)
→ 结果放进 chunk.value,作为"数据"
$ 前缀引用机制是 Flight 对 JSON 的类型扩展,也是攻击者往解析过程里塞逻辑的入口。
0x05 漏洞本质:还是”混淆数据和业务逻辑”
我反复验证的万能公式:SQL 注入是输入变成 SQL、命令注入是输入变成命令、XSS 是输入变成 HTML——React2Shell 是攻击者的”数据”变成了服务端的”Chunk 对象”。
POC 的形态:POST 一个 multipart 请求(Next-Action: x 头 + Content-Type: multipart),其中模拟一个假的 Chunk 对象——通过 $ 引用构造出带 constructor 的原型链结构,让解析器把攻击者控制的”数据”识别成了可调用对象(Thenable/函数路径),随后触发执行,达成 RCE。
数据变代码的边界在这里失守:JSON.parse 的产物本该只是数据,但 $ 引用 + 原型链让解析器”亲手”把它升格成了逻辑。
0x06 15 万美元的 WAF 绕过
漏洞爆发后 Vercel 官方办了 WAF 绕过活动,作者拿下奖金。思路分两档:
思维实验级(大多数人能想到)
- payload 是 JSON → 用 JSON 支持的 unicode 转义改写关键词:
constructor写成\u0063onstructor——能不能过取决于 WAF 是否在解码后再匹配; - 超大数据包:部分 WAF 为平衡效率会放过超大 body。暴力美学,实战可试,研究价值低。
工程级细节
- Next.js 默认接收请求的缓冲区是 1MB——这给了 WAF 绕过一个精确的”预算”:payload 体积策略要贴着这个限制设计;
- 绕过的核心是找到 WAF 解析和后端解析的差分点(谁多解码一层、谁的缓冲上限先到),这和SQL 注入绕过里”找解析差异而非规则漏洞”是同一条方法论。
0x07 小结与防守清单
- React/Next.js 业务:升级到官方修复版本;WAF 规则对
Next-Action头 + multipart +$引用结构的组合流量做检测; - 应急排查不要只信 SCA:Next.js 类自打包框架要用
compiled目录实测; - 对学习者的启示:这个洞横跨 JS 异步机制、框架协议设计、原型链、WAF 对抗——追一个新洞追到底,胜过刷一百个旧洞。
相关旧文
- Next.js CVE-2025-29927:中间件认证绕过复现
- SQL 注入绕过手册(解析差异方法论的通用版)
- Java FreeMarker SSTI 与积木报表 CVE(同为”框架引擎吃用户输入”的 RCE)