What Is a Race Condition Vulnerability?


A race condition occurs when two operations must occur in a specific order, but they may run in the opposite order.


For example, in a multithreaded application, two separate threads might access a common variable. As a result, if one thread changes the value of the variable, the other may still use the older version, ignoring the newest value. This will cause undesirable results.

To better understand this model, it would be good to examine the process switching process of the processor closely.


How a Processor Switches Processes

Modern operating systems can run more than one process simultaneously, called multitasking. When you look at this process in terms of the CPU’s execution cycle, you may find that multitasking doesn’t really exist.

Instead, processors are constantly switching between processes to run them simultaneously or at least act as if they are doing so. The CPU may interrupt a process before it has completed, and resume a different process. The operating system controls the management of these processes.

For example, the Round Robin algorithm, one of the simplest switching algorithms, works as follows:

A diagram showing 3 queued, sleeping processes an 1 active process that the CPU is currently running

Generally, this algorithm allows each process to run for very small chunks of time, as the operating system determines. For example, this could be a period of two microseconds.

The CPU takes each process in turn and executes commands that will run for two microseconds. It then continues to the next process, regardless of whether the current one has finished or not. Thus, from the point of view of an end-user, more than one process seems to be running simultaneously. However, when you look behind the scenes, the CPU is still doing things in order.

By the way, as the diagram above shows, the Round Robin algorithm lacks any optimization or processing priority notions. As a result, it is a rather rudimentary method that is rarely used in real systems.

Now, to understand all this better, imagine that two threads are running. If the threads access a common variable, a race condition may arise.

An Example Web Application and Race Condition

Check out the simple Flask app below to reflect on a concrete example of everything you’ve read so far. The purpose of this application is to manage money transactions that will take place on the web. Save the following in a file named money.py:

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)

class Account(db.Model):
id = db.Column(db.Integer, primary_key = True)
amount = db.Column(db.String(80), unique = True)

def __init__(self, count):
self.amount = amount

def __repr__(self):
return '' % self.amount

@app.route("/")
def hi():
account = Account.query.get(1)
return "Total Money = ".format(account.amount)

@app.route("/send/")
def send(amount):
account = Account.query.get(1)

if int(account.amount) < amount:
return "Insufficient balance. Reset money with /reset!)"

account.amount = int(account.amount) - amount
db.session.commit()
return "Amount sent = ".format(amount)

@app.route("/reset")
def reset():
account = Account.query.get(1)
account.amount = 5000
db.session.commit()
return "Money reset."

if __name__ == "__main__":
app.secret_key = 'heLLoTHisIsSeCReTKey!'
app.run()

To run this code, you’ll need to create a record in the account table and continue the transactions over this record. As you can see in the code, this is a test environment, so it makes transactions against the first record in the table.

from money import db
db.create_all()
from money import Account
account = Account(5000)
db.session.add(account)
db.session.commit()

You have now created an account with a balance of $5,000. Finally, run the above source code using the following command, provided you have the Flask and Flask-SQLAlchemy packages installed:

python money.py

So you have the Flask web application that does a simple extraction process. This application can perform the following operations with GET request links. Since Flask runs on the 5000 port by default, the address you access it on is 127.0.0.1:5000/. The app provides the following endpoints:

  • 127.0.0.1:5000/ displays the current balance.
  • 127.0.0.1:5000/send/amount subtracts amount from the account.
  • 127.0.0.1:5000/reset resets the account to $5,000.

Now, at this stage, you can examine how the race condition vulnerability occurs.

Probability of a Race Condition Vulnerability

The above web application contains a possible race condition vulnerability.

Imagine you have $5,000 to start with and create two different HTTP requests that will send $1. For this, you can send two different HTTP requests to the link 127.0.0.1:5000/send/1. Assume that, as soon as the web server processes the first request, the CPU stops this process and processes the second request. For example, the first process may halt after running the following line of code:

account.amount = int(account.amount) - amount

This code has calculated a new total but hasn’t yet saved the record in the database. When the second request begins, it will perform the same calculation, subtracting $1 from the value in the database—$5,000—and storing the result. When the first process resumes, it will store its own value—$4,999—which will not reflect the most recent account balance.

So, two requests have completed, and each should have subtracted $1 from the account balance, resulting in a new balance of $4,998. But, depending on the order in which the web server processes them, the final account balance can be $4,999.

Imagine that you send 128 requests to make a $1 transfer to the target system in a time frame of five seconds. As a result of this transaction, the expected statement of account will be $5,000 – $128 = $4,875. However, due to the race condition, the final balance might vary as anything between $4,875 and $4,999.

Programmers Are One of the Most Important Components of Security

In a software project, as a programmer, you have quite a few responsibilities. The example above was for a simple money transfer application. Imagine working on a software project that manages a bank account or the backend of a large e-commerce site.

You must be familiar with such vulnerabilities so that the program you have written to protect them is free from vulnerabilities. This requires a strong responsibility.

A race condition vulnerability is only one of them. No matter what technology you use, you need to watch out for vulnerabilities in the code you write. One of the most important skills you can acquire as a programmer is familiarity with software security.


Source link

Leave a Reply

Your email address will not be published. Required fields are marked *