| Field | Detail |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | Race Conditions |
| Difficulty | Apprentice |
| Objective | Apply the PROMO20 coupon multiple times simultaneously to bring the jacket's price below $50 and complete the purchase |
Limit Overrun Race Conditions¶
The homepage showed a promo code:
For 20% off use code at checkout: PROMO20
I logged in as wiener:peter (balance: $50), added the jacket to the cart, and applied the coupon. Applying it a second time returned "Coupon already applied":
The backend logic is: check if already applied → apply discount → mark as applied. These three steps happen sequentially but not atomically. The race window sits between the check and the state update — if two requests both pass the check before either records usage, both apply the discount. Parallelism turns a one-time code into a repeatable one. The fix is atomicity, not rate limiting: a conditional database update with a uniqueness constraint that fails if the coupon is already recorded.
I sent the POST /cart/coupon request to Repeater, duplicated it 20 times, grouped the tabs, and used "Send group in parallel":
Most responses returned "Coupon already applied" but a few slipped through during the race window:
Results vary — the race window is real but non-deterministic. Server load, scheduling, and internal latency mean some parallel requests are still processed sequentially. Running 20 requests in parallel doesn't guarantee 20 successful applications; it increases the probability of at least a few slipping through. Burp's parallel send over HTTP/2 packs multiple requests into one TCP packet, eliminating network jitter — the remaining variance is server-side processing order.
Alternatively, Burp's "Trigger Race Conditions" custom action automates this:
Multiple successes confirmed. The jacket's price dropped to $15 — well below the $50 balance. Placing the order:
Lab solved