Receber por email
← Voltar para o catálogo

hunt-race-condition

Uma skill para caçar vulnerabilidades de condição de corrida, desenvolvida a partir de 12 relatórios públicos de bug bounty. Abrange casos modernos de ataque de pacote único HTTP/2 e cenários comuns como resgate duplo de cupom, gasto duplo de cartão-presente, corrida de validação MFA-OTP, corrida de criação de conta e gasto duplo de token cripto.

2.6kestrelas
Atualizado há 4 meses

Ver no GitHub ↗ Licença: NOASSERTION

Como adicionar

/plugin marketplace add elementalsouls/Claude-BugHunter

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/hunt-race-condition/svg)](https://www.skillteca.com.br/skills/hunt-race-condition)

Skills relacionadas

Ver todas de Segurança →

Alerta por categoria

Receba novas skills de Segurança toda segunda

1 email curto, só com as skills novas de Segurança. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Crown Jewel Targets

Race conditions are high-severity findings because they break financial, access control, and integrity assumptions that defenders rarely stress-test. Highest payouts come from:

  • Monetary/credit systems — double-spending gift cards, coupons, referral bonuses, promotional credits, wallet balances
  • Vote/reputation manipulation — upvoting the same content multiple times, gaming leaderboards or trending algorithms
  • Account limits bypass — exceeding free-tier quotas, bypassing "one per user" restrictions on invites, trial activations, or API key generation
  • Privilege escalation — racing role assignment or permission checks during user creation/upgrade flows
  • Deletion bypass — reading or exfiltrating data during a narrow window between "marked for deletion" and "actually deleted"
  • Payment flows — charging a card once but receiving multiple fulfillments

Best-paying asset types: Fintech apps, SaaS platforms with credit/subscription models, social platforms with reputation systems, e-commerce checkout flows, OAuth/SSO token endpoints.

Attack Surface Signals

URL Patterns

/vote, /upvote, /like, /favorite
/redeem, /apply-coupon, /use-code, /claim
/purchase, /checkout, /confirm-order, /pay
/transfer, /withdraw, /send-money
/invite, /referral, /accept-invite
/upgrade, /activate, /trial
/delete, /deactivate, /cancel
/follow, /subscribe

Response Headers That Signal Race-Prone Backends

X-RateLimit-*        # rate limiting exists, but may not be atomic
X-Request-Id         # each request independently tracked
No Cache-Control     # stateful ops not idempotent

JavaScript Patterns to Grep

// Single-use action buttons with client-side disable
button.disabled = true
$('#btn').prop('disabled', true)
// Optimistic UI updates (state set before server confirms)
setState({ used: true })
// Sequential async calls without locking
await useVoucher(); await deductBalance();

Tech Stack Signals

  • Ruby on Rails without with_lock / lock! — ActiveRecord doesn't lock by default
  • Node.js with async/await chains — non-atomic DB reads then writes
  • PHP without SELECT ... FOR UPDATE — common in legacy codebases
  • Microservices — inter-service calls introduce natural TOCTOU windows
  • Redis counters without Lua scripts or INCR atomicity checks
  • Message queues — idempotency keys often missing

Step-by-Step Hunting Methodology

  1. Enumerate one-time or limited-use actions — Map every endpoint that enforces a "once per user", "limited quantity", or "deduct balance" constraint. These are your primary targets.
  1. Understand the state machine — For each target action, identify: (a) what state is read, (b) what state is written, (c) what validation sits between read and write. The gap between read and write is your window.
  1. Capture a clean baseline request — Perform the action once legitimately with Burp Suite intercepting. Confirm you get the expected single-use behavior (e.g., coupon marked used, vote counted once).
  1. Set up parallel request tooling — Use one of:
  2. Burp Suite Repeater → "Send group in parallel" (Turbo Intruder for HTTP/2 single-packet attacks)
  3. Turbo Intruder with engine=Engine.BURP2 for last-byte sync
  4. curl with & backgrounding
  5. Python threading or asyncio with pre-built connections
  1. Execute the race — Send 10–50 identical requests simultaneously. Key technique: pre-connect and buffer all requests, release the final byte of all simultaneously (single-packet attack when HTTP/2 is available).
  1. Analyze responses — Look for:
  2. Multiple 200 OK where only one should succeed
  3. Duplicate success messages
  4. Database constraint errors (signals the race worked but hit the last-line-of-defense)
  5. Inconsistent response times (one fast, rest slow = serialized; all same speed = parallel processing)
  1. Verify the effect — Check the actual state: Was the credit applied twice? Did the vote count increment multiple times? Is the coupon still marked unused despite two successes?
  1. Determine exploitability window — Re-run with decreasing parallelism (5 requests, 3 requests, 2 requests) to understand how tight the window is and reliability of exploitation.
  1. Test across account types — Sometimes the race only works for new accounts, specific subscription tiers, or under specific server load. Test varied conditions.
  1. Document reproducibility — Record exact timing, number of parallel requests needed, and success rate across 5 independent attempts before reporting.

Payload & Detection Patterns

Turbo Intruder — Basic Parallel Race

# turbo_intruder_race.py
def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=1,
                           engine=Engine.BURP2)  # HTTP/2 single-packet
    for i in range(20):
        engine.queue(target.req, gate='race1')
    engine.openGate('race1')

def handleResponse(req, interesting):
    if '200' in req.status:
        table.add(req)

curl — Parallel Requests (bash)

# Fire 15 simultaneous vote/redeem requests
for i in $(seq 1 15); do
  curl -s -o /dev/null -w "%{http_code}\n" \
    -X POST "https://target.com/api/vote" \
    -H "Cookie: session=YOUR_SESSION" \
    -H "Content-Type: application/json" \
    -d '{"report_id": "12345", "vote": "up"}' &
done
wait

Python asyncio Race

import asyncio, aiohttp

async def race_request(session, url, payload, headers):
    async with session.post(url, json=payload, headers=headers) as r:
        return await r.text()

async def main():
    url = "https://target.com/redeem"
    payload = {"code": "GIFT50"}
    headers = {"Cookie": "session=XXXXX"}
    
    async with aiohttp.ClientSession() as session:
        tasks = [race_request(session, url, payload, headers) for _ in range(20)]
        results = await asyncio.gather(*tasks)
    
    for r in results:
        print(r[:100])  # print first 100 chars of each response

asyncio.run(main())

Grep Patterns for Source Code Auditing

# Look for read-then-write without locking
grep -rn "find_by\|where.*first" --include="*.rb" | grep -v "lock"
grep -rn "SELECT.*WHERE" --include="*.php" | grep -v "FOR UPDATE"

# JavaScript async without atomicity
grep -rn "await.*get\|await.*find" --include="*.js" -A2 | grep "await.*update\|await.*save"

# Python Django ORM without select_for_update
grep -rn "\.get(\|\.filter(" --include="*.py" | grep -v "select_for_update"

HTTP/2 Single-Packet Check

# Verify target supports HTTP/2 (prerequisite for single-packet attack)
curl -sI --http2 https://target.com | grep -i "HTTP/2\|h2"

Common Root Causes

  1. Check-Then-Act without atomic operations — Developer reads state (if voucher.used == false), then writes state (voucher.update(used: true)) in two separate database operations. Any thread can read the same "unused" state before either writes.
  1. Missing database-level locking — Using ORM methods like find or filter instead of SELECT ... FOR UPDATE. The fix is one line but developers don't think about concurrency.
  1. Optimistic concurrency without version checking — Systems increment counters or mark records without checking if the record changed since it was read.
  1. Microservice TOCTOU — Service A validates eligibility, Service B executes the action. No shared atomic transaction spans both services.
  1. Client-side "protection" — Developers disable the button in JavaScript after first click, assuming that prevents duplicate submissions. Server-side logic is never hardened.
  1. Counter increments outside transactions — votes_count += 1; save() instead of an atomic SQL UPDATE SET votes = votes + 1 WHERE id = ?.
  1. Async background jobs — Eligibility checked synchro
Compartilhar X LinkedIn