TechKnowSurge
VideoSecurityFree

DEMO: Race Conditions

Race conditions occur when multiple processes access or modify shared resources simultaneously, creating a vulnerability that can be exploited to bypass security controls or corrupt data. Understanding the time-of-check to time-of-use (TOCTOU) gap is essential for identifying and defending against this class of weakness.

Complete this video to capture a CTF flag worth 1 point.

About this video

A race condition is a weakness, defined under CWE-362, that occurs when multiple processes or threads access a shared resource concurrently and the outcome depends on the timing or sequence of those accesses. The condition is not inherently an attack, but it creates a window of vulnerability that can be exploited or that can produce harmful unintended behavior on its own. The critical concept at the center of this weakness is the time-of-check to time-of-use (TOCTOU) interval — the gap between the moment a system reads or verifies a value and the moment it acts on that value. If another process modifies the shared resource during that gap, the system proceeds based on data that is no longer accurate. This plays out across many real-world scenarios. In financial systems, a bank account balance verified at the time of check may no longer reflect the true balance by the time a transaction is processed, allowing overdrafts or duplicate disbursements. In software, two threads incrementing a shared counter simultaneously may both read the same initial value, each add to it independently, and then each write back a result that ignores the other's update — producing a final count far below what is correct. A live demonstration of this behavior, using ten concurrent threads each intended to increment a counter 100,000 times toward a target of one million, consistently yields results well below that target due to overlapping reads and writes. From a security perspective, TOCTOU gaps can be deliberately targeted. A rapid succession of requests submitted before a system updates a state flag — such as whether a promotional discount has already been applied — can cause that check to pass multiple times before the flag is ever set, allowing an attacker to apply a discount far more times than permitted. Similarly, authenticated session tokens granted at login may remain valid even after an account is revoked, giving a terminated user continued access to protected resources during the interval between credential removal and session expiration. Mitigating race conditions requires awareness of concurrent access patterns and the implementation of controls such as proper locking mechanisms, atomic operations, and prompt session invalidation procedures.

What you'll learn

What's covered

Race Conditions

Key terms

Race Condition
A software vulnerability that occurs when a system's behavior depends on the timing or sequence of events — an attacker who can manipulate the window between a time-of-check and a time-of-use can alter the outcome in their favor.
Time of Check to Time of Use
TOCTOU
Time of Check to Time of Use is a race condition vulnerability where a resource's state is verified at one moment but changes before it is actually used, allowing attackers to substitute malicious content in the intervening window.
Vulnerability
A weakness in a system, application, or process that can be exploited by a threat actor.
Exploit
A piece of software or technique that takes advantage of a vulnerability to gain unauthorized access or cause harm.
Privilege Escalation
An attack that exploits vulnerabilities to gain higher-level access than originally authorized.

Topics

Race Conditions Toctou Concurrency Application Security Vulnerability Exploitation Secure Coding

Transcript

We're going to get into race conditions. This isn't exactly an attack — it's more of a condition, something that exists, but we can utilize it to carry out an attack. Just a reminder that this is for educational and ethical hacking purposes only. This is to protect systems, not to compromise them.

The wiki article on race conditions calls out that it's not just software — it could be electronics or really any other system. This is more of a general concept that we commonly apply to software development. There's also the common weakness enumeration, CWE-362, which defines a race condition as being a weakness. It gives us a description of what this looks like, and it even gives us a little picture of it. It's just that there's code accessing the same piece of information at the same time, and that can become problematic.

A bank account example

Let's use the example of a bank account right here. We have a bank account that has $1,000 in it. Somebody writes a check for $500, and that same day writes another check for $800. Both these checks are verified: whoever is accepting this money, accepting this check, checks the bank account and sure enough finds that there's $1,000 in the bank. So this check says, okay, yeah, you're good to go to accept this check. The same thing happens with this $800 one.

But it doesn't happen until a day or two, or maybe a week later, that these get deducted from the bank account. They get turned into the bank and the bank then deducts from this account. So if we take $500 off this, we have $500 left. Now we take $800 off, and there's not enough money to pay this second person here.

This is an example of a race condition. What's happening is that the time of check of these two, when both of these are accepted, is different than the time of use, where they actually turn it into the bank and the bank processes this and deducts it from the bank account. That's where the issue comes into play.

The same thing in computers

The same thing can happen in computers. Here we have two different programs. Maybe they're accessing the same variable. So the first program, program A, grabs this variable and sees it's 42, and adds three to it, so then it comes up with 45. At the same time, this program is accessing that same data and says, well, I'm adding six to it, and comes up with 48. This program right here checks it in and says, all right, the new number is 45. And then this program checks its number in, so now the new number is 48 — when in actuality it should be an addition of nine there. So it should be 51, but instead it's 48, so it's off.

The way this is supposed to work is that we have thread one here, and it reads the value from whatever value this is and grabs it. So it says, it's zero. It's going to increment this value — maybe it's just incrementing it by one — and then writes it back, and so this value becomes one. Then thread two does the same thing: it checks out this and reads this value, it increments it, and then writes it back. So now we have two. Two should be the answer in this case.

The race condition says, though, that these possibly could happen at the same time. So we're reading the values right there. We increment the values, so now we've got one over here and one over here. And then we write it back — here's one — and then write it back, and here's one again, even though the correct value at this point in time should be two.

This can happen in a lot of different cases when it comes to computing. It could happen in processing, or in storage, or in memory, or in networking.

Time of check to time of use

There are some terms that are associated with this race condition. One of the terms is time of check. Here we have a program and it checks this value; when it checks that value, that's the time of check. Then we have the time it uses it — maybe it uses it for some sort of purpose — that is the time of use. Then there is the lapse between these two, or the duration. That is the time of check to time of use, or TOCTOU. It's just that duration between the time it checks this data and the time it uses the data. The problem that exists is that between these two times, when it checks and when it uses it, something could have changed. Maybe another program has come along and changed the data, and that becomes problematic.

Race conditions aren't just something that can be exploited. What I mean by that is a race condition can exist and cause a problem without any kind of hacker or adversary trying to utilize it. But there are cases where, as hackers or adversaries, we could utilize a race condition to take advantage of it.

A hacking example: stacking a coupon code

Let's take a look at a hacking example of using a race condition. In our example here, let's say we are buying something for $321 and we're applying a coupon code to it, and that coupon code is going to give us $50 off. So in total we're going to only have to pay $271. But maybe there are some promotional limitations, like you can only apply this once — you can't go and find multiple promotions and apply them to get the price down any further. So maybe somewhere within the code we have some sort of checkbox that says yes, you've applied this promotion, and you can only do it once.

It seems like computers process this in an instant, but really it goes through a process. There are lines of code that it has to go through, and as fast as it goes through it — and it goes through it pretty fast — there is still a delay from the point in time that it starts something and the point in time it ends.

In this example right here, it's going through the promotional function. This process, or function, is going to subtract this $50. It starts out seeing if there are any other promotions applied — it checks that value, and no, no other promotions have been applied. Then it checks the validity of that promotion: maybe it checks to see if the code's accurate and has been used somewhere else, or whatever kinds of things that we've set out as being standards for this. Then it subtracts the discount, so we subtract $50, and then it updates the applied promotion and gives a checkbox in here. This is what we're tracking to make sure that not more than one promotion is applied to this.

The real issue happens between this time of check right here, where it checks applied promotions — this is the box that is going to say, oh yes, we've already applied something — so it happens between this number two and this number five, update applied promotion, which is when it actually does the checkbox.

So what happens here is, let's say we have some software that is able to submit this request multiple times, and it does it in rapid succession. Now we are trying to apply this promotion many times. This is when it checks to see whether anything has been applied. Each of these says no, it's not been applied. So each of these goes on to step three, and step three is to check the validity of the offer, and it checks and says it's okay. And then it subtracts the discount. So number four happens, and it happens six times — we're executing this six different times. So that's 6 times 50; we subtract 300, and now we're at $21. Each one of these updates this last box and checks it, so now we can't get any more of these applied to it. But that's okay, because we're already at $21. So we went from $321 down to $21, when we were supposed to be paying $271 for this promotion.

A Windows login example

Let's take a look at one more example of this time of check to time of use problem. We're going to use Windows login as an example. When you log into your machine, you get access — you get some tokens to access certain resources. These tokens are like permissions, or little keys, so that you can go and access certain files and folders, or maybe some sort of instant messaging, your email system perhaps, maybe a database. You get these keys to access certain stuff, but it's not necessarily checked on a continuous basis.

Now let's say this person already logged into the machine, already got access to these keys, and now they get fired. If we allow them to go back to their desk, they can still access a lot of resources, because their access to these resources doesn't necessarily shut down right away. Even if we discontinue their account and remove them from all their groups and reset their password, if they're already logged onto their machine, they could potentially still have access to a lot of these resources. So that time of check to the point in time of time of use — in between there they've got fired, and now they have these credentials, they have this access — that can cause a lot of problems to the company. It's just one thing that we need to be concerned with as security professionals, to make sure that we have processes in place to minimize this risk.

Seeing a race condition in code

I asked ChatGPT to create some code so that we can see a race condition in action, and this is what it came up with. We're going to execute this code and see the race condition in action, but that's a little less exciting than actually looking at the code, so let's look at the code and see what it's doing.

One of the first things that this code does is create an integer called a counter, and we're setting the counter to be zero. Essentially this counter is what's going to increment up. It starts at zero and we're going to count up to a million — that's going to be the goal of this.

The next section here is a function. The function in a program doesn't get called right away; we have the main section here that is going to actually execute, and then it will call the function. We're going to call this function multiple times — 10 times, to be exact.

So what does this function do? This function is going to increment a counter. This line right here says we're going to run this piece of code 100,000 times, so this is going to get run 100,000 times each time we call this function.

What is it going to do? First of all, it's going to set another integer, temp, being the counter. So this is like a time of check: it's going to check the counter and see what the counter is, and we're going to load that up into temp, so now temp is going to equal counter. The next thing we're going to do is put a little pause. This just increases the time from the time of check to the time we check this back in, and so this is going to accentuate this problem. It's going to make it worse, because we put this little pause in here, and now the window of race condition has opened up. Then we're going to set the counter to be the temp plus one, so we're going to increment it up by one. Since this gets reproduced 100,000 times, then it should increment it by 100,000.

Down here, what we're going to do is call that function 10 different times. Since we're calling this function 10 times with 10 different threads, and this function is going to reiterate 100,000 times, counting up 100,000 times, we should get a million — 10 times 100,000 is a million. So the end result that's printed out, if we scroll down to the bottom here, this is when the final value gets printed out: what should be a million, if it were to not have this race condition at all.

So let's go ahead and execute this code and see what happens. It will take a little bit to calculate, because what it's doing is creating those 10 threads and it's calculating up to 100,000 times. Now we see that the total value here is 111,000. That's far less than the million that we should have received. If I run this again, then it's going to come up with a different number, because there's going to be some variance in how it calculates and how we do this. So it's a little bit unpredictable, but it's around that 110,000 mark that we get every time. And yes, 111,755. So there's an example of a race condition that we've created, because we're running those 10 different threads that are accessing that same function and that same integer there. So that can be problematic.

The attack card

Here's the attack card on race conditions. It's when there are multiple processes manipulating resources, or changing resources, or accessing these resources at the same time, and it causes a problem because multiple things are trying to do things to that at the same time. Here's an example of it where we're reading values: two threads are reading values and then they're increasing values, and now when they go to write, it doesn't write out the correct amount, because they were simultaneously processing this. Some terms that are associated with this are the time of check, the time of use, and that duration, the time of check to time of use.

About TechKnowSurge

TechKnowSurge builds IT and cybersecurity professionals through hands-on, concept-first training built around real understanding — not memorization. Free interactive tools, structured programs, and 25+ years of real-world experience, all in one place.

Explore free tools and programs →