TOCTOU Race Condition Exploit Demo

Account 1 (Vulnerable)

${{ account1.balance.toFixed(2) }}
RACE CONDITION EXPLOITED!

Negative balance achieved through TOCTOU attack.

Account 2

${{ account2.balance.toFixed(2) }}

Attack Parameters

Attack Results

Total Transfers
{{ results.total }}
Successful
{{ results.success }}
Failed
{{ results.failed }}
Total Transferred
${{ results.totalTransferred.toFixed(2) }}
ID Time Amount Status Account 1 After Account 2 After
{{ op.id }} {{ op.time.toLocaleTimeString() }} ${{ op.amount.toFixed(2) }} {{ op.status }} ${{ op.account1After.toFixed(2) }} ${{ op.account2After.toFixed(2) }}

TOCTOU Vulnerability Successfully Exploited!

By performing {{ concurrentRequests }} concurrent transfers of ${{ transferAmount }} each while intentionally delaying operations by {{ operationDelay }}ms, we were able to:

  • Bypass balance checks simultaneously
  • Transfer more money than available in account 1
  • Result in a negative balance of ${{ Math.abs(account1.balance).toFixed(2) }}
  • Account 2 received ${{ account2.balance.toFixed(2) }}
  • Total funds discrepancy of ${{ (account1.balance + account2.balance - originalTotalFunds).toFixed(2) }}

Technical Explanation

TOCTOU Vulnerability Explained

This demo shows a classic Time-of-Check to Time-of-Use (TOCTOU) race condition in banking systems. The vulnerability occurs when funds are transferred between accounts without proper locking.

Vulnerable Code Flow (Fixed Version)

// UNSAFE - Vulnerable to TOCTOU
async function transferVulnerable(amount) {
    // 1. Check balance (Time-of-Check)
    if (account1.balance >= amount) {
        
        // 2. WINDOW OF VULNERABILITY - Other threads can pass the check
        
        // 3. Simulate processing delay 
        await delay(operationDelay);
        
        // 4. Transfer funds (Time-of-Use)
        account1.balance -= amount;
        account2.balance += amount;
        
        return true;
    }
    return false;
}

Key Changes Made

  • Implemented actual transfers between accounts (withdraw + deposit)
  • Added account2 tracking in operation records
  • Fixed result calculation to show total transferred amount
  • Updated UI to show both account balances after each operation
  • Added funds discrepancy calculation to highlight the race condition effect

How to Exploit

  1. Start with $1000 in account 1 and $0 in account 2
  2. Set transfer amount to $200
  3. Set concurrent requests to 10
  4. Set delay to 500ms
  5. Run in vulnerable mode - account 1 will go negative while account 2 gets multiple deposits

Protected Implementation

// SAFE - Uses locking to prevent race conditions
async function transferProtected(amount) {
    const release = await account1.lock.acquire();
    
    try {
        if (account1.balance >= amount) {
            // No window of vulnerability
            account1.balance -= amount;
            account2.balance += amount;
            return true;
        }
        return false;
    } finally {
        release();
    }
}