An internal ops console over a twenty-action API. Nineteen of those actions do their check and their write as one atomic operation. Exactly one does not. Every action also sleeps about the same amount of time, so response timing will not hand you the answer.
Goal: find the action whose limit can be broken by firing requests in parallel, break it, and collect the flag. Sequential requests will never do it - each one of these limits holds perfectly when you go one at a time.
Your session id:
Every request needs ?sid=<your session id>.
State lives per session id, not per cookie, so parallel requests really do share one bank account.
Send the same request N times at once. Works against any action - picking the right one is the lab.
ready
| action | params | limit it enforces |
|---|---|---|
| state / balance / docs | - | read only |
| deposit / withdraw / transfer | amount, to | balance may not go negative |
| redeem_coupon | code=RAT100 | coupon is single use, +100 |
| apply_cashback | - | once per cycle, +5 |
| buy_item / refund_item | - | stock is 1, price 40 |
| add_beneficiary / remove_beneficiary | name | maximum 3 beneficiaries |
| upgrade_plan / cancel_plan | - | one free upgrade |
| claim_invite | code=RAT-INVITE-2026 | invite is single use, +25 |
| activate_gift_card | code=GC-7741-0043 | gift card single use, +50 |
| vote_poll | choice | one vote per account |
| reserve_seat | who | 2 seats total |
| rotate_token / reset | - | housekeeping |
Work through the limited actions one at a time: every one of them refuses a second sequential request. Now repeat the same test with 20 copies fired at once and compare the state afterwards. Only one action ends up in a state it should not be able to reach.
The tell is in the response body, not the timing. After a parallel burst, call state and
look for a counter that went past its ceiling, a balance that grew twice, or a flag that should only have
fired once.
It is the coupon. redeem_coupon reads the state, waits on a "partner service" call, then
writes coupon_used back. Two requests that both read before either writes will both pass the
check and both credit 100.
SID=$(head -c 12 /dev/urandom | od -An -tx1 | tr -d ' \n')
seq 20 | xargs -P 20 -I{} curl -s \
"https://labs.hackxpert.com/RACECONDITION/RatBankOps/api.php?action=redeem_coupon&code=RAT100&sid=$SID" \
| grep -o 'flag{[^}]*}' | head -1
In Burp: send the request to Turbo Intruder (or Repeater, tab group, "Send group in parallel").