Trick 1: "A race condition can only be exploited if the attacker has physical access to the system." True or False?
FALSE β race conditions are exploited through normal program interfaces, not through physical access.
The attacker controls timing by running automated loops β sending requests, writing files, triggering operations β not by physically touching hardware. There is no requirement to be near the machine.
Remote race condition exploitation is well-documented:
What physical access actually gives an attacker: Physical access enables other attack classes β tampering with hardware, installing keyloggers, direct memory access β but it is not a prerequisite for race condition exploitation. The attacker only needs the ability to trigger the vulnerable program and influence the shared resource, both of which are routinely achievable remotely.
Exam tip: Race conditions are a software vulnerability class. They are exploited through software interfaces. Physical access is neither required nor particularly helpful for this class of attack.
The attacker controls timing by running automated loops β sending requests, writing files, triggering operations β not by physically touching hardware. There is no requirement to be near the machine.
Remote race condition exploitation is well-documented:
- Web API double-spend attacks: two HTTP requests submitted simultaneously from anywhere on the internet
- Remote file replacement via NFS: an attacker with network access to a shared filesystem can replace files between check and write
- Concurrent session token attacks: two requests sharing the same session token sent from the same browser or from automated scripts
What physical access actually gives an attacker: Physical access enables other attack classes β tampering with hardware, installing keyloggers, direct memory access β but it is not a prerequisite for race condition exploitation. The attacker only needs the ability to trigger the vulnerable program and influence the shared resource, both of which are routinely achievable remotely.
Exam tip: Race conditions are a software vulnerability class. They are exploited through software interfaces. Physical access is neither required nor particularly helpful for this class of attack.
Trick 2: "A race window of 1 millisecond is too small to exploit in practice." True or False?
FALSE β 1 millisecond is 1,000 microseconds, during which a tight loop can execute thousands of operations.
An attacker running concurrent loops β one triggering the vulnerability, one modifying the resource β will land a modification inside a 1ms window within seconds of starting. The attack does not require manually timed precision. It requires automation, which is trivially available.
Why "too small" is the wrong frame: The relevant question is not "is the window shorter than human reaction time?" β it is "how many attacker operations fit inside the window per second?" A modern processor executing millions of instructions per second can fire thousands of file replacement or API calls per second. Statistically, at least one will land inside a 1ms window very quickly.
What the window size actually affects: A smaller window means more attempts are needed before the attacker wins. It does not mean the attacker cannot win. The attacker only needs to succeed once β and automation means they can attempt thousands of times per second until they do.
Race conditions with windows measured in microseconds have been demonstrated in real exploitation research against operating system utilities, package managers, and setuid programs. The size of the window affects how quickly the exploit succeeds, not whether it can succeed at all.
An attacker running concurrent loops β one triggering the vulnerability, one modifying the resource β will land a modification inside a 1ms window within seconds of starting. The attack does not require manually timed precision. It requires automation, which is trivially available.
Why "too small" is the wrong frame: The relevant question is not "is the window shorter than human reaction time?" β it is "how many attacker operations fit inside the window per second?" A modern processor executing millions of instructions per second can fire thousands of file replacement or API calls per second. Statistically, at least one will land inside a 1ms window very quickly.
What the window size actually affects: A smaller window means more attempts are needed before the attacker wins. It does not mean the attacker cannot win. The attacker only needs to succeed once β and automation means they can attempt thousands of times per second until they do.
Race conditions with windows measured in microseconds have been demonstrated in real exploitation research against operating system utilities, package managers, and setuid programs. The size of the window affects how quickly the exploit succeeds, not whether it can succeed at all.
Trick 3: "Adding a mutex lock to prevent a race condition always makes the program safer." True or False?
FALSE β mutex locks prevent race conditions, but incorrectly implemented mutexes can introduce deadlock.
Deadlock occurs when two threads each hold a lock the other needs β neither can proceed, and the program halts. A halted program is a denial of service. The security problem (race condition) may be solved while a new availability problem (deadlock) is introduced.
Two ways mutex implementation fails:
What correct mutex implementation requires: Locks acquired in a consistent order across all threads; locks released in all code paths including exception handlers and early returns; appropriate timeout handling if locking may block for long periods. A mutex implemented correctly β acquired before the check, released after the use, in all code paths β does make the program safer against race conditions. "Always makes safer" is the false word β it is conditional on correct implementation.
Deadlock occurs when two threads each hold a lock the other needs β neither can proceed, and the program halts. A halted program is a denial of service. The security problem (race condition) may be solved while a new availability problem (deadlock) is introduced.
Two ways mutex implementation fails:
- Deadlock: Thread A acquires Lock 1 and waits for Lock 2. Thread B acquires Lock 2 and waits for Lock 1. Both are permanently blocked. The program stops. Locks must be acquired in a consistent order across all threads to prevent deadlock.
- Lock abandonment: A mutex is acquired but an exception or early return causes the lock to never be released. All other threads attempting to access the protected resource block indefinitely. One code path failure stops the entire resource permanently.
What correct mutex implementation requires: Locks acquired in a consistent order across all threads; locks released in all code paths including exception handlers and early returns; appropriate timeout handling if locking may block for long periods. A mutex implemented correctly β acquired before the check, released after the use, in all code paths β does make the program safer against race conditions. "Always makes safer" is the false word β it is conditional on correct implementation.
Trick 4: "Logging file accesses prevents TOCTOU attacks." True or False?
FALSE β logging records that something happened; it does not prevent anything from happening.
A TOCTOU attack succeeds or fails based on whether the race window is exploited before the write occurs. By the time a log entry is written, the write has already happened. Logging is reactive β it documents events after the fact.
Why logging provides no TOCTOU protection:
What logging does provide: After a TOCTOU attack is discovered, logs can confirm what was written and when, help reconstruct the timeline, and identify when the symlink was placed. This supports incident response and attribution β not prevention.
TOCTOU prevention requires: Making the check and use atomic, or protecting them with a mutex so no other thread can modify the resource between check and use. Those controls close the race window. Logging observes through it.
A TOCTOU attack succeeds or fails based on whether the race window is exploited before the write occurs. By the time a log entry is written, the write has already happened. Logging is reactive β it documents events after the fact.
Why logging provides no TOCTOU protection:
- The log entry for a file write is written after the file write completes β the damage is already done
- Logging the check and the write as separate events does not close the gap between them
- An attacker's symlink replacement may not itself be logged β the log may show only the program's write, not the swap that preceded it
- Even if both events are logged with timestamps, the log is forensic evidence, not a prevention control
What logging does provide: After a TOCTOU attack is discovered, logs can confirm what was written and when, help reconstruct the timeline, and identify when the symlink was placed. This supports incident response and attribution β not prevention.
TOCTOU prevention requires: Making the check and use atomic, or protecting them with a mutex so no other thread can modify the resource between check and use. Those controls close the race window. Logging observes through it.
Performance Task: A software team is developing a ticketing system. When a user purchases the last available ticket, the system: (1) queries the database to check if tickets remain (time of check), (2) displays a confirmation page which takes approximately 200ms to render, (3) then inserts the purchase record and decrements the ticket count (time of use). The development lead says the 200ms rendering gap between check and use is not a security concern because "it's just a UI delay." Evaluate this claim, identify the vulnerability, and describe the correct fix.
Model Answer:
Vulnerability identification: This is a textbook TOCTOU race condition. The 200ms rendering delay between the availability check (step 1) and the purchase commit (step 3) is a 200ms race window β an extremely large window by race condition standards. Two users who both reach the confirmation page at the same moment have both passed the availability check before either's purchase is committed. Both will complete their purchase, overselling the last ticket.
Evaluation of the claim: The development lead is wrong. 200ms is not "just a UI delay" β it is 200 milliseconds during which hundreds of concurrent users can pass the same availability check before any purchase is committed. In a high-demand ticketing scenario (concerts, sports events), thousands of users may hit the purchase endpoint simultaneously. Many of them will be in the race window simultaneously. Without protection, the system will sell the same ticket to multiple buyers.
Correct fix: The check and purchase must be performed as a single atomic database transaction. The correct approach:
With this approach, no second transaction can read the ticket count until the first transaction commits. The race window is closed because the check and use are a single locked, atomic operation. Users who reach the confirmation page but lose the lock will see a "sold out" message β correct behaviour.
Broader lesson: The 200ms UI delay makes this vulnerability more severe, not less. Any architecture that separates a resource check from the resource commitment β even by milliseconds β creates a race window. High-demand scenarios (ticketing, payment processing, inventory) are precisely where race conditions cause the most visible and costly failures. The development lead's framing of "UI delay" obscures that this is a database consistency and security problem, not a rendering problem.
Vulnerability identification: This is a textbook TOCTOU race condition. The 200ms rendering delay between the availability check (step 1) and the purchase commit (step 3) is a 200ms race window β an extremely large window by race condition standards. Two users who both reach the confirmation page at the same moment have both passed the availability check before either's purchase is committed. Both will complete their purchase, overselling the last ticket.
Evaluation of the claim: The development lead is wrong. 200ms is not "just a UI delay" β it is 200 milliseconds during which hundreds of concurrent users can pass the same availability check before any purchase is committed. In a high-demand ticketing scenario (concerts, sports events), thousands of users may hit the purchase endpoint simultaneously. Many of them will be in the race window simultaneously. Without protection, the system will sell the same ticket to multiple buyers.
Correct fix: The check and purchase must be performed as a single atomic database transaction. The correct approach:
- Begin a database transaction
- SELECT the ticket count WITH a row-level lock (SELECT ... FOR UPDATE) β this prevents any other transaction from reading or modifying the count until this transaction commits
- If count > 0: decrement count and insert the purchase record in the same transaction
- Commit the transaction (releases the lock)
With this approach, no second transaction can read the ticket count until the first transaction commits. The race window is closed because the check and use are a single locked, atomic operation. Users who reach the confirmation page but lose the lock will see a "sold out" message β correct behaviour.
Broader lesson: The 200ms UI delay makes this vulnerability more severe, not less. Any architecture that separates a resource check from the resource commitment β even by milliseconds β creates a race window. High-demand scenarios (ticketing, payment processing, inventory) are precisely where race conditions cause the most visible and costly failures. The development lead's framing of "UI delay" obscures that this is a database consistency and security problem, not a rendering problem.