race-condition

race-condition

热门

Web 应用的竞态条件(Race Condition)与 TOCTOU 漏洞测试指南。适用于测试一次性操作、高并发 HTTP 滥用、突破频率限制(Rate-limit bypass)、Turbo Intruder 闸门控制(gates)、HTTP/2 单数据包攻击(single-packet attacks)以及 CWE-362 类型的同步漏洞。

1528Star
197Fork
更新于 2026/6/16
SKILL.md
只读
名称
race-condition
描述

Web 应用的竞态条件(Race Condition)与 TOCTOU 漏洞测试指南。适用于测试一次性操作、高并发 HTTP 滥用、突破频率限制(Rate-limit bypass)、Turbo Intruder 闸门控制(gates)、HTTP/2 单数据包攻击(single-packet attacks)以及 CWE-362 类型的同步漏洞。

SKILL:竞态条件 — 测试与利用实战手册

AI 加载指令:请将竞态条件(Race Condition)视为权限/状态完整性问题:非原子的“先读后写”(read-then-write)操作会让多个并发请求读取到旧状态。优先测试一次性余额类操作。将并行传输技术(HTTP/1.1 尾字节同步、HTTP/2 单数据包攻击、Turbo Intruder 闸门控制)与应用层证据(重复的成功响应、余额异常、账单重复落库)相结合。仅限授权测试。 路由提示:对于业务工作流、优惠券、库存或一次性奖励,优先加载本 Skill,并交叉加载 business-logic-vulnerabilities


0. 快速上手 — 优先测试什么

重点针对**检查(check)更新(update)**未在一个原子化数据库事务中完成的端点:

优先级 操作类型 示例路径 / 参数
1 一次性兑换 / 优惠券 / 奖励领取 redeem, apply_coupon, claim_reward, voucher
2 余额 / 额度 / 库存扣减 transfer, purchase, reserve, inventory
3 邀请 / 裂变 referral / 注册奖励 invite_accept, referral_claim
4 密码 / 邮箱 / MFA 验证 verify_token, confirm_email, reset_password
5 未使用强主键的伪幂等 API 单个用户理论上只能成功一次的 POST 请求

第一步动作(概念性)

  1. 在代理工具中截获引发状态变更的请求。
  2. 在工具能力范围内,尽可能同步发送 20~100 个重复请求。
  3. 归类测试结果:预期仅 0/1 次成功 vs N 次成功最终状态不一致

1. 核心概念

1.1 TOCTOU(检查时间到使用时间间隔漏洞)

线程 A                      线程 B
   |                            |
   +-- 检查 CHECK (资源有效)     |
   |                            +-- 检查 CHECK (资源有效)  ← 双方均判定“有效”
   +-- 使用/更新 USE / UPDATE   |
   |                            +-- 使用/更新 USE / UPDATE  ← 造成双重/重复生效

TOCTOU 指的是**逻辑判断(检查)数据变更(使用)**并非不可分割的单一原子步骤。

1.2 非原子的“先读后写”(Read-Then-Write)

典型的漏洞伪代码逻辑:

balance = SELECT balance FROM accounts WHERE id = ?
if balance >= amount:
    UPDATE accounts SET balance = balance - ? WHERE id = ?

两个并发请求在任意一个 UPDATE 提交之前,均能顺利通过 if 校验。

1.3 数据库层与应用层的锁机制漏洞

层级 问题根源
应用层(Application) 内存标志(In-memory flag)、缓存或 Session 仍显示“未使用”,而数据库其实已更新 — 反之亦然。
ORM / 服务层 部署了多实例但缺少分布式锁;各实例均认为自己掌握了决策权。
数据库层(DB) 缺少 SELECT … FOR UPDATE 显式锁、事务隔离级别错误,或业务逻辑散落在多个非事务语句中。
API 网关层 基于 IP 的限流逻辑采用先检查后递增策略 — 并发突发请求绕过了重复检查。

提示UNIQUE 唯一性约束与幂等键(Idempotency keys)通常能直接打掉整类漏洞 — 重点测试目标应用是否在核心热点路径上硬性校验了这些机制。


2. 攻击模式

2.1 突破额度上限(重复兑换 / 重复领取)

并行发送多个相同的已认证请求:

POST /api/v1/rewards/claim HTTP/1.1
Host: target.example
Authorization: Bearer <token>
Content-Type: application/json

{"reward_id":"welcome_bonus"}

成功特征:多次返回 HTTP 200/201、数据库产生多条账单明细记录,或账户余额超出了规则允许的上限。

2.2 利用并发绕过频率限制(Rate-Limit Bypass)

如果限流策略被实现为针对每个请求独立检查计数器,且递增操作非原子化:

POST /api/v1/login HTTP/1.1
Host: target.example
Content-Type: application/json

{"email":"victim@example.com","password":"wrong"}

在一个时间波次内并行发送 N 个请求;与 N 个串行请求的结果进行对比。

成功特征:成功提交的失败尝试次数超过了官方公布的封顶值,或者在短时间内高并发冲刷时未触发账号锁定。

2.3 多步骤逻辑利用(打破流水线节奏)

典型工作流:创建订单 → 支付 → 确认。若确认步骤在密码学上未与支付完成强绑定:

  1. 在同一个 Session/条目下同时开启两条并行流水线。
  2. 当通道 A 的支付仍处于处理中(in-flight)或被放弃时,在通道 B 上直接完成确认

成功特征:在无对应支付记录的情况下,商品被标记为已支付/已发货,或流程状态发生异常回退。


3. HTTP/1.1 尾字节同步技术(Last-Byte Synchronization)

原理:挂起所有请求的 Socket,使其发送完除 Body 最后一个字节之外的所有数据;接着瞬间并发释放这最后一个字节,使服务器在极短的时间窗口内密集接收到全部请求。

客户端 1: [请求头 + Body - 1字节] ----挂起----+
客户端 2: [请求头 + Body - 1字节] ----挂起----+--> 一起冲刷释放最后一个字节 (flush)
客户端 N: [请求头 + Body - 1字节] ----挂起----+

优势:相比在 Repeater 中简单手动重放,能极大降低各个请求之间的网络抖动

工具选型:自定义脚本、部分 Burp 插件,或使用 Turbo Intrudergate 门控模式(详见第 5 节)作为同步释放的具体落地方案。


4. HTTP/2 单数据包攻击(Single-Packet Attack)

原理:在单个 HTTP/2 连接中复用(Multiplex)多个完整的 HTTP/2 流(Streams),并将它们的帧(Frames)合并在一起,确保所有请求的首字节能在同一个 TCP 报文段(Segment)(或极其微小的间隔)中挤出网卡。服务端接收调度器随后会以毫秒级别以下的时间间隔处理这些请求。

Burp Repeater(现代工作流)

  1. 打开多个标签页或选中多个请求。
  2. 使用右键功能 Send group (parallel) / single-packet attack(单包攻击模式)。
  3. 如果目标支持,优先强制走 HTTP/2 协议。
  [ 请求 A 流 ]
  [ 请求 B 流 ]  --HTTP/2-->  单次突发 (one burst) -->  应用工作线程池
  [ 请求 C 流 ]

相比 HTTP/1.1 尾字节技巧的优势:线缆层面的对齐更加紧密,大幅减少了对单连接序列化发送的依赖。


5. TURBO INTRUDER 脚本模板

开源仓库:PortSwigger/turbo-intruder(Burp Suite 扩展插件)。

5.1 模板 1 — 同一端点,闸门(Gate)同步释放

配置参数concurrentConnections=30, requestsPerConnection=30,使用 gate(闸门)确保所有线程同时触发。

核心模式(压入 N 个请求后统一开闸):

for _ in range(N):
    engine.queue(request, gate='race1')
engine.openGate('race1')
def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=30,
                           requestsPerConnection=30,
                           pipeline=False,
                           engine=Engine.THREADED,
                           maxRetriesPerRequest=0
                           )

    for i in range(30):
        engine.queue(target.req, gate='race1')

    engine.openGate('race1')

def handleResponse(req, interesting):
    table.add(req)

请求头要求(为每个队列中的副本打上唯一标记以供日志关联;Turbo Intruder 的 Payload 占位符):

x-request: %s

当搭配字典(或其它 Payload 来源)时,Turbo Intruder 会自动将每个请求中的 %s 替换掉 — 在将请求从 Repeater 发送到 Turbo Intruder 前,请在基础请求中保留该 Header。HTTP 协议下大小写不敏感;建议使用统一名称以便做日志 Grep 关联。

5.2 模板 2 — 跨多端点,同一闸门

测试模式:1 个针对 target-1POST(引发状态变更)加上多个针对 target-2GET(读取侧),在同一闸门下同时释放,以此拉大 TOCTOU 时间窗口以便观察。

def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=30,
                           requestsPerConnection=30,
                           pipeline=False,
                           engine=Engine.THREADED,
                           maxRetriesPerRequest=0
                           )

    engine.queue(post_to_target1, gate='race1')
    for _ in range(30):
        engine.queue(get_target2, gate='race1')

    engine.openGate('race1')

如果涉及不同的 Host/路径,可通过实例化多个 RequestEngine 来调整端点(Turbo Intruder 支持多 Engine 架构 — 请查阅您使用的 Burp 版本对应的上游文档)。


6. CVE 案例参考 — CVE-2022-4037

CVE-2022-4037(GitLab CE/EE):竞态条件漏洞,导致已验证邮箱地址伪造;当该产品充当 **OAuth 身份提供方(OAuth IdP)**时,还会引发第三方账号关联绑定安全风险。漏洞归类:CWE-362。公开研究表明,通过 HTTP/2 单数据包攻击的时序控制技术,可以在极窄的时间窗口内成功碰撞出该漏洞。

对测试人员的启示:邮箱验证、OAuth 关联绑定以及“所有权确认”工作流都是极高价值的竞态攻击目标 — 远不止优惠券和余额扣减场景。

参考链接(官方 / 客观)


7. 工具箱

工具 作用 / 场景
PortSwigger/turbo-intruder Burp 中的高并发重放、**闸门控制(gates)**与 Python 脚本支持。
JavanXD/Raceocat 专为竞态测试设计的 HTTP 客户端模式(使用前请确认与自身技术栈的兼容性)。
nxenon/h2spacex HTTP/2 底层 / 单数据包风格攻击测试工具(请合规使用,仅限授权目标)。
Burp Suite — Repeater 内置 Send group (parallel) / single-packet attack 组合发送功能,实现多请求同步。

8. 测试决策树

                       起始:是否为引发状态变更的 API?
                                    |
                     否 -----------+---------- 是
                      |                        |
                   终止测试               是否涉及一次性操作/余额/身份验证?
                                                    |
                          +-------------------------+-------------------------+
                          |                         |                         |
                    类优惠券场景                频率限制 (Rate Limit)       多步骤工作流
                          |                         |                         |
                   并发发送相同请求           并发 vs 串行对比            并发多条流水线
                          |                         |                         |
                   是否重复成功?              是否突破限制?              状态是否不匹配?
                     /       \                    /       \                  /       \
                   是         否                 是         否               是         否
                    |         |                  |         |                |         |
              输出报告 +  尝试 HTTP/2         输出报告 +  尝试 TI        输出报告 +  深入分析
              收集证据   单数据包攻击         收集证据   闸门控制        收集证据   单步细节
                    |         |                  |         |                |         |
                    +----+----+                  +----+----+                +----+----+
                         |                            |                          |
                      选工具                       选工具                     选工具
                         v                            v                          v
              Burp 分组 / h2spacex             TI 闸门 / Raceocat           TI + 追踪 ID

如何确认漏洞(漏洞证据 Checklist)

  1. 高并发场景下可稳定复现重复成功,而非偶然的单次重试波动。
  2. 存在服务端落库证据:产生了两条记录、发送了两封邮件、赋予了两次权限,或最终余额数值异常。
  3. 结合 x-request(或类似)请求标记,或唯一的 Body 参数进行关联验证。