hacktricks/pentesting-web/race-condition.md

341 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 竞态条件
<figure><img src="../.gitbook/assets/image (3) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
使用 [**Trickest**](https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks) 轻松构建并**自动化工作流程**,由世界上**最先进的**社区工具提供支持。
立即获取访问权限:
{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}
<details>
<summary><strong>从零开始学习 AWS 黑客攻击直到成为英雄,通过</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS 红队专家)</strong></a><strong></strong></summary>
其他支持 HackTricks 的方式:
* 如果您想在 HackTricks 中看到您的**公司广告**或**下载 HackTricks 的 PDF**,请查看[**订阅计划**](https://github.com/sponsors/carlospolop)
* 获取[**官方 PEASS & HackTricks 商品**](https://peass.creator-spring.com)
* 发现[**PEASS 家族**](https://opensea.io/collection/the-peass-family),我们独家的[**NFTs 集合**](https://opensea.io/collection/the-peass-family)
* **加入** 💬 [**Discord 群组**](https://discord.gg/hRep4RUj7f) 或 [**telegram 群组**](https://t.me/peass) 或在 **Twitter** 🐦 上**关注**我 [**@carlospolopm**](https://twitter.com/carlospolopm)**。**
* **通过向** [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github 仓库提交 PR 来分享您的黑客技巧。
</details>
## 利用竞态条件
滥用竞态条件的主要问题是您需要请求并行处理,并且时间差非常短(通常>1ms。在以下部分中提出了不同的解决方案来实现这一点。
<figure><img src="../.gitbook/assets/image (5) (1) (1).png" alt=""><figcaption></figcaption></figure>
### 单包攻击HTTP/2/ 最后字节同步HTTP/1.1
HTTP2 允许在**单个 TCP 连接中发送 2 个请求**(而在 HTTP/1.1 中它们必须是顺序的)。
使用单个 TCP 数据包完全**消除了网络抖动的影响**,因此这显然也有潜力进行竞态条件攻击。然而,**两个请求对于可靠的竞态攻击来说是不够的**,这要归功于**服务器端抖动** - 应用程序请求处理时间的变化,由不可控变量如 CPU 竞争引起。
但是,使用 HTTP/1.1 的“**最后字节同步**”技术,可以预先发送大部分数据,保留每个请求的一小部分,然后用**单个 TCP 数据包“完成”20-30个请求**。
要**预先发送每个请求的大部分数据**
* 如果请求没有正文,发送所有头部,但不设置 END_STREAM 标志。保留设置了 END_STREAM 的空数据帧。
* 如果请求有正文,发送头部和除最后一个字节和 END_STREAM 标志外的所有正文数据。保留包含最后一个字节的数据帧。
接下来,**准备发送最后的帧**
* 等待 100ms 以确保初始帧已发送。
* 确保 TCP_NODELAY 被禁用 - 关键是 Nagle 算法批量处理最后的帧。
* 发送 ping 数据包以预热本地连接。如果不这样做,操作系统网络堆栈会将第一个最终帧放在单独的数据包中。
最后,发送保留的帧。您应该能够使用 Wireshark 验证它们是否落在单个数据包中。
{% hint style="info" %}
请注意,它**不适用于某些服务器上的静态文件**,但静态文件与竞态条件攻击无关。但静态文件对于竞态条件攻击来说是无关紧要的。
{% endhint %}
使用这种技术,您可以使 20-30 个请求同时到达服务器 - 不受网络抖动的影响:
<figure><img src="../.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
**适应目标架构**
值得注意的是,许多应用程序位于前端服务器之后,这些服务器可能决定将某些请求通过现有连接转发到后端,并为其他请求创建新的连接。
因此,重要的是不要将不一致的请求时间归因于应用程序行为,如锁定机制,这些机制一次只允许单个线程访问资源。此外,前端请求路由通常是基于每个连接进行的,因此您可以通过在攻击前执行服务器端连接预热 - **在连接上发送一些不重要的请求**(这只是在开始实际攻击前发送几个请求)来平滑请求时间。
#### 基于会话的锁定机制 <a href="#session-based-locking-mechanisms" id="session-based-locking-mechanisms"></a>
一些框架尝试通过使用某种形式的**请求锁定**来防止意外数据损坏。例如,**PHP 的原生会话处理器**模块一次只处理**一个会话的一个请求**。
发现这种行为极其重要,因为它可能会掩盖容易被利用的漏洞。如果您注意到所有请求都在顺序处理,请尝试使用不同的会话令牌发送每个请求。
#### **滥用速率或资源限制**
如果连接预热没有任何变化,有几种解决方案可以解决这个问题。
使用 Turbo Intruder您可以引入短暂的客户端延迟。然而由于这涉及将您的实际攻击请求分散到多个 TCP 数据包中,您将无法使用单包攻击技术。因此,在高抖动目标上,无论您设置什么延迟,攻击都不太可能可靠地工作。
<figure><img src="../.gitbook/assets/image (2) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
相反,您可能可以通过滥用常见的安全功能来解决这个问题。
Web 服务器通常**延迟处理请求,如果发送得太快太多**。通过发送大量虚假请求故意触发速率或资源限制,您可能能够造成适当的服务器端延迟。这使得即使在需要延迟执行的情况下,单包攻击也是可行的。
<figure><img src="../.gitbook/assets/image (3) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
{% hint style="warning" %}
有关此技术的更多信息,请查看原始报告 [https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine)
{% endhint %}
#### 攻击示例
* **Turbo Intruder - HTTP2 单包攻击1 个端点)**:您可以将请求发送到 **Turbo Intruder**`Extensions` -> `Turbo Intruder` -> `Send to Turbo Intruder`),您可以在请求中更改您想要暴力破解的值,例如在 `csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s` 中的 **`%s`**,然后从下拉菜单中选择 **`examples/race-single-packer-attack.py`**
<figure><img src="../.gitbook/assets/image (4) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
如果您打算**发送不同的值**,您可以使用这段代码,它使用来自剪贴板的单词列表:
```python
passwords = wordlists.clipboard
for password in passwords:
engine.queue(target.req, password, gate='race1')
```
{% hint style="warning" %}
如果网站不支持HTTP2仅支持HTTP1.1),请使用`Engine.THREADED`或`Engine.BURP`代替`Engine.BURP2`。
{% endhint %}
* **Tubo Intruder - HTTP2单包攻击多个端点**如果你需要向1个端点发送请求然后向其他多个端点发送多个请求以触发RCE你可以更改`race-single-packet-attack.py`脚本,如下所示:
```python
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
engine=Engine.BURP2
)
# Hardcode the second request for the RC
confirmationReq = '''POST /confirm?token[]= HTTP/2
Host: 0a9c00370490e77e837419c4005900d0.web-security-academy.net
Cookie: phpsessionid=MpDEOYRvaNT1OAm0OtAsmLZ91iDfISLU
Content-Length: 0
'''
# For each attempt (20 in total) send 50 confirmation requests.
for attempt in range(20):
currentAttempt = str(attempt)
username = 'aUser' + currentAttempt
# queue a single registration request
engine.queue(target.req, username, gate=currentAttempt)
# queue 50 confirmation requests - note that this will probably sent in two separate packets
for i in range(50):
engine.queue(confirmationReq, gate=currentAttempt)
# send all the queued requests for this attempt
engine.openGate(currentAttempt)
```
* 它也可以在 Burp Suite 的 **Repeater** 中通过新的“**Send group in parallel**”选项使用。
* 对于 **limit-overrun**,你可以在组中**添加相同的请求 50 次**。
* 对于 **connection warming**,你可以在**组**的**开始**处**添加**一些对网站服务器的非静态部分的**请求**。
* 对于在处理**一个请求和另一个请求**之间的过程中**延迟**,在两个请求之间你可以**添加额外的请求**。
* 对于一个**多端点**的 RC你可以开始发送**进入隐藏状态**的**请求**,然后紧接着发送**50个请求**来**利用隐藏状态**。
<figure><img src="../.gitbook/assets/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
### 原始 BF
在之前的研究之前,这些是一些使用过的有效载荷,它们尝试尽可能快地发送数据包以引起 RC。
* **Repeater:** 查看前一节的示例。
* **Intruder**: 将**请求**发送到**Intruder**,在**Options 菜单中**设置**线程数**为**30**,选择作为有效载荷的**Null payloads**并生成**30个**。
* **Turbo Intruder**
```python
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
pipeline=False
)
a = ['Session=<session_id_1>','Session=<session_id_2>','Session=<session_id_3>']
for i in range(len(a)):
engine.queue(target.req,a[i], gate='race1')
# open TCP connections and send partial requests
engine.start(timeout=10)
engine.openGate('race1')
engine.complete(timeout=60)
def handleResponse(req, interesting):
table.add(req)
```
* **Python - asyncio**
```python
import asyncio
import httpx
async def use_code(client):
resp = await client.post(f'http://victim.com', cookies={"session": "asdasdasd"}, data={"code": "123123123"})
return resp.text
async def main():
async with httpx.AsyncClient() as client:
tasks = []
for _ in range(20): #20 times
tasks.append(asyncio.ensure_future(use_code(client)))
# Get responses
results = await asyncio.gather(*tasks, return_exceptions=True)
# Print results
for r in results:
print(r)
# Async2sync sleep
await asyncio.sleep(0.5)
print(results)
asyncio.run(main())
```
## **RC 方法论**
### 超限/TOCTOU
这是最基本的竞态条件类型,**漏洞**通常出现在**限制你可以执行某个操作的次数**的地方。例如,在网上商店多次使用同一折扣码。一个非常简单的例子可以在[**这篇报告**](https://medium.com/@pravinponnusamy/race-condition-vulnerability-found-in-bug-bounty-program-573260454c43)或[**这个bug**](https://hackerone.com/reports/759247)**。**
这类攻击有许多变种,包括:
* 多次兑换礼品卡
* 多次评价产品
* 提取或转账超过账户余额的现金
* 重复使用单个CAPTCHA解决方案
* 绕过防暴力破解的速率限制
### **隐藏子状态**
更复杂的RC将利用**机器状态中的子状态**,这可能允许攻击者**滥用**他本不应该访问的状态,但存在一个**小窗口**让攻击者能够访问它。
1. **预测潜在的隐藏且有趣的子状态**
第一步是识别所有写入或从中读取数据然后用于重要事情的端点。例如,用户可能存储在数据库表中,该表通过注册、编辑个人资料、启动密码重置和完成密码重置来修改。
我们可以使用三个关键问题来排除不太可能引起冲突的端点。对于每个对象及其关联的端点,问:
* **状态是如何存储的?**
存储在持久的服务器端数据结构中的数据是理想的利用目标。一些端点完全在客户端存储它们的状态例如通过电子邮件发送JWT的密码重置 - 这些可以安全地跳过。
应用程序通常会在用户会话中存储一些状态。这些通常在一定程度上受到子状态的保护 - 稍后会详细介绍。
* **我们是在编辑还是在添加?**
编辑现有数据的操作(例如更改账户的主要电子邮件地址)有很大的冲突潜力,而仅仅是向现有数据添加(例如添加额外的电子邮件地址)的操作不太可能除了超限攻击之外的其他漏洞。
* **操作是基于什么关键字的?**
大多数端点都操作特定的记录,这些记录是使用“关键字”查找的,例如用户名、密码重置令牌或文件名。要成功攻击,我们需要两个使用相同关键字的操作。例如,想象两种合理的密码重置实现:
<figure><img src="../.gitbook/assets/image (2) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
2. **寻找线索**
此时,是时候**发起一些RC攻击**针对潜在有趣的端点,尝试找到与常规结果不同的意外结果。**任何与预期响应的偏差**,例如一个或多个响应的变化,或者像电子邮件内容不同或会话中可见变化的二阶效应,都可能是表明有问题的线索。
3. **证明概念**
最后一步是**证明概念并将其转化为可行的攻击**。
当你发送一批请求时,你可能会发现一个早期的请求对触发了一个脆弱的最终状态,但后来的请求覆盖/使其无效,最终状态是不可利用的。在这种情况下,你会想要消除所有不必要的请求 - 两个请求应该足以利用大多数漏洞。然而,减少到两个请求会使攻击更加依赖时机,因此你可能需要多次尝试攻击或自动化它。
### 时间敏感攻击
有时你可能找不到竞态条件,但**精确时机发送请求的技术**仍然可以揭示其他漏洞的存在。
一个例子是当**高分辨率时间戳被用来代替加密**安全的随机字符串来生成安全令牌。
考虑一个**密码重置令牌仅使用时间戳随机化**的情况。在这种情况下,可能可以**同时触发两个不同用户的密码重置**,它们都使用**相同的令牌**。你需要做的就是使请求的时间产生相同的时间戳。
{% hint style="warning" %}
例如,为了确认前面的情况,你可以同时请求**2个重置密码令牌**(使用单个数据包攻击)并检查它们是否**相同**。
{% endhint %}
查看[**这个实验室中的例子**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-exploiting-time-sensitive-vulnerabilities)。
## 隐藏子状态案例研究
### 支付并添加商品
[**查看这个实验室**](https://portswigger.net/web-security/logic-flaws/examples/lab-logic-flaws-insufficient-workflow-validation)来了解如何在商店**支付**并**额外添加**一个你**不需要支付**的商品。
### 确认其他电子邮件
这个想法是**同时验证一个电子邮件地址并将其更改为另一个**,以找出平台是否验证了更改后的新电子邮件。
### 基于Cookie的更改电子邮件到2个电子邮件地址
根据[**这篇文章**](https://portswigger.net/research/smashing-the-state-machine)Gitlab因为可能**将一个电子邮件的验证令牌发送到另一个电子邮件**而容易受到攻击。
你也可以查看[**这个实验室**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-single-endpoint)来了解这方面的内容。
### 隐藏的数据库状态/确认绕过
如果**2个不同的写操作**用于在**数据库**中**添加信息**,那么会有一小段时间**只写入了第一个数据**。例如,在创建用户时,可能会**写入**用户名和密码,**然后写入**用于确认新创建账户的令牌。这意味着在很短的时间内,**确认账户的令牌是空的**。
因此,**注册一个账户并发送几个带有空令牌**`token=` 或 `token[]=` 或其他变体)的请求来立即确认账户,可能允许**确认一个你无法控制电子邮件的账户**。
查看[**这个实验室**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-partial-construction)来查看一个例子。
### 绕过2FA
以下伪代码演示了一个网站如何可能对这种攻击的竞态变体容易受到攻击:
```python
session['userid'] = user.userid
if user.mfa_enabled:
session['enforce_mfa'] = True
# generate and send MFA code to user
# redirect browser to MFA code entry form
```
正如您所见,这实际上是**在单个请求范围内的多步骤序列**。最重要的是,它经历了一个子状态,在该子状态中,**用户暂时拥有有效的登录**会话,**但尚未执行MFA**。攻击者可能通过发送登录请求以及对敏感的、已认证的端点的请求来利用这一点。
### OAuth2 永久持久化
有几个[**OAuth提供商**](https://en.wikipedia.org/wiki/List_of_OAuth_providers)。这些服务允许您创建一个应用程序并认证提供商已注册的用户。为此,**客户端**将需要**允许您的应用程序**访问他们在**OAuth提供商**内的一些数据。\
所以到目前为止只是一个常见的使用google/linkdin/github...登录在您被提示一个页面说“_应用程序\<InsertCoolName>想要访问您的信息您是否同意_”
#### `authorization_code` 中的竞态条件
**问题**出现在您**接受它**并自动向恶意应用程序发送一个**`authorization_code`**时。然后,这个**应用程序滥用OAuth服务提供商中的竞态条件来生成不止一个AT/RT**_认证令牌/刷新令牌_从您账户的**`authorization_code`**。基本上,它会滥用您接受应用程序访问您的数据的事实来**创建多个账户**。然后,如果您**停止允许应用程序访问您的数据一对AT/RT将被删除但其他的仍然有效**。
#### `Refresh Token` 中的竞态条件
一旦您**获得了有效的RT**,您可以尝试**滥用它来生成多个AT/RT**,并且**即使用户取消了对恶意应用程序访问其数据的权限****多个RT仍然有效**。
## **WebSockets 中的RC**
在[**WS_RaceCondition_PoC**](https://github.com/redrays-io/WS_RaceCondition_PoC)中您可以找到一个用Java编写的PoC以**并行**发送websocket消息来滥用**Web Sockets中的竞态条件**。
## 参考资料
* [https://hackerone.com/reports/759247](https://hackerone.com/reports/759247)
* [https://pandaonair.com/2020/06/11/race-conditions-exploring-the-possibilities.html](https://pandaonair.com/2020/06/11/race-conditions-exploring-the-possibilities.html)
* [https://hackerone.com/reports/55140](https://hackerone.com/reports/55140)
* [https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine)
* [https://portswigger.net/web-security/race-conditions](https://portswigger.net/web-security/race-conditions)
<details>
<summary><strong>通过</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>从零到英雄学习AWS黑客攻击</strong></summary>
支持HackTricks的其他方式
* 如果您想在**HackTricks中看到您的公司广告**或**下载HackTricks的PDF**,请查看[**订阅计划**](https://github.com/sponsors/carlospolop)
* 获取[**官方PEASS & HackTricks商品**](https://peass.creator-spring.com)
* 发现[**PEASS家族**](https://opensea.io/collection/the-peass-family),我们独家的[**NFTs系列**](https://opensea.io/collection/the-peass-family)
* **加入** 💬 [**Discord群组**](https://discord.gg/hRep4RUj7f)或[**telegram群组**](https://t.me/peass)或在**Twitter** 🐦 上**关注**我 [**@carlospolopm**](https://twitter.com/carlospolopm)**。**
* **通过向** [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来**分享您的黑客技巧**。
</details>
<figure><img src="../.gitbook/assets/image (3) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
\
使用 [**Trickest**](https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks) 来轻松构建并**自动化工作流程**,由世界上**最先进的**社区工具提供支持。\
立即获取访问权限:
{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}