Race conditions in web apps occur when two requests, processed concurrently, produce a state that neither would alone. Classic TOCTOU (time-of-check-to-time-of-use) bugs. They turn “redeem this coupon once” into “redeem 50 times if you fire 50 requests at the same instant.” James Kettle’s research on single-packet attacks (2023) made these exploitable at high reliability. This module covers detection, exploitation, and defense.
The classic pattern
# Pseudocode of a vulnerable coupon-redeem endpoint
def redeem(user_id, coupon_code):
coupon = db.query("SELECT * FROM coupons WHERE code=?", coupon_code)
if coupon.redeemed_count >= coupon.max_uses:
return "expired"
# ... business logic ...
user.balance += coupon.value
db.execute("UPDATE coupons SET redeemed_count = redeemed_count + 1 WHERE code=?", coupon_code)
db.execute("UPDATE users SET balance=? WHERE id=?", user.balance, user_id)
Two requests arrive concurrently. Both pass the redeemed_count < max_uses check before either UPDATE runs. Both increment balance. Coupon redeemed twice, money credited twice.
Where they hide
- Promo code redemption
- Withdrawal / transfer endpoints (double-spend)
- Rate-limiter increment (race past the limit)
- 2FA verification (race to bypass)
- Account creation with uniqueness check
- One-time-use tokens
- Voting / liking systems
- Cart / checkout flows that re-validate inventory
The single-packet attack
James Kettle showed that with HTTP/2, you can send the last byte of multiple requests in a single TCP packet. The server processes them in nearly-perfect parallel β the timing window opens microseconds wide.
Tools:
- Burp Suite Repeater with “Send group in parallel (single-packet)” mode β built-in for race testing
- Turbo Intruder β Burp extension; programmable for complex race scenarios
- Project Discovery’s nuclei β limited race testing
Testing β the safe approach
- Identify candidate endpoints (state-changing, with check-then-act logic)
- In Burp, capture a successful request
- Send to Repeater, group-tab
- Duplicate the request 20-50 times in the group
- Send group in parallel (single-packet)
- Compare server responses; check downstream state
If 50 parallel requests produce 50 successful actions when business logic intended 1, you have a race condition.
Real exploitation examples
- Promo code intended for 1 use, applied 50 times β 50Γ discount on order
- Withdrawal with $1000 balance, fired 5 times in parallel β $5000 withdrawn
- Account creation race producing duplicate username (different DB locking modes)
- 2FA bypass: the wrong-code-counter check + reset race
- Voting endpoint: 100 votes from one user when limit was 1
Detection in code review
Look for:
- Read-then-update sequences without transactional consistency
- Counter increments via SELECT-then-UPDATE rather than UPDATE with WHERE
- Application-level locking using flag columns
- Cache-based deduplication that depends on cache consistency
- Anything described as “we check if X, then do Y”
Defense patterns
Database-level locking
# SELECT FOR UPDATE β pessimistic lock
BEGIN TRANSACTION;
SELECT * FROM coupons WHERE code='X' FOR UPDATE; -- locks the row
-- check + decide
UPDATE coupons SET redeemed_count = redeemed_count + 1 WHERE code='X';
COMMIT;
# Atomic UPDATE with conditional WHERE
UPDATE coupons
SET redeemed_count = redeemed_count + 1
WHERE code = 'X' AND redeemed_count < max_uses;
-- If no rows affected, redemption failed; act accordingly
Optimistic concurrency control
# Version-based; no locking needed
SELECT *, version FROM accounts WHERE id=1; -- v=5
-- ... compute new state ...
UPDATE accounts
SET balance=newbal, version=version+1
WHERE id=1 AND version=5;
-- Affected rows = 0 means another transaction won; retry
Idempotency keys
Client provides a unique key per logical operation. Server stores key β result mapping. Retry with same key returns cached result, doesn’t execute again.
π Advanced Module Β· Pro Tier
Continue reading with Pro tier (βΉ4,999/year)
You've read 29% of this module. Unlock the remaining deep-dive, quiz, and every other Advanced/Expert module.
136+ modulesAll levels up to this tier
20-question quizzesUnlimited retries with explanations
Completion certificatesShareable on LinkedIn
7 more sections locked below