Race Conditions
A race condition happens when two or more requests touch the same state at once and the result depends on the exact order the server processes them. Between the moment the app checks something (balance, coupon, stock, limit) and the moment it acts on it, there is a window —sometimes milliseconds— in which several requests see the “old” state and all pass the check. It’s the classic TOCTOU (Time-Of-Check to Time-Of-Use).
Threat model
Section titled “Threat model”The attacker doesn’t break the logic: they run it many times simultaneously before the state updates. If “redeem coupon” checks used == false then sets used = true, sending 50 redemptions in parallel can slip 50 through before the first one writes. The damage is directly business impact: money, stock, limits, uniqueness.
Red Team
Section titled “Red Team”Discovery
Section titled “Discovery”Look for operations where “once” or “only N times” matters:
- Redeem coupon/gift card, apply a one-time discount.
- Withdraw balance / transfer (spend the same money twice).
- Attempt limits (rate-limit bypass), vote/like limits.
- Reserve unique stock/seat; register a unique username/email.
- Privilege escalation in a two-step flow.
By hand: the technique
Section titled “By hand: the technique”The goal is to make all requests land in the same window. Two key methods:
- Single-packet attack (HTTP/2): ~20-30 requests sent in a single TCP packet, removing network jitter → they arrive practically simultaneously. The modern reference technique (James Kettle).
- Last-byte sync (HTTP/1.1): send all requests minus the last byte, then that last byte of all of them at once.
With Burp Repeater just group the tabs and use “Send group in parallel”. With Turbo Intruder, the race-single-packet-attack.py script.
# conceptual patternfor i in 1..30: prepare POST /redeem (coupon=X) # without sending the last bytesend the last byte of all 30 at once-> if several return "redeemed successfully", there is a raceVariants
Section titled “Variants”- Limit-overrun: spend a single-use resource several times (the balance/coupon case).
- Multi-endpoint: two different endpoints touching the same object in parallel (e.g. apply coupon while confirming payment).
- Single-endpoint state collision: two updates to the same record stepping on each other.
- Partial construction: using a half-created object before its invariants are validated.
- Burp Suite — Repeater with parallel groups (single-packet built in).
- Turbo Intruder — single-packet / last-byte sync scripts.
- ffuf/curl + GNU parallel — for simple HTTP/1.1 cases.
Impact
Section titled “Impact”Free money (double spend, unlimited coupons), bypass business limits, break uniqueness (two accounts with the same email), privilege escalation.
Blue Team
Section titled “Blue Team”Detection
Section titled “Detection”- Multiple identical requests to the same resource in a millisecond window from the same user.
- Inconsistent states: negative balance, a coupon with N uses when max was 1.
- Concurrency spikes on a single record.
Telemetry
Section titled “Telemetry”Log with high-resolution timestamps and a request ID; correlate concurrent requests on the same object.
Hardening
Section titled “Hardening”- Database atomicity: transactions with the right isolation level;
SELECT ... FOR UPDATE(pessimistic lock) or a version/WHERE used=falsein theUPDATE(optimistic) so only one wins. - Locks at the application/record level (idempotency keys, per-user/resource locks).
- Unique constraints in the database itself (unique index) that fail the second write.
- Idempotent operations with an idempotency token for payments.
- Don’t separate check and action: do it in a single atomic operation.
Response
Section titled “Response”Reconcile state (void double redemptions/spends), add the missing lock, review logs for historical exploitation of the same pattern.
CVEs and real-world cases
Section titled “CVEs and real-world cases”- James Kettle — “Smashing the state machine” (PortSwigger, 2023) — introduces the single-packet attack; the basis of modern web race exploitation.
- E-commerce / fintech platforms — many HackerOne reports of coupons and balances redeemed multiple times.
- Starbucks, points programs — public cases of balance duplication via race.
- GitLab / Shopify — publicly reported race bugs in invitations and limits.
Testing checklist
Section titled “Testing checklist”- Are there “single-use” or “limit N” operations? (coupon, balance, votes)
- Does sending 20-30 parallel requests (single-packet) slip more than one through?
- Can the same resource be spent twice? (limit-overrun)
- Do two endpoints touch the same object with no common lock?
- Is there a unique DB constraint or only a code check?
- Are check and action a single atomic transaction?
- Are there idempotency keys on payments?