This Is How I Hacked a Web App Using Race Conditions

spyboy's avatarPosted by

(Concurrency Abuse That Developers Almost Never Test – Educational Case Study)

Disclaimer
This article is written strictly for educational and defensive purposes.
All applications, endpoints, identifiers, timings, and data are fully anonymized.
No real-world system was harmed.
This write-up demonstrates how lack of concurrency control leads to critical exploitation.


Why Race Conditions Are a Hacker’s Dream

Most vulnerabilities are about bad input.

Race conditions are about bad timing.

And timing bugs are deadly because:

  • They don’t show in normal testing
  • They disappear when logged
  • They only appear under pressure
  • Developers almost never simulate them

⏱️ If two requests arrive “at the same time” — who wins?

Attackers make sure they do.


Target Overview (Anonymized)

  • Type: SaaS web application
  • Feature abused: Coupon redemption
  • Auth: Logged-in user
  • Constraints enforced by app:
    • Coupon usable once per user
    • Wallet credit applied after redemption
  • Backend: REST API
  • Database: Relational (transaction-based, but poorly used)

Phase 1: Understanding the Logic (Before Touching Tools)

The normal coupon flow:

  1. User applies coupon
  2. Backend checks:
    • Is coupon valid?
    • Is coupon already used?
  3. Backend credits wallet
  4. Coupon marked as used

That looks safe.

But here’s the real logic (simplified):

IF coupon_not_used:
    credit_wallet()
    mark_coupon_used()

🚩
These steps were not atomic.


Phase 2: Spotting the Race Opportunity

I watched the request:

POST /api/coupon/redeem
Content-Type: application/json

{
  "code": "WELCOME100"
}

Response (normal):

{ "wallet": 100 }

Then I tried again.

Response:

{ "error": "Coupon already used" }

So far, so good.

But the real question is:

What happens if two requests arrive before the coupon is marked used?


Phase 3: Forcing Concurrency (The Key Step)

I didn’t brute-force.

I sent requests in parallel.

Tool Used:

  • Burp Suite → Intruder
  • Attack type: Pitchfork
  • Threads: 10 simultaneous requests

All requests contained the same coupon code.


Phase 4: The Moment It Broke

Out of 10 requests:

  • 7 returned success
  • 3 returned “already used”

Wallet balance:

"wallet": 700

😐
Coupon supposed to give ₹100 once.

I got ₹700.


Phase 5: Why This Worked (Critical Insight)

The backend logic was:

if not coupon.used:
    wallet += coupon.amount
    coupon.used = True

But without:

  • Database locks
  • Transactions
  • Atomic operations

Multiple threads passed the if check before the flag flipped.

This is a classic race condition.


Phase 6: Scaling the Exploit (Danger Zone)

I reduced delay.
Increased threads.
Timed it better.

Results:

  • Wallet inflated repeatedly
  • No alerts
  • No errors
  • Logs looked legitimate

This is the scariest kind of exploit.


Real-World Impact

Race conditions can allow:

  • 💸 Unlimited wallet credit
  • 🎟️ Multiple redemptions
  • 🛒 Free purchases
  • 🧾 Duplicate refunds
  • 🔓 Double spending

Severity:

Critical – Financial Abuse


Where Race Conditions Commonly Exist

Developers almost always miss them in:

  • Coupon redemption
  • OTP verification
  • Password reset tokens
  • Inventory stock
  • Voting / likes
  • Rate-limited actions

Anywhere with:

“Only once” logic


How I Systematically Hunt Race Conditions

This is my exact method:

1️⃣ Find state-changing endpoints

  • Redeem
  • Apply
  • Verify
  • Confirm

2️⃣ Check order of operations

  • Validation
  • Action
  • State update

3️⃣ Send parallel requests

  • Same payload
  • Same session
  • Same time

4️⃣ Watch for partial success

  • Multiple OKs
  • Inconsistent state

One success is enough.


Tools Used

ToolPurpose
Burp Suite IntruderParallel requests
RepeaterBaseline behavior
TimingExploit window
BrainAlways

Root Cause Analysis

❌ Developer Mistakes

  • No transaction locking
  • No atomic updates
  • Business logic split across steps
  • Assumed single-threaded behavior

Web apps are never single-threaded.


✅ Proper Fix (Non-Negotiable)

  • Database transactions
  • Atomic operations
  • SELECT ... FOR UPDATE
  • Idempotency keys
  • Server-side locking

Anything less is vulnerable.


Why These Bugs Are Rare but Severe

Because:

  • Hard to reproduce
  • Hard to test
  • Hard to explain
  • Easy to exploit once found

That’s why companies fear them.


Final Thoughts

Race condition bugs are not about skill.

They’re about thinking like time itself.

And time is on the attacker’s side.


Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.