Table of Contents

BBC Ch 12: Race Conditions

Source: Bug Bounty Bootcamp by Vickie Li

How Race Conditions Work

A race condition occurs when the security of a system depends on the sequence or timing of events, and that sequence can be disrupted by an attacker. Web applications are particularly vulnerable when they perform a check-then-act sequence without atomic locking: the state can change between the check and the act.

The classic pattern is time-of-check/time-of-use (TOCTOU):

  1. Application reads current state (check)
  2. Application performs action based on that state (use)
  3. If two requests execute concurrently, both may pass the check before either completes the use, leading to double-execution

Example: Bank Transfer Double-Spend

Account has $500. Transfer $500 to account A, and simultaneously (before the first transfer completes) transfer $500 to account B:

  1. Thread 1: checks balance → $500, OK to proceed
  2. Thread 2: checks balance → $500 (first transfer not yet committed), OK to proceed
  3. Thread 1: deducts $500, sends to account A
  4. Thread 2: deducts $500, sends to account B
  5. Balance is now -$500; $1000 was sent from a $500 account

Example: Coupon/Vote Manipulation

An application allows a coupon code to be applied once. If the “check used” and “mark used” steps are not atomic:

  1. Thread 1: checks – not used → OK
  2. Thread 2: checks – not used → OK (mark not yet written)
  3. Thread 1: applies discount, marks used
  4. Thread 2: applies discount, marks used

Both threads succeed and the coupon is applied twice. The same logic applies to vote-once checks, daily limit checks, and inventory reservation.

Prevention

Hunting for Race Conditions

Step 1: Find Features with Limits

Target any functionality that:

Examples:

Step 2: Send Simultaneous Requests

Use curl with the & operator to fire multiple requests in parallel from the shell:

curl -s "https://example.com/api/transfer?to=attacker&amount=500" &
curl -s "https://example.com/api/transfer?to=attacker&amount=500" &
curl -s "https://example.com/api/transfer?to=attacker&amount=500" &
wait

For POST requests with a session cookie:

for i in $(seq 1 10); do
  curl -s -X POST https://example.com/api/use_coupon     -d "code=SAVE50"     -H "Cookie: session=YOUR_SESSION" &
done
wait

The & sends each request to the background without waiting for the previous to finish.

Step 3: Check for Impact

After the parallel requests complete, check whether the limit was exceeded:

Step 4: Vary Timing and Thread Count

Race conditions are timing-sensitive. If the first attempt fails, try:

Escalating the Attack

5-Step Checklist

  1. Find features that enforce a one-time or limited-count action (coupons, votes, transfers, OTPs).
  2. Understand the check-then-act sequence; identify what window exists between check and commit.
  3. Send simultaneous curl requests (or Turbo Intruder) targeting the limited action.
  4. Check state after requests complete to confirm the limit was exceeded.
  5. Quantify impact: financial loss, unauthorized access, or data corruption and document with a minimal PoC.