In this lesson,
we're going to talk about race conditions
and the vulnerabilities associated with them.
Now, what exactly is a race condition?
Well, a race condition is a software vulnerability
that occurs when the resulting outcome
from the execution processes
is directly dependent on the order
and timing of certain events,
and those events failed to execute in the order
and timing intended by the developer.
Now, essentially, this is a complicated way
of saying that the computer is unexpectedly trying to race
itself in the processing of certain types of data.
For example, if you're trying to do something legitimately
and the attacker's trying to do something malicious
at the same time,
they might be able to get their request
in right before yours,
so they could take advantage
of a race condition vulnerability in the program's code
to run their processes before you can run yours.
Race condition vulnerabilities
are found when there are multiple threads
attempting to write to a particular variable or object
at the same memory location at the same time.
Now, one of the ways this can happen is by a dereference
or no pointer dereference exploit being exploited
to trigger that race condition.
Dereferencing is a software vulnerability
that occurs when the code attempts to remove
the relationship between a pointer
and the thing that that pointer was pointing
to inside of your memory.
Dereferencing will break the reference pairing
between the pointer
and the thing that was pointing to in memory,
and this allows changes to the location of memory
or the variable that contains that memory location.
Now race conditions are difficult to detect
and mitigate in our modern networks.
The Dirty COW exploit was extremely popular back in 2016,
that's an excellent example of a race condition
that was being exploited.
COW is actually an acronym
and it stands for "Copy On Write".
In the Dirty Cow Exploit,
the Linux operating systems and some Android devices
were being exploited to allow a local privilege escalation
to occur by exploiting a race condition vulnerability
based on the implementation of the programming
for the copy on write function
that was used inside of the Kernel's memory
management system.
Now because a race condition existed in this code,
if the attacker used the right timing,
they could exploit the copy and write mechanism
to turn the read-only mapping of a file
into a write mapping.
This would allow the attacker to have escalated privileges
because they can now write to a file
even though they're only supposed to have read access
to that file.
Now, what made this exploit even more dangerous
is that it was difficult to detect
because it didn't even leave anything
inside of the system logs
to let you know that it was happening.
And this is why race conditions are so dangerous,
because they often happen outside
of the normally logged processes on a given system.
While the Dirty COW vulnerability allows a race condition
against the memory management system,
race conditions can also be used against databases
and file systems too.
When race conditions are used to exploit a database
or file system,
they're generally going to take the form
of either a time-of-check,
time-of-use,
or time-of-evaluation vulnerability.
Now, first we have a Time-of-Check vulnerability,
a time-of-check vulnerability,
also known as a TOC vulnerability,
is a type of race condition that occurs
when an application checks the state of a system resource,
such as a file or database record,
and then makes a decision based on that check.
But then an attacker is able to manipulate
the state of the resource
before the application's operation actually occurs.
For example,
an application might check if a user has sufficient funds
before initiating a bank transfer.
However, if there's a significant time gap
between the time that the check occurs
and the actual money transfer happens,
the attacker could potentially initiate multiple transfers
in that time window,
which could end up overdrawing your bank account.
Second, we have a Time-of-Use vulnerability.
Now a time-of-use vulnerability
known as a TOU vulnerability,
is a type of race condition
that occurs when an attacker can change the state
of a system resource between the time it is checked
and the time it is used.
This is similar to the time of check vulnerability,
but it focuses more on the time that the resource is used
instead of focusing on the time that the check conducted.
Third, we have a Time-of-Evaluation vulnerability.
Now a time-of-evaluation vulnerability,
also known as a TOE Vulnerability,
is a type of race condition
that involves the manipulation of data or resources
during a time window
when a system is making a decision or evaluation.
For example, the attacker might manipulate inputs being used
inside of a calculation to cause incorrect results
to be used instead.
To protect against a race condition,
you can use locks and mutexes
to lock resources while a process is being run.
Now, a mutex is simply a mutually exclusive flag
that acts as a gatekeeper to a section of code,
so only one thread can be processed at a time
and this helps prevent race conditions from occurring
because we're not going to have that concurrent execution
of the code or function
because of that mutex being there.
Now, most databases have a method
that can be used to lock a resource
while it's being processed.
For example, if you've ever used SharePoint at work
to access a particular file for editing,
it actually puts a lock on that file
so that other people can read the file
while you're editing it,
but nobody else can check the file out for editing
while you have it in use.
This prevents two users from changing the file
at the same time
and helps us to maintain version control over our files.
This same concept is used when locking a record
in a database or using a mutex
to lock out the execution or function
of a particular part of the code
until the other threat is completed.
Overall, the use of locks and mutexes works pretty well
to prevent race conditions,
but sometimes they can cause a deadlock to occur.
Now, a deadlock occurs when a lock takes effect,
but the process that the lock is waiting for
is terminated,
crashes or simply doesn't finish properly,
which causes the lock to never be removed
from the resource even though the processing was completed.
When this occurs,
the system or code can lose access to the resource
until the lock is overridden or manually removed.
To prevent dead locking,
it's important that you properly design and test
any locks or mutexes that you're using inside of your code
to prevent a race condition.
So remember, a race condition is a software vulnerability
that occurs when the resulting outcome from the execution
processes is directly dependent on the order
and timing of certain events,
and those events failed to execute in the order
and timing intended by the developer.
There are three main types of race conditions
that you might come across in the real world,
including a time-of-check vulnerability,
a time-of-use vulnerability,
and a time-of-evaluation vulnerability.
All these race conditions can have serious implications
on the security and reliability
of your software applications.
Finally, it's important to remember
that to prevent a race condition
from being successfully used,
you need to mitigate these vulnerabilities
by using mutexes or resource locks.