
WebVerse Pro · Labs
BowBuy
Category: Web / Business Logic / Race Condition
Difficulty: Easy
XP: 100
Sypnosis
A premium archery store whose new-member '$300 welcome credit' is applied at checkout through a check-then-act race. The redeem endpoint reads a one-time flag, pauses to 'authorize', then adds the credit and sets the flag — so concurrent redeems all pass the check and each adds $300. By stacking the credit past an item's price and placing the order, the confirmation page renders the flag as a gift.
Skills Required
- Identifying a check-then-act (TOCTOU) race in business logic
- Distinguishing logic that is safe sequentially but unsafe under concurrency
- Driving a race with a concurrent request tool (Burp Turbo Intruder or a threaded Python script)
- Recognizing that a one-time guard must be atomic with the effect it protects
Skill Learned
- Recognizing a check-then-act (TOCTOU) window in a one-time server action
- Driving a race with a concurrent request tool and confirming the stacked state
- Understanding why a one-time check must be atomic with the effect it guards
- Spotting business logic that is correct sequentially but unsafe under concurrency
- Stacking a one-time credit by racing its redemption past the guard
Briefing
"BowBuy runs lean: a Blue Ridge pro shop that went online, a small crew, and a launch promo that drops a little store credit into every new member's account to get them started. The catalog, though, is all premium glass and carbon — every rig priced just out of reach of that welcome handout. The crew are sure no new member walks out with a bow on day one. Prove them wrong, and the thank-you gift is yours."
Exploitation

The target was a premium archery storefront offering a $300 welcome credit to new members. Browsing the catalog, I quickly realized the financial constraint: every single product was priced above the $300 credit.

The cheapest item on the rack was the Hartwell Takedown Recurve at $329. Since I couldn't legitimately afford anything, I added it to my cart to inspect the checkout flow and see exactly how the application handled the credit logic.


At checkout, the UI presented a button to apply the one-time $300 welcome credit.

Intercepting the Flow
I fired up Burp Suite and intercepted the initial GET /checkout request, followed by clicking the application button to catch the endpoint handling the business logic.


The redemption was handled by a parameter-less POST /checkout/redeem request. Knowing the challenge hinted at a check-then-act (TOCTOU) vulnerability, my working theory was that the server checks the welcome_redeemed flag, pauses, and then updates the balance. If I could send enough requests simultaneously inside that exact pause window, multiple threads would pass the initial validation check before the database recorded the first redemption.
Executing the Race Condition
I sent the POST request to Burp's Turbo Intruder. I modified the Python script to use a concurrent request engine with 15 connections, queuing them up behind a single synchronization gate (gate='redeem') before opening the gate to fire them all at the exact same millisecond.

I clicked Run.

The attack executed perfectly. Instead of one successful 302 redirect and 14 errors, all 15 concurrent requests returned a 302 FOUND status code, indicating they all successfully bypassed the sequential guard.
Returning to the browser and refreshing my checkout session confirmed the exploit: the single $300 credit had stacked multiple times, leaving me with a heavily inflated store credit of $2,400.

With my balance well over the $329 total, I clicked "Place Order" to finalize the transaction.

The application processed the payment using my stacked credit, rendering the confirmation page along with the challenge flag as a "gift with purchase."

Root Cause Analysis
The vulnerability is a classic Time-of-Check to Time-of-Use (TOCTOU) race condition within the application's business logic.
When a user hits POST /checkout/redeem, the backend performs a standard read query to check if welcome_redeemed == 0. Crucially, this read does not hold an atomic database lock. The thread then artificially pauses for ~70ms to "authorize" the credit (releasing the Global Interpreter Lock in the process). After the pause, it executes an atomic increment to the balance (balance = balance + 300) and updates the flag (welcome_redeemed = 1).
Because the check and the resulting action are separated by an unguarded time window, forcing ~15 concurrent requests to hit the endpoint simultaneously allows all of them to read welcome_redeemed == 0 before any single thread reaches the UPDATE step. Every thread that passes the check subsequently executes the atomic addition, stacking the credit far beyond its intended limit.
Impact
An attacker can trivially bypass financial constraints and generate arbitrary amounts of store credit. By exploiting the TOCTOU window, users can effectively print money within the application ecosystem to purchase premium physical goods for free, directly impacting the business's revenue and inventory.
Attack Chain Summary
| Step | Action |
|---|---|
| 1 | Browsed the catalog and added an item ($329) exceeding the default $300 welcome credit. |
| 2 | Intercepted the POST /checkout/redeem request in Burp Suite during checkout. |
| 3 | Sent the request to Turbo Intruder and configured a multi-threaded payload (15 concurrent connections). |
| 4 | Fired the concurrent requests to bypass the unguarded welcome_redeemed check via a TOCTOU race. |
| 5 | Confirmed the balance successfully stacked to $2,400 in the checkout UI. |
| 6 | Placed the order using the inflated credit to reach the confirmation page and retrieve the flag. |
Remediation
- Use Atomic Database Operations: Combine the check and the update into a single atomic SQL statement. For example:
UPDATE users SET balance = balance + 300, welcome_redeemed = 1 WHERE id = ? AND welcome_redeemed = 0;. If the row is successfully updated (rows affected > 0), only then apply the credit. - Implement Row-Level Locking: If complex logic requires multiple steps, utilize database locking mechanisms (like
SELECT ... FOR UPDATEin PostgreSQL/MySQL) within a single transaction to ensure no other threads can read or modify the row until the transaction commits. - Enforce State Consistency: Do not release application thread locks (or the GIL) in the middle of a critical state-changing operation unless the underlying data state is already strictly secured at the database level.