Skip to main content

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.

Source facts

Repository
yaklang/hack-skills
Last source activity
April 21, 2026 at 21:48
Detected SKILL.md language
English
Stars
2,458
Forks
331

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions ยท Read-only preview
name
race-condition
description
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.
# 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) ``` Thread A Thread B | | +-- CHECK (resource OK) | | +-- CHECK (resource OK) โ† both see "OK" +-- USE / UPDATE | | +-- USE / UPDATE โ† duplicate effect ``` **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: ```text balance = SELECT balance FROM accounts WHERE id = ? if balance >= amount: UPDATE accounts SET balance = balance - ? WHERE id = ? ``` 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: ```http POST /api/v1/rewards/claim HTTP/1.1 Host: target.example Authorization: Bearer <token> Content-Type: application/json {"reward_id":"welcome_bonus"} ``` **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: ```http POST /api/v1/login HTTP/1.1 Host: target.example Content-Type: application/json {"email":"victim@example.com","password":"wrong"} ``` 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. ```text Client 1: [headers + body - 1 byte] ----hold----+ Client 2: [headers + body - 1 byte] ----hold----+--> flush last byte together Client N: [headers + body - 1 byte] ----hold----+ ``` **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. ```text [ Req A stream ] [ Req B stream ] --HTTP/2--> one burst --> app worker pool [ Req C stream ] ``` **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): ```python for _ in range(N): engine.queue(request, gate='race1') engine.openGate('race1') ``` ```python def queueRequests(target, wordlists): engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=30, requestsPerConnection=30, pipeline=False, engine=Engine.THREADED, maxRetriesPerRequest=0 ) for i in range(30): engine.queue(target.req, gate='race1') engine.openGate('race1') def handleResponse(req, interesting): table.add(req) ``` **Header requirement** (unique per queued copy for log correlation; Turbo Intruder payload placeholder): ```http x-request: %s ``` 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. ```python def queueRequests(target, wordlists): engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=30, requestsPerConnection=30, pipeline=False, engine=Engine.THREADED, maxRetriesPerRequest=0 ) engine.queue(post_to_target1, gate='race1') for _ in range(30): engine.queue(get_target2, gate='race1') engine.openGate('race1') ``` 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 ```text START: state-changing API? | NO -----------+---------- YES | | stop here one-time / balance / verify? | +-------------------------+-------------------------+ | | | coupon-like rate limit multi-step | | | parallel same req parallel vs serial parallel pipelines | | | duplicate success? limit exceeded? state mismatch? / \ / \ / \ YES NO YES NO YES NO | | | | | | report + try HTTP/2 report + try TI report + deepen evidence single-packet evidence gates per-step | | | | | | +----+----+ +----+----+ +----+----+ | | | tool pick tool pick tool pick v v v Burp group / h2spacex TI gates / Raceocat TI + trace IDs ``` **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). ```text Application layer: [Stream 1 H+D] [Stream 3 H+D] [Stream 5 H+D] โ†“ TCP Nagle coalescing โ†“ TCP segment: [Stream 1 H+D | Stream 3 H+D | Stream 5 H+D] โ† one packet on the wire ``` - `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 ```text NIC IRQ โ†’ kernel recv buffer โ†’ HTTP/2 demuxer โ†’ concurrent dispatch โ”Œโ”€ Stream 1 โ†’ worker thread A โ”€โ” โ”œโ”€ Stream 3 โ†’ worker thread B โ”€โ”ค sub-microsecond spacing โ””โ”€ Stream 5 โ†’ worker thread C โ”€โ”˜ ``` 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" |
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub