Web 应用程序的 Race condition(竞态条件)与 TOCTOU 漏洞测试。适用于测试一次性操作、并发 HTTP 滥用、绕过速率限制(Rate-limit bypass)、Turbo Intruder gate 闸门并发、HTTP/2 单一封包攻击(Single-packet attacks)以及 CWE-362 类型的同步时隙漏洞。
SKILL: Race Conditions — Testing & Exploitation Playbook
AI LOAD INSTRUCTION: 将 Race condition 视为授权/状态完整性问题:非原子化的「先读取后写入(read-then-write)」会让多个请求观察到过期的旧状态。优先测试一次性操作或余额类操作。结合平行传输手段(HTTP/1.1 最后一字节同步、HTTP/2 单一封包、Turbo Intruder gates)与应用层证据(重复的成功响应、不一致的余额、重复的账目记录)。仅限已授权测试。 路由说明:对于业务流程、优惠券、库存或一次性奖励,请由此 Skill 入手并交叉载入
business-logic-vulnerabilities。
0. QUICK START — What to Test First
针对**检查(check)与更新(update)**不太可能属于单一数据库原子操作的端点:
| Priority | Operation class | Example paths / parameters |
|---|---|---|
| 1 | 一次性兑换 / 优惠券 / 奖励 | redeem, apply_coupon, claim_reward, voucher |
| 2 | 余额 / 配额 / 扣减库存 | transfer, purchase, reserve, inventory |
| 3 | 邀请 / 推荐 / 注册奖励 | invite_accept, referral_claim |
| 4 | 密码 / 邮箱 / MFA 验证 | verify_token, confirm_email, reset_password |
| 5 | 缺乏强键值约束、看似具备幂等性的 API | POST (每个用户理应只能成功一次) |
初步操作(概念):
- 在代理(Proxy)中截获会变更状态的请求。
- 在工具支持的范围内,尽可能同时发送 20–100 个重复请求。
- 分类结果:预期仅 0/1 次成功 vs 多次成功(N 次)或最终状态不一致。
1. CORE CONCEPT
1.1 TOCTOU (Time-of-check to time-of-use)
Thread A Thread B
| |
+-- CHECK (resource OK) |
| +-- CHECK (resource OK) ← both see "OK"
+-- USE / UPDATE |
| +-- USE / UPDATE ← duplicate effect
TOCTOU 代表决策(检查 CHECK)与状态变更(使用 USE)并非不可分割的单一原子步骤。
1.2 Non-atomic read-then-write
常见的易受攻击伪代码流程:
balance = SELECT balance FROM accounts WHERE id = ?
if balance >= amount:
UPDATE accounts SET balance = balance - ? WHERE id = ?
两个并发请求都可能在任何一个 UPDATE 事务提交之前,同时通过 if 条件判断。
1.3 Database-level vs application-level locking gaps
| Layer | What goes wrong |
|---|---|
| Application | 内存标记、缓存或 Session 显示「尚未被使用」,但数据库其实已更新 — 反之亦然。 |
| ORM / service | 多个实例且未挂载分布式锁,各个实例都以为自己拥有唯一的决策权。 |
| DB | 缺乏 SELECT … FOR UPDATE、隔离级别配置错误,或逻辑分散在多个非事务处理的 SQL 语句中。 |
| API gateway | 基于 IP 的速率限制采用先检查后递增机制 — 并发突发请求绕过了重复检查。 |
提示:UNIQUE(唯一)约束与幂等键(Idempotency keys)通常能直接根除一整类漏洞 — 需测试应用是否在核心关键路径上严格执行这些约束。
2. ATTACK PATTERNS
2.1 Limit-overrun (double redeem / double claim)
平行发送多个完全相同且已身份验证的请求:
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 via simultaneity
如果速率限制是以每次请求检查计数器的方式实现,而未采用原子递增:
POST /api/v1/login HTTP/1.1
Host: target.example
Content-Type: application/json
{"email":"victim@example.com","password":"wrong"}
在一个波次内并发发出 N 次尝试;并与 N 次顺序发送的请求做对比。
成功迹象:接受的失败尝试次数超过设定的上限,或者当突发并发请求在单一时间窗口内完成时未触发锁定。
2.3 Multi-step exploitation (beat the pipeline)
工作流程:创建 → 支付 → 确认。如果**确认(confirm)步骤在密码学或逻辑上未与支付(pay)**的完成做强绑定:
- 在相同的 Session 或项目中启动两条平行的操作管道。
- 在通道 A 的支付仍处于传输中或已放弃时,直接在通道 B 上完成确认。
成功迹象:项目在未完成对应支付的情况下被标记为已支付/已发货,或者状态发生了逆向跳跃。
3. HTTP/1.1 LAST-BYTE SYNCHRONIZATION
核心概念:保持所有请求处于阻塞状态,直到每个 Socket 都已发送完整请求(仅留 Body 的最后一个字节未发);接着统一同时释放这最后一个字节,使服务器在极短的时间间隔内集中接收到所有请求。
Client 1: [headers + body - 1 byte] ----hold----+
Client 2: [headers + body - 1 byte] ----hold----+--> flush last byte together
Client N: [headers + body - 1 byte] ----hold----+
原因:相较于在 Repeater 中简单的顺序点按,此方法可大幅降低各个请求副本之间的网络抖动(Network jitter)。
工具:自定义脚本、部分 Burp 扩展程序,或者 Turbo Intruder 的 gate 模式(参见第 5 节)作为同步释放的实用方案。
4. HTTP/2 SINGLE-PACKET ATTACK
核心概念:多路复用(Multiplex)多个完整的 HTTP/2 流,并将它们的 Frame 合并,使所有请求的首字节能打包在同一个 TCP 报文段(Segment)中从网卡发出(或间隔极小)。接收端的调度器随后能以**毫秒以下(Sub-millisecond)**的微小时间差处理这些请求。
Burp Repeater(现代工作流程):
- 打开多个标签页或选中多个请求。
- 在支持的界面中使用 Send group (parallel) / single-packet attack。
- 若目标支持 HTTP/2,请优先选用 HTTP/2。
[ Req A stream ]
[ Req B stream ] --HTTP/2--> one burst --> app worker pool
[ Req C stream ]
为何通常优于 HTTP/1.1 最后一字节技巧:在物理线路上的排列更紧密;对单条连接序列化的依赖度更低。
5. TURBO INTRUDER TEMPLATES
存储库:PortSwigger/turbo-intruder(Burp Suite 扩展)。
5.1 Template 1 — Same endpoint, gate release
设置: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
当配合 Wordlist(或其他 Payload 来源)使用时,Turbo Intruder 会替换每个请求中的 %s — 在发送至 Turbo Intruder 之前,请在 Repeater 的基础请求中保留此 Header。HTTP Header 不区分大小写;请使用一致的名称以方便用 grep 查找日志。
5.2 Template 2 — Multi-endpoint, same gate
模式:包含一个发送至 target-1 的 POST(状态变更)以及多个发送至 target-2 的 GET(读取端),并同时释放,从而扩大对 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')
若端点不同,可通过复制 RequestEngine 实例来调整 Host/Path(Turbo Intruder 支持多个 Engine — 详情请参阅对应 Burp 版本的官方文档)。
6. CVE REFERENCE — CVE-2022-4037
CVE-2022-4037 (GitLab CE/EE):竞态条件(Race condition)导致已验证邮箱地址伪造漏洞;当产品作为 OAuth 身份提供者(OAuth identity provider) 时存在高风险 — 涉及第三方账号关联与影响场景。属于 CWE-362。公开研究展示了利用 HTTP/2 单一封包的时序技巧,成功捕获极窄的时隙窗口。
测试人员要点:邮箱验证、OAuth 账号绑定以及「所有权确认」流程都是高价值的 Race condition 测试目标,绝不仅限于优惠券与余额。
参考资料(官方 / 中立):
- NVD — CVE-2022-4037
- GitLab security advisories and vendor CVE JSON for affected version ranges
7. TOOLS
| Tool | Role |
|---|---|
| PortSwigger/turbo-intruder | Burp 中的高并发重放、**闸门控制(Gates)**与脚本扩展。 |
| JavanXD/Raceocat | 专注 Race condition 的 HTTP 客户端模式(请先确认与技术栈的兼容性)。 |
| nxenon/h2spacex | HTTP/2 底层 / 单一封包风格测试实验(请负责任地使用,仅限于授权目标)。 |
| Burp Suite — Repeater | 通过 Send group (parallel) / single-packet attack 实现多请求同步。 |
8. DECISION TREE
START: state-changing API?
|
NO -----------+---------- YES
| |
stop here one-time / balance / verify?
|
+-------------------------+-------------------------+
| | |
coupon-like rate limit multi-step
| | |
parallel same req parallel vs serial parallel pipelines
| | |
duplicate success? limit exceeded? state mismatch?
/ \ / \ / \
YES NO YES NO YES NO
| | | | | |
report + try HTTP/2 report + try TI report + deepen
evidence single-packet evidence gates per-step
| | | | | |
+----+----+ +----+----+ +----+----+
| | |
tool pick tool pick tool pick
v v v
Burp group / h2spacex TI gates / Raceocat TI + trace IDs
如何确认漏洞(证据清单):
- 在并发条件下可稳定重现重复成功,而非偶然的单次重试。
- 存在服务器端痕迹:两条数据库记录、两封邮件、两次授权发放或错误的最终余额。
- 利用
x-request(或类似)标记或唯一的请求体数据进行关联比对。






