Race Condition
Description
A Race Condition (also known as Time of Check to Time of Use) is a type of attack that exploits the order that an application carries out a task. Most commonly, the order in which a multi-threaded application performs a task. This allows an attacker to easily manipulate a shared resource to their gain.
Race Conditions are nearly always caused by poor atomic programming practices. A programmer has attempted to use threads, for example, to handle multiple inbound connections but has allowed them all to access one resource with no restrictions. However, not all race conditions cause harm; therefore, priority setting in finding and fixing problematic race conditions is crucial when correcting them.
While Race Conditions can appear in any programming language, some are much more resistant than others. An older programming language such as C, which gives full thread control and memory allocation to the programmer, is inherently more at risk due to minor mistakes having major complications. However, languages such as Go Lang come with built-in tools such as race detector, which natively support finding and fixing race conditions. These are much more resistant.
Race Conditions can be hard to find. However, many companies offer continuous bug bounties for them, as many are found once out in production where an attack could cause significant damage. Static analysis can be used internally to identify possible race conditions before pushing to production.
While not always prominent, race conditions have been around since the first multi-threaded applications, and as computer CPUs get more and more multi-threaded and, therefore, more of these applications, race conditions will not be going anywhere.
Impact
Race conditions can wreak internal havoc without even needing a malicious actor present. One of the worst bugs to directly affect human life was the Therac-25 radiotherapy machines. The company produced the software internally without much in the way of checks. After at least three people had died and many more were injured, it was discovered that a race condition was leading the machines to deliver over 100x the correct dose.
Other examples without a malicious actor can be as simple as auto deployment and CI pipelines where a simple race condition there can incorrectly configure and deploy server tools which can interrupt the availability of the pipeline.
Malicious users can use Race Conditions for personal gain, a bug on DropBox allowed users to redeem one 5GB voucher multiple times, resulting in a much larger amount given away. While not disastrous for DropBox, if this was financial or gift card related, such as this Reverb attack, it could cause much larger damage affecting the integrity of the storing database.
Scenarios
The simplest race condition is where a call is made to a method, and then another method edits a variable halfway through. For example, a method to subtract 5 from a number such as x:
def minus_five(x):
if x > 5: # Some arbitrary check
y = x - 5
If between x > 5
and y = x - 5
, another method comes along and modifies the value of x to below five, it would allow the application to enter an “illegal” state.
The use of locks here could fix this issue by:
# Locking here
def minus_five(x):
if x > 5: # Some arbitrary check
y = x - 5
# Unlocking here once complete
Prevention
Prevention of Race Conditions is entirely down to developers. Correct use of atomic programming and locks when required can completely mitigate the possibility of this type of attack.
Shared synchronous variables are another way to check to see if the method is already running using a global variable that, if set, calls a wait until unset by the initial method.
Testing
Most testing of race conditions comes down to built-in tools or community-built plugins to analyze the code and attempt to find them. As mentioned above, static analysis is also valuable for finding possible race conditions.
- OWASP ASVS: 1.11.3, 11.1.6
- OWASP Testing Guide: 4.4.10 - Testing for Race Conditions