`shuvonsec/race-condition-tester`: The Small Python Script That Makes Threads Hit All at Once
A zero-dependency race-condition tester for bug bounty work, built around one idea that matters: synchronize every request, then let the server blink first.
- The script's real leverage is synchronization, because it turns timing into the exploit primitive instead of treating it as a side effect of threading.
- Its zero-dependency design is a portability choice, which makes it easy to drop onto a jump box or a locked-down host without installing anything.
- GraphQL and other compact mutation surfaces are especially interesting here because one request can hide several state changes behind a single endpoint.
- The broader lesson is that coordinated observers win when state updates lag behind them, whether the target is 2FA, coupons, or payment logic.
Most race-condition scripts spray requests and hope timing lines up. This repo does something sharper: it holds every worker at a barrier, then releases them together so the server has to answer a synchronized rush. That is the difference between noise and a timing primitive.
The release valve, not the flood
The barrier is the whole trick. Every thread gets ready first, then blocks until the last one arrives, and only then do they all move. The point is not to create more traffic. The point is to compress arrival time so the first write, the first redemption, or the first failed login lands before the server has updated its own state.
Why ordinary threads miss the window
Plain threading is messy. Thread startup, scheduler jitter, network latency, and interpreter overhead smear request timing across a wider window than you want. If you are testing a race, that smear is the enemy, because the server only has to recover between arrivals to make the bug disappear.
That is why the barrier matters more than the thread count. A dozen badly timed requests are weaker than four perfectly aligned ones. The script is trying to create a thundering herd on purpose, which is exactly what most web defenses are least prepared to reason about.
How the script is wired
import json
import threading
import urllib.error
import urllib.request
barrier = threading.Barrier(worker_count)
def post(url, payload):
data = json.dumps(payload).encode('utf-8')
req = urllib.request.Request(url, data=data, headers={'Content-Type': 'application/json'})
try:
with urllib.request.urlopen(req, timeout=10) as resp:
return resp.status, resp.read().decode('utf-8', 'replace')
except urllib.error.HTTPError as exc:
return exc.code, exc.read().decode('utf-8', 'replace')
def worker(payload):
barrier.wait()
return post(target_url, payload)
The standard library choice is not a compromise. It is a deployment strategy. By leaning on `urllib.request`, `threading`, and `urllib.error.HTTPError`, the tool can run on a machine with no `pip install` step, which is exactly what you want on a jump box, a VPS, or any environment where speed matters more than polish.
What it targets
The interesting targets here are not just classic low-level races. The repo is pointed at 2FA rate limits, duplicate actions, coupon-style mutations, negative amounts, and other business-logic failures where the first request should win and the rest should be rejected. GraphQL is especially useful because it often compresses the check and the action into one mutation, which can widen the gap between intention and actual state.
| Tool | Main goal | Synchronization method | Best at | Trade-off |
|---|---|---|---|---|
| race-condition-tester | Force simultaneous HTTP requests against web targets | Python threading.Barrier | Lightweight bug bounty tests and portable one-off runs | Narrow scope and less throughput than larger purpose-built suites |
| TREM | Exploit web timing windows with higher precision | Network-level request orchestration | Precision race exploitation on modern HTTP services | More specialized to learn and operate |
| Fray | Expose concurrency bugs in Java code by steering execution | Scheduler control and replay | Deterministic bug finding in tests and production code | Not a web request attack tool |
| ThreadSanitizer | Detect shared-memory data races during development | Runtime instrumentation | C, C++, and Go debugging workflows | Finds code races, not request-level exploit windows |
Seen next to broader tools, the repo lands in a very specific lane. It is small, surgical, and easy to carry. That makes it useful when the job is not to build a framework, but to prove whether one timing window exists on one endpoint, right now.
Why the zero-dependency choice matters
A bigger tool can be better in the abstract and worse in the field. If you need a browser, a proxy stack, or a heavy runtime just to start testing, you have already made the workflow harder than it needs to be. This repo keeps the surface area tiny, and that is an advantage when the real problem is finding a narrow window before the server state catches up.
The larger lesson
Race conditions are not only a CPU problem. Any system that checks, then acts, then updates state can be beaten by a coordinated observer who arrives faster than the state machine heals. That is why payments, rate limits, coupon redemption, and multi-step account flows deserve the same suspicion as low-level locks.