race-condition
Race condition and TOCTOU testing for web apps. Use when testing one-time operations, concurrent HTTP abuse, rate-limit bypass, Turbo Intruder gates, HTTP/2 single-packet attacks, and CWE-362-style synchronization gaps.
By yaklang · 3,005 installs
npx skills add yaklang/hack-skills --skill race-condition
Source repository · Upstream listing
SKILL: Race Conditions — Testing & Exploitation Playbook
AI LOAD INSTRUCTION : Treat race conditions as authorization/state integrity issues: non atomic read then write lets multiple requests observe stale state. Prioritize one time or balance like operations. Combine parallel transport (HTTP/1.1 last byte sync, HTTP/2 single packet, Turbo Intruder gates) with application evidence (duplicate success responses, inconsistent balances, duplicate ledger rows). Authorized testing only. Routing note: for business workflows, coupons, inventory, or one time rewards, start with this skill and cross load business logic vulnerabilities .
0. QUICK START — What to Test First
Target endpoints where check and update are unlikely to be a single atomic database operation:
Priority Operation class Example paths / parameters
1 One time redeem / coupon / bonus redeem , apply coupon , claim reward , voucher
2 Balance / quota / stock deduction transfer , purchase , reserve , inventory
3 Invite / referral / signup bonus invite accept , referral claim
4 Password / email / MFA verification verify token , confirm email , reset password
5 Idempotent looking APIs without strong keys POST that should succeed only once per user
First moves (conceptual) :
1. Capture the state changing request in a proxy.
2. Send 20–100 copies as simultaneously as your tooling allows .
3. Classify outcome: 0/1 expected successes vs N successes or inconsistent final state .
1. CORE CONCEPT
1.1 TOCTOU (Time of check to time of use)
TOCTOU means the decision (check) and the mutation (use) are not one indivisible step.
1.2 Non atomic read then write
Typical vulnerable pseudo flow:
Two concurrent requests can both pass the if before either UPDATE commits.
1.3 Database level vs application level locking gaps
Layer What goes wrong
Application In memory flag, cache, or session says "not used yet" while DB already updated — or the reverse.
ORM / service Two instances, no distributed lock; each thinks it owns the decision.
DB Missing SELECT … FOR UPDATE , wrong isolation level, or logic split across multiple statements without transaction.
API gateway Per IP rate limit is check then increment — parallel burst passes duplicate checks.
Hint : UNIQUE constraints and idempotency keys often eliminate entire bug classes — test whether the app enforces them on the hot path.
2. ATTACK PATTERNS
2.1 Limit overrun (double redeem / double claim)
Send the same authenticated request many times in parallel:
Success signal : HTTP 200 / 201 more than once, duplicate ledger entries, or balance higher than policy allows.
2.2 Rate limit bypass via simultaneity
If limits are implemented as counters checked per request without atomic increment:
Fire N parallel attempts in one wave; compare with N sequential attempts.
Success signal : more failures accepted than documented cap, or lockout never triggers when burst completes inside one window.
2.3 Multi step exploitation (beat the pipeline)
Workflow: create → pay → confirm . If confirm does not cryptographically bind to pay completion:
1. Start two parallel pipelines from the same session/item.
2. Complete confirm on channel B while pay on channel A is still in flight or abandoned.
Success signal : item marked paid/shipped without matching payment, or state skips backward.
3. HTTP/1.1 LAST BYTE SYNCHRONIZATION
Idea : Hold all requests blocked until every socket has sent the full request except the last byte of the body; then release the final byte together so the server receives them in a tight cluster.
Why : Reduces network jitter between copies compared to naive sequential paste in Repeater.
Tooling : Custom scripts, some Burp extensions, or Turbo Intruder gate pattern (see §5) as the practical stand in for synchronized release.
4. HTTP/2 SINGLE PACKET ATTACK
Idea : Multiplex several complete HTTP/2 streams and coalesce their frames so the first bytes of all requests exit the NIC in one TCP segment (or minimally separated). Receiver side scheduling then processes them with sub millisecond spacing.
Burp Repeater (modern workflows) :
1. Open multiple tabs or select multiple requests.
2. Use Send group (parallel) / single packet attack where available.
3. Prefer HTTP/2 to the target if supported.
Why it often beats HTTP/1.1 last byte tricks : tighter alignment on the wire; less dependence on per connection serialization.
5. TURBO INTRUDER TEMPLATES
Repository: [PortSwigger/turbo intruder](https://github.com/PortSwigger/turbo intruder) (Burp Suite extension).
5.1 Template 1 — Same endpoint, gate release
Settings : concurrentConnections=30 , requestsPerConnection=30 , use a gate so all threads fire together.
Core pattern (repeat N times, then release):
Header requirement (unique per queued copy for log correlation; Turbo Intruder payload placeholder):
Turbo Intruder replaces %s per request when paired with a wordlist (or other payload source) — keep this header on the base request in Repeater before sending to Turbo Intruder. Case insensitive for HTTP; use a consistent name for log grep.
5.2 Template 2 — Multi endpoint, same gate
Pattern : One POST to target 1 (state change) plus many GETs to target 2 (read side) released together to widen the TOCTOU window observation.
Adjust hosts/paths by duplicating RequestEngine instances if endpoints differ (Turbo Intruder supports multiple engines — consult upstream docs for your Burp version).
6. CVE REFERENCE — CVE 2022 4037
CVE 2022 4037 (GitLab CE/EE): race condition leading to verified email address forgery and risk when the product acts as an OAuth identity provider — third party account linkage/impact scenarios. CWE 362 . Demonstrated in public research with HTTP/2 single packet style timing to win narrow windows.
Takeaway for testers : email verification, OAuth linking, and "confirm ownership" flows are high value race targets — not only coupons and balances.
References (official / neutral) :
[NVD — CVE 2022 4037](https://nvd.nist.gov/vuln/detail/CVE 2022 4037)
GitLab security advisories and vendor CVE JSON for affected version ranges
7. TOOLS
Tool Role
[PortSwigger/turbo intruder](https://github.com/PortSwigger/turbo intruder) High concurrency replay, gates , scripting in Burp.
[JavanXD/Raceocat](https://github.com/JavanXD/Raceocat) Race focused HTTP client patterns (verify compatibility with your stack).
[nxenon/h2spacex](https://github.com/nxenon/h2spacex) HTTP/2 low level / single packet style experimentation (use responsibly, authorized targets only).
Burp Suite — Repeater Send group (parallel) / single packet attack for multi request synchronization.
8. DECISION TREE
How to confirm (evidence checklist) :
1. Reproducible duplicate success under parallelism, not flaky single retries.
2. Server side artifact: two rows, two emails, two grants, or wrong final balance.
3. Correlate with x request (or similar) markers or unique body fields in logs (authorized environments).
Routing summary : if the scenario is more about business rules, pricing, or workflow bypass, load skills/business logic vulnerabilities/SKILL.md ; this file focuses on concurrency and transport layer synchronization .
9. HTTP/2 SINGLE PACKET ATTACK — DETAILED MECHANICS
9.1 TCP Nagle Algorithm & Frame Coalescing
TCP's Nagle algorithm (RFC 896) buffers small writes and coalesces them into fewer, larger segments. When an HTTP/2 client writes multiple HEADERS+DATA frames in rapid succession without flushing between them , the kernel merges them into a single TCP segment (up to MSS, typically ~1460 bytes on Ethernet).
TCP NODELAY disabled (default) → Nagle active → coalescing happens naturally
If TCP NODELAY is set, the client must use writev() / gather write syscall to batch frames
Practical limit: ~20–30 small requests per 1460 byte MSS; exceeding this splits across packets and degrades synchronization
9.2 Server Side Request Queue Processing
1. Single recv() syscall returns the entire segment
2. HTTP/2 frame parser demultiplexes streams from same segment
3. Dispatcher fans out to application worker pool
First to last request dispatch gap: < 100 μs on modern servers — orders of magnitude tighter than HTTP/1.1 last byte sync (~1–5 ms network jitter).
9.3 HTTP/2 vs HTTP/1.1 Last Byte Comparison
Factor HTTP/2 Single Packet HTTP/1.1 Last Byte
Connections needed 1 N (one per request)
Wire synchronization Same TCP segment N segments released "simultaneously"
Network jitter impact Zero (same packet) Each connection has independent RTT
Server dispatch gap < 100 μs 1–5 ms typical
Practical limit ~20–30 requests per MTU Limited by connection setup
9.4 Practical Execution with h2spacex
10. DATABASE ISOLATION LEVEL EXPLOITATION MATRIX
Isolation Level Phenomenon Exploited Attack Window Typical Vulnerable Pattern
READ UNCOMMITTED Dirty reads Thread B reads Thread A's uncommitted write SELECT balance sees in flight deduction, proceeds with stale logic
READ COMMITTED Non repeatable reads (TOCTOU) Both threads read committed balance, both pass check, both deduct SELECT → app check → UPDATE without FOR UPDATE
REPEATABLE READ Phantom reads Snapshot isolation hides concurrent inserts; both threads see "0 claims" and insert INSERT IF NOT EXISTS pattern without UNIQUE constraint
SERIALIZABLE Advisory lock bypass Application uses pg advisory lock() / GET LOCK() with wrong scope or derivable key Lock key from user input; session vs transaction scope mismatch
READ COMMITTED TOCTOU (most common in production)
Fix verification : SELECT ... FOR UPDATE should block Thread B's SELECT until Thread A commits.
REPEATABLE READ Phantom Insert
Fix : UNIQUE(user id, coupon id) constraint causes one INSERT to fail with duplicate key error regardless of isolation level.
SERIALIZABLE Advisory Lock Bypass
Quick Audit Checklist
11. LIMIT OVERRUN ATTACK PATTERNS
11.1 Coupon / Promo Code Reuse
Variations: same coupon across different cart items; apply coupon + checkout in parallel (coupon consumed only at checkout).
11.2 Vote / Rating Manipulation
11.3 Balance Double Spend
Higher value variant: withdrawal to external system (crypto, bank wire) where reversal is difficult.
11.4 Inventory Oversell
Compound attack: add to cart and checkout are separate steps, each checking inventory independently.
11.5 Referral / Signup Bonus
12. SINGLE PACKET MULTI ENDPOINT ATTACK
Instead of N copies of the same request, send requests to different endpoints in one HTTP/2 single packet burst. This widens the TOCTOU window by hitting both the check and use paths simultaneously.
Pattern 1: State check + State mutate
Balance inconsistency between stream 1 and stream 7 confirms the race window was hit.
Pattern 2: Cross resource race
If coupon application and checkout check prices independently, the discount may apply after checkout has locked the price.
Pattern 3: Auth verification + Privileged action
Upgrade may succeed during the brief window where verification is processing but not yet committed.
Practical setup
Burp Repeater: add requests targeting different paths to the same group → "Send group (single packet)".
Related
business logic vulnerabilities — workflow, coupon abuse, and logic first checklists ( ../business logic vulnerabilities/SKILL.md ).