Entersoft Security Blog

Race Condition Vulnerabilities in Web Applications

Posted by Entersoft Team on Sep 15, 2017 8:14:29 PM

Race conditions in software arise when two concurrent threads of execution access a shared resource in a way that unintentionally produces different results; depending on the time at which the code is executed. For example, a multi-threaded program may spawn 2 threads that have access to the same location in memory.

Let’s start with an example – Imagine yourself in a bus, where all the seats are occupied and several people are standing. Now, the destination of one of the passengers seated has arrived. He gets down the bus leaving his seat vacant. You see that vacant seat and proceed towards it. But you are unaware of the fact that there is one more passenger in the bus who is approaching the same seat but, from the opposite side (who is also unaware of you). As you near that seat, you see that the other passenger reached before you and has already occupied the seat.

Consider this real life scenario. What just happened? Well, you just practically experienced a concept which in programming is termed as “The Race Condition Vulnerability”.

How does this simple condition become a critical flaw then?

Suppose if you are trying to book a ticket in a train where only 1 seat is empty. Simultaneously another passenger is also attempting to book a ticket for the same seat. What happens in this situation?

Proof of Concept

To demonstrate the attack, I implemented the withdraw function using a 1-entry MySQL table to hold the balance, and setup a PHP script that accepts a URL of the form poc.php?wd=10 to withdraw the value specified in the URL (in this case, 10). I then wrote a python script to make 128 simultaneous requests to the URL:

import os

os.fork() #2

os.fork() #4

os.fork() #8

os.fork() #16

os.fork() #32

os.fork() #64

os.fork() #128

print os.popen('php -r ' + \

'"echo file_get_contents(\'http://defuse.ca/poc.php?wd=10\');"').read()

Executing this script will make 128 requests to withdraw $10. If all 128 requests work properly, and the balance starts out at $10,000, we expect the balance to be $10,000 - 128 * $10 = $8720 after the script finishes executing. But due to the race condition, there's actually more money left in the account than there should be.

Table 1. Actual result of running the Python script.

Table

As you can see, if this were an actual online financial service (e.g. Paypal), I'd be able to make hundreds of dollars in only a few seconds. Just to be thorough, I can verify that $1280 was actually withdrawn by looking at the output of the Python script:

$ grep "You have withdrawn: 10" test2 | wc -l

128

These tests were done using the VPS that hosts this website (Debian 6, running Apache 2.2.16) to host the withdraw script and my Debian 6 PC at home to run the Python script. I've also confirmed that the attack works just as well when the withdraw script is hosted on the same PC that executes the Python script. It works when they're separated by a 100mbps Ethernet LAN, too.

What's going on here?

This attack is possible because web servers like Apache process queries asynchronously. That means, if two requests come in at nearly the same time, they'll get executed at the same time or their execution will be interleaved by the operating system's CPU time sharing system. The result is that a few of the requests end up being processed like this:

RCV

After the two requests are processed, the balance should be $9980, but since the second request is processed while the first is still being processed, we end up with a balance of $9990. Both withdraws work (imagine that the echo statement was replaced by code to increment another account's balance), so $20 is withdrawn but only $10 is deducted from the balance.

Recommendations:

In general terms, a lock system should be implemented. As in, when a particular thread is working on a resource, the resource should not be given access to any other thread unless it’s work is complete, especially if the thread is meant to overwrite new values. This is termed as “Sequencing”. As a contribution from our side, the program must be checked thoroughly to notice any part of the code that fails if any code is getting executed in between them.

While implementing locking, the other problems of locking, like dead locks, also must be taken care of by ensuring that the threads do not have to wait forever for the corresponding processes to complete.

Race Conditions Exceptions are unchecked Exceptions which occur only in the runtime, and cannot be detected by the compiler, unlike the other kind of errors which the compiler detects as soon as it is typed. In order to prevent them, using Synchronised block around the shared resource is recommended to prevent multiple access at a time.

There are different tools available for detecting race conditions but they depend on upon the OS.

Mutex means Mutual Exclusion. It is a Computer Program that allows multiple threads to access the same resources, but not simultaneously.

Apart from this, it is also a good practice to minimise the number of shared resources if they are meant to change values.

Research by Hussain Pattan

SOURCE

Topics: Application Security, Application Security Guidelines, cyber security