This is why you should ALWAYS check for Race Conditions (even in JavaScript) — an Infinite Money Glitch

A walkthrough of Diogenes’ Rage (Hack the Box Web Challenge)

Roberto
InfoSec Write-ups

--

Update 07/26/2022: A huge $5000 bounty was given based on a race condition. Read on it here.

What is a Race Condition?

A race condition occurs when two threads or processes are trying to access the same resource at the same time. Exactly what happens can depend on the the machine, the CPU, the compiler, etc., and the behavior is not always deterministic. Essentially, it all depends on the timing of context switches as the order of operations changes. For example, if a user starts with a balance of $500, and then thread A handles a deposit of $1000 and thread B handles a withdraw $500, we should expect a total of $1000. However, if the order of operations is such that A and B both read that there is $500, A updates 500 + 1000 , then B updates 500 — 500 , the user is left with $0! Conversely, you could expect the user to be left with $1500 if A went second!

Race conditions can cause a huge headache in multi-threaded programming languages, but they are usually an afterthought in the single-threaded JavaScript. However, it is essential to note that this can occur in JS, and the consequences can be equally devastating depending on where the race condition takes place. Note: A great explanation of a simple JS race condition can be found on this Stack Overflow post. We will explore the Hack The Box (HTB) Web Challenge “Diogenes’ Rage,” which demonstrates how a hacker code potentially create money out of thin air. For a real-life example (and kind of ironically), Jack Cable found a race condition letting attackers continue to get payments in increments of $100 in one of HackerOne’s feature. The full report is here.

AI-generated illustration from craiyon.com “hackers money illustration”

Diogenes’ Rage: Getting Started

Go to https://app.hackthebox.com/challenges . Under “Active Challenges,” click on “Web”. Look for “Diogenes’ Rage” and then start the instance (if you don’t see it, it might be under “Retired Challenges” at the time of reading). When you start the instance, you should be able to see a host:port (i.e something like 157.245.46.136:30055). Copy the host into a new browser tab/window. Good to go!

On this challenge, you will also need to download the necessary files as well as Docker downloaded to run a local version of the app. To run the app locally (on http://localhost:1337), make sure to have Docker running, open a terminal/command prompt and run the “build-docker.sh” file (“sh build-docker.sh” on mac). Good to go!

Diogenes’ Rage
Starting the “Diogenes’ Rage” challenge

Reconnaissance

Looking at the main page, there appears to be a vending machine and a coupon for $1. You can drag the bill worth $1 into the machine and buy anything except C8.

Looking through the files, there is a DB file, which is not vulnerable to SQL injections, there is a middleware file, which helps create an Express session, and there is a routes file that defines the HTTP methods for the app API. The two important routes are the /api/purchase POST and the /api/coupons/apply POST.

“/api/purchase” POST method
"/api/purchase" POST method

In the /api/purchase POST method, if there is no session, which means no user cookie is used, then the app will initialize a session and register you a user with 0 balance. The other thing of note is that if the product you buy is C8, then the flag will be added to the response. So, somehow we have to get more than $13.37 into the account to buy C8.

“/api/coupons/apply” POST method
“/api/coupons/apply” POST method

In the /api/coupons/apply POST method, the same thing occurs with the session check and initialization. If you apply a coupon, it adds the value, but you’re not supposed to be able to redeem a coupon more than once. However, we can see that the order of operations here is 1) add to the balance through db.addBalance and then 2) set that the user has used the coupon in db.setCoupon.

So, if you issue a bunch of /api/coupons/apply POST requests at the same time for a user, more than one coupon may be applied if more than one request makes it past the if (coupon_code) check. Let’s try it out!

The Exploit

Multi-Processing Script exploit.py

In our script (full link to copy here), we first must issue a request to purchase some item in order to create a user session (aka cookie) we can keep using. Once we have the user session, we are using the Process class from the multiprocessing module to create multiple processes that each spam the /api/coupons/apply POST using the same coupon. Lastly, we issue out a request to purchase C8 and try to get the flag. Note: the first newRes POST request is just to buy A1, which we’ll be able to buy no matter what. This is just to show how much money each attempt has succeeded in abusing the same coupon. Since I don’t know how many attempts this will take to trigger the race condition, the execution runs in an infinite while loop until the flag has been printed out. Run python3 exploit.py from the terminal to execute. Note: you need to do cmd/ctrl+c to stop the process.

Results from executing exploit.py

And bingo! We have the flag. Notice because of race condition, only one of the executions managed to get over $14 for C8, but many of the other executions got more than the suppose $1. If this were a banking system, then the actions of many users could aggregate significantly. Again, for a real-life example (and kind of ironically), see Jack Cable’s exploit of a HackerOne feature. The full report is here.

Thanks for reading through and please leave any constructive feedback, suggestions, or questions below! If you enjoyed, please consider following me on Medium. Contact me at roberto.cyberkid@gmail.com, follow me on twitter, and connect with me on LinkedIn!

From Infosec Writeups: A lot is coming up in the Infosec every day that it’s hard to keep up with. Join our weekly newsletter to get all the latest Infosec trends in the form of 5 articles, 4 Threads, 3 videos, 2 Github Repos and tools, and 1 job alert for FREE! https://weekly.infosecwriteups.com/

--

--

Stanford alum, Software Engineer with a passion for CyberSec, Biotech, and Sustainability. Work with me at https://www.tidallabs.io/.