Intermediate·300 points·4 solves
Tenderpay — Race Condition
Learn race conditions (TOCTOU — time-of-check to time-of-use): when code checks a condition and then acts on it without holding a lock, concurrent requests can all pass the check before any of them changes the state, so a "once only" action happens several times.
Tenderpay's redeem handler does: if not claimed → (slow work) → balance += 100; claimed = True. There is no lock around the check-and-set, and the slow work in the middle widens the window. If you fire many redeem requests simultaneously, several of them evaluate "not claimed" as true at the same instant — before the first one has set claimed = True — so each adds another 100. A single honest claim leaves the balance at exactly 100; anything higher proves the one-time bonus was credited more than once. The fix is to make the check-and-update atomic: a lock, a database transaction with SELECT ... FOR UPDATE, or a single conditional UPDATE.
Your tasks
1
Redeem once, look at the balance
Open the lab. Redeeming the bonus once credits exactly 100. Claiming again says it is already claimed.
2
Spot the window
The handler checks 'not claimed', then does slow work, then credits and marks it claimed. Between the check and the mark, another request can also pass the check.
3
Send many at once
Fire a burst of redeem requests concurrently (curl in parallel, Burp Intruder, a short script) so several hit the check before the first sets claimed = True.
4
Over-credit the wallet
Multiple requests each add 100, so your balance climbs past a single bonus.
5
Capture the flag
With the balance above 100, reload — the wallet flags the anomaly and reveals the fraud-audit token.
Submit the flag
Start the lab, capture the flag from the running machine, and submit it here.