Race Condition
This vulnerability is related to Business logic vulnerabilities
These occur when website processes requests concurrently without adequate safeguards.
Multiple distinct threads interacting with the same data at the same time can result in collisions that cause unintended behaviour.
An attacker uses carefully timed requests to cause collisions and exploit unintended behaviour (race condition).
The time window during which this collusion can occur is called the race window — it can be a fraction of a second between two interactions with the database.
This collusion is what we call Race window — could be a fraction of a second between two interactions with the database.
Limit overrun race conditions
Example scenario: promotional code in an online store / checkout for one-time discount on the order.
Typical steps performed by the server:
Check that you haven't already used this code.
Apply the discount to the order total.
Update the record in the database to reflect that you've now used this code.
Applying the discount code twice at the same time can cause a race condition:
The application can enter a sub-state (entered and then exits before request processing completes).
The substate begins when the server starts processing the first request and ends when it updates the database indicating you have already used the code.
This introduces a small race window during which you can repeatedly reclaim the discount as many times as you like.
Note: Limit overruns are subtypes of "Time-of-check to Time-of-use" (TOCTOU) flaws.
Kinds of attacks:
Redeeming a gift card multiple times
Rating a product multiple times
Withdrawing or transferring cash in excess of your account balance
Reusing a single CAPTCHA solution
Bypassing an anti-brute-force rate limit
Detecting and exploiting overrun race conditions with Burp Repeater
Identify a single-use or rate-limited endpoint with a security impact or useful purpose.
Issue multiple requests to this endpoint in quick succession.
The challenge is timing the requests so two race windows line up to cause a collision. This window is in milliseconds.
Various uncontrollable factors can affect when the server processes each request and in which order.
Using Burp Suite 2023.9 (or later) can help by sending requests in groups in parallel via Repeater.
Lab: Limit overrun race conditions — walkthrough
Lab introduction
Web_Pentest200: Pen-tester finds a website with a race condition.
Vulnerability / Problem
The site enables purchasing items for an unintended price due to a race condition.
Payload & End-goal
Goal: buy an item at a cheaper price than intended.
Reconnaissance plan
Login as a normal user.
Buy the cheapest item with a discount code.
Study the purchase flow and shopping cart mechanism.
Identify endpoints related to the cart in Burp History:
POST /cartadds items to the cart.POST /cart/couponapplies the discount code.
Confirm cart state is stored server-side (test
GET /cartwith and without session cookie).Determine if there is a race window between applying the discount and database update.
Attack
Ensure no discount in cart.
Send
POST /cart/couponto Repeater.Add the tab to a group and create ~19 duplicates.
Send the group of requests in sequence (separate connections) and observe responses.
The first usually succeeds; others are rejected with "Coupon already applied".
Exploit
Remove the discount from the cart.
Send the same group of requests in parallel.
If successful, multiple requests may indicate the code was applied multiple times.
Refresh the cart to confirm multiple 20% reductions applied.
Enumerate
Add an expensive item to the cart.
Resend the group of
POST /cart/couponin parallel and refresh cart.If order total falls below remaining store credit, attempt purchase.
Mitigate
Last remarks (see general mitigations later).
Detecting and exploiting limit overrun race conditions with Turbo Intruder
Turbo Intruder (BApp store) can be used to send many requests with controlled timing using Python.
Single-packet attacks require HTTP/2 and using
engine=Engine.BURP2andconcurrentConnections=1.Use the
gateargument to group requests andengine.openGate()to release them simultaneously.
Example Turbo Intruder script snippet:
Lab: Bypassing rate limits via race conditions — walkthrough
Lab introduction
Web_Pentest201: Pen-tester finds a site using rate limiting to defend against brute-force.
Vulnerability / Problem
Bypass per-username rate limits via race conditions.
Payload & End-goal
Goal: brute-force admin password (use list of common passwords).
Reconnaissance plan
Create two valid users.
Observe rate limit behavior (e.g., 3 attempts per username).
Confirm rate limit is enforced per-username and stored server-side.
There may be a race window between submitting a login attempt and incrementing the failed-attempt counter.
Attack (Repeater)
Find an unsuccessful
POST /loginin proxy history and send to Repeater.Duplicate the tab to create ~19 copies in a group.
Send the group in sequence; observe locking behavior after three attempts.
Exploit
Send the group in parallel.
If timed well, more than three attempts receive normal "Invalid username and password" responses before account lock triggers.
Enumerate (Turbo Intruder)
Highlight the
passwordvalue and send to Turbo Intruder.Mark the password parameter as payload position.
Use a single-packet attack template, assign
wordlists.clipboardfor passwords, and launch.Inspect results for successful login responses (e.g., 302), note corresponding password, wait for lock reset, then log in.
Mitigate
Study responses, repeat carefully, and note successful credentials if any.
Example Turbo Intruder single-packet template (concept):
Hidden multi-step sequences
A single request may initiate a multi-step sequence behind the scenes.
Applications pass through different sub-states before exiting the request process.
Identifying multiple HTTP requests that interact with the same data can allow you to abuse sub-states and expose time-sensitive logic flaws.
Example: Flawed multi-factor authentication (MFA) workflow:
Code snippet showing vulnerable flow:
If state changes are not atomic, forced browsing or race windows can bypass MFA.
Methodology for testing race conditions
1 — Predict potential collisions
Testing every endpoint is impractical. Map the target site to reduce endpoints to test:
Is this endpoint security-critical?
Is there collision potential? Successful collisions typically need two or more requests that operate on the same record.
Example: variations of a password reset implementation — requesting parallel resets for different users or using the same session ID for both.
2 — Probe for clues
Benchmark how the endpoint behaves under normal conditions.
Use Burp Repeater grouping and "Send group in sequence" (separate connections) to observe timings.
Use the single-packet attack or last-byte sync (if HTTP/2 not supported), or Turbo Intruder to reduce jitter.
Look for deviations from benchmark behaviour in responses or side-effects (e.g., emails, app changes).
Multi-endpoint race conditions
Classic example in online stores:
Add items to basket -> pay -> add more items -> force-browse to order confirmation.
Vulnerability occurs when payment validation and order confirmation are performed during the process of a single request.
You can add more items during the race window between payment validation and order confirmation.
Aligning multi-endpoint race windows
Lining up race windows across different endpoints is challenging, even with single-packet technique.
Two delaying factors:
Delays from network architecture (e.g., front-end server establishing backend connection, protocol used).
Endpoint-specific processing time variability.
Connection warming: send inconsequential requests first to smooth backend connection delays (e.g., add a GET / first in Repeater and send in sequence over single connection). If warming reduces variability, then backend warm-up was the issue.
Lab: Multi-endpoint race conditions — walkthrough
Lab introduction
Web_Pentest202: site with a purchasing flow that contains a race condition enabling unintended prices.
Vulnerability / Problem
Exploit multi-endpoint race conditions in purchasing flow.
Payload & End-goal
Goal: purchase an item at an unintended price (Burp Suite 2023.9+).
Reconnaissance plan
Create two valid users. Login and purchase a gift card to study the flow.
Identify cart endpoints (
POST /cart,POST /cart/checkout).Confirm cart state is session-keyed (
GET /cartwith/without cookie).Note that order validation and confirmation occur within a single request/response, implying a race window.
Attack
Send both
POST /cartandPOST /cart/checkoutto Repeater and group them.Send the two requests in sequence over a single connection repeatedly; observe the first often takes longer.
Add
GET /to warm the connection; the latter requests complete in a smaller window.Modify
POST /cartto add product ID 1 and test—order should be rejected for insufficient funds.
Exploit & Enumerate
Remove item, add another gift card, and send requests in parallel.
If you get a 200 for checkout, confirm success and enumerate further attempts to exploit.
Notes
If inconsistent timing persists even with single-packet, backend delays may interfere — Turbo Intruder may work better (use warming requests followed by attack requests).
Abusing rate or resource limits
If connection warming doesn't help, you can:
Introduce a short client-side delay using Turbo Intruder to split the attack across multiple TCP packets (avoid single-packet).
Send many dummy requests to trigger server-side rate/resource limits, then launch the actual attack.
Single-endpoint race conditions
Sending parallel requests with different values to the same endpoint can trigger race conditions.
Example: password reset mechanism that stores userID and reset token in session.
Send two parallel password reset requests from the same session with two different usernames.
Final state might store victim userID but the valid reset token sent to the attacker:
session['reset-user'] = victim
session['reset-token'] = 1234
Note: This requires the operations to occur in the right order and may need multiple attempts or luck. Email-based workflows are good targets.
Lab: Single-endpoint race conditions — walkthrough
Lab introduction
Web_Pentest203: site where the email-change feature has a race condition.
Vulnerability / Problem
Allows associating an arbitrary email with your account via race.
Payload & End-goal
Goal: identify a race condition that allows claiming an arbitrary email address.
Reconnaissance plan
Login and attempt to change your email to something like anything@email.net.
A confirmation email is sent with a unique token link.
Confirm the site stores a single pending email (new submissions overwrite existing pending email).
There's a potential collision between kicking off an email send and updating the pending email.
Attack
Send
POST /my-account/change-emailto Repeater and create ~19 duplicates.Modify each request to use different email addresses (
test1@,test2@, ...).Send sequence (one by one) and note one confirmation email per change.
Send the group in parallel; observe confirmation emails where the recipient and pending email don't match, indicating a race.
Exploit & Enumerate
Create a group with two copies of
POST /my-account/change-email.Set one email to your address and the other to
admin@domain.net.Send in parallel:
If a confirmation email body shows your address but the recipient is admin (or vice versa), use the confirmation link as appropriate to change account email and access admin features.
Notes
Success depends on timing and repeated attempts.
Session-based locking mechanisms
Some frameworks attempt to prevent accidental data corruption by locking requests per session.
PHP's native session handler typically processes one request per session at a time.
If you see this behaviour, try sending each request with different session tokens to bypass single-session locking.
Partial construction race conditions
Many applications create objects in multiple steps, leaving temporary middle states that are exploitable.
Example: user registration that creates the user record then sets the API key with a separate statement — there can be a short window where the user exists but the API key is uninitialised.
During the race window, an attacker may inject input that matches the uninitialised value (empty string, null) to bypass checks.
Framework specifics:
PHP:
param[]=foo→param=['foo'],param[]→param=[].Ruby on Rails: a parameter key with no value can render as
nil.
Example HTTP (possible exploit during race window):
Note: Similar collisions with passwords are possible, but because passwords are hashed you must inject a value that results in the same hash as the uninitialised value.
Lab: Partial construction race conditions — walkthrough
Lab introduction
Web_Pentest203: site where the user registration mechanism has a race condition.
Vulnerability / Problem
Allows bypassing email verification and registering with an arbitrary email address you control.
Payload & End-goal
Goal: exploit race condition to create an account (use Burp Suite 2023.9+ and Turbo Intruder).
Reconnaissance plan
Study registration mechanism; you can only register with
@domain.shopemails and must confirm via email.In Burp, find
/resources/static/users.jswhich generates the confirmation form (notice final confirmation isPOST /confirm?token=...).Craft confirmation requests in Repeater and test token parameter variants:
arbitrary token →
incorrect token:<YOUR-TOKEN>missing token →
Missing parameter: tokenempty token →
Forbiddentoken[](empty array) →Invalid token: Array— this suggests the server treats empty array differently and may match an uninitialised token.
Attack
Send
POST /registerto Repeater and experiment.Create a group with the
POST /registerand a craftedPOST /confirm?token[]=.Send sequentially and in parallel; note confirmation is often processed quicker than registration.
Exploit & Enumerate (Turbo Intruder)
Use Turbo Intruder to queue one registration request and many confirmation requests released together (gated).
Strategy:
Make
usernamea payload placeholder (%s), use a fixed password and@domain.shopemail.For each attempt, queue one registration and many confirmation requests (same gate).
openGate()to release them simultaneously.
Inspect results for 200 responses to
POST /confirm?token[]that indicate successful registration confirmation.Use the discovered username and password to login and access privileged areas.
Mitigate
(See general mitigations section below.)
Example Repeater / Turbo Intruder request template and script are included in the original content (kept as-is).
Time-sensitive attacks
Race techniques that produce requests with precise timing can reveal other vulnerabilities where non-cryptographic randomness is used (e.g., timestamps as tokens).
Example: password reset tokens generated from timestamps — two resets at the same timestamp may yield identical tokens.
Lab: Exploiting time-sensitive vulnerabilities — walkthrough
Lab introduction
Web_Pentest204: site with a password reset mechanism (not necessarily a race condition).
Vulnerability / Problem
The password reset token generation uses a predictable input (timestamp), which can be exploited with carefully timed requests.
Payload & End-goal
Goal: obtain a valid password reset token for a target user (requires a valid account).
Reconnaissance plan
Submit password reset requests multiple times and inspect emails/tokens.
Check token length, variability, and whether tokens change each time.
Duplicate the reset request and send in parallel to see if requests are processed sequentially.
Attack
If the site uses PHP (session locking), process one request per session. Remove the session cookie from one
GET /forgot-passwordto obtain a new session/cookie and CSRF token for a second session.Send two
POST /forgot-passwordrequests in parallel from different sessions.When response times match, the two confirmation emails may contain identical tokens — indicating a timestamp or predictable state in token generation.
Exploit & Enumerate
If two users receive the same token, change one request's username to the targeted user and resend in parallel.
If successful, a confirmation email with a token appears for the target. Use the token to reset password and log in.
Mitigate
Use cryptographically secure randomness for token generation and avoid predictable inputs (timestamps, counters) alone.
Note: you may need to try a few times to align server processing timestamps.
How to prevent race condition vulnerabilities
Goals: eliminate sub-states and make sensitive endpoints atomic.
Recommendations:
Avoid mixing data from different storage places.
Ensure sensitive endpoints make state changes atomically using datastore concurrency features (use a single database transaction to check and update).
Use datastore integrity features (e.g., uniqueness constraints) as defense-in-depth.
Don't rely on one storage layer (e.g., sessions) to secure another (e.g., database).
Ensure session handling keeps sessions internally consistent — avoid updating session variables individually rather than in a batch. ORMs should manage transactions correctly.
Where appropriate, consider avoiding server-side state and push state client-side securely (e.g., encrypted JWTs) if it fits the architecture.
Last updated