Introduction

I started this blog a few months ago with this post about a race condition I found. I refuse to contribute to the internet’s blog graveyard, so here we are.

Anyway, this led to a similar blog post, this time on Blip’s blog and eventually a conference talk at ØxOPOSɆC, which at the time of writing has not happened yet. Our team’s manager had the cool idea of building a simple demo web application I could use at the talk (thanks João! :D).

I took the web app and turned it into a small challenge based on my initial finding. Then I decided to go a step further and build an entire virtual machine around it.

The challenge

I’ll try not to spoil it too much (yet, walkthrough below!), but it is a pretty simple system from an architectural point of view. There’s a web application and an SSH server running. After hacking into the vroomnerable user, you get to escalate your privileges to root by exploiting another race condition - this second part is where it can get a bit tricky.

I confess the machine is a bit CTF-y, it’s not supposed to simulate a real situation or environment. They are just challenges presented in a straight forward way, with no rabbit holes and no need to read between the lines.

Here is the link to download the .ova file. I promise it’s not malware :)

It’s the first time I’m building a vulnerable VM, so I’m not expecting it to be perfect. But come on, give it a go! Write a walkthrough or give me your feedback. Don’t forget to tag me!

Walkthrough

Setting up

If you’re using VirtualBox, importing the machine should be pretty straight forward after downloading the .ova file. Simple press File –> Import appliance and select the downloaded file. It should come pre-configured (I recommend setting your machine and the vulnerable machine to “NAT network”, that never results in any network problems for me).

image

You boot both machines (your Kali and vroomnerable) and if everything goes according to plan, they should be on the same network. Time to find out the victim’s IP address. The attacker’s IP is 10.0.2.4/24, so vroomnerable should be somewhere in the 10.0.2.0/24 range.

image

To be sure which of the results is the target machine, you can compare the MAC addresses found to the VM’s MAC address in the network settings.

image

Enumeration

This is a pretty basic nmap scan, but we don’t need more than this. There’s an SSH server and a web app running on port 8000.

image

image

This seems like an impossible challenge at first, since you cannot set a higher active value, only lower ones. After performing all your enumeration steps, you should find a hint. Yes, it’s a shamless plug to my blog (again 🙄).

image

User flag

The exploit for this first challenge is exactly what’s in the initial blog post, so I won’t go deep into that. This time, it only took me 6 tries - starting at the values 49/150, down to 44/150.

image

These credentials work for SSH, so here is the user flag.

image

The source code is in the home folder. This is artificially vulnerable due to the log_operation() function. The larger the value in the for loop, the longer the thread will wait before setting a value. wait_set() sets a PENDING value, while set() sets an ACTIVE value. When sending a lower value, the else branch is taken and the artificial delay starts. That’s the race condition window and when the second value (150) should change the y variable before the first thread completes.

image

image

Root flag

This is where it can get tricky. At first, this is the only thing we have to escalate our privileges.

image

Some files are being deleted and some files are being executed. The numbers are timestamps. After some enumeration, you’ll find an empty folder at /opt/race/ - this is where the deleting and executing is happening.

image

There’s another race going on here - deleting vs executing. Let’s start by building an exploit, throwing it inside /opt/race and running sudo /root/leclerc. Hopefully the exploit (setting the SUID bit for /bin/bash) wins the race and is executed.

image

I guess we came in second. The goal here is to extend the deleting window just enough so that all the executions can happen inside it. Please allow my artistic (aka paint) skills to demonstrate that.

image

However, this can only happen if deleting files takes longer than executing files. Let’s put on our scientific goggles and do an experiment. Here’s what we’re going to do:

1) Copy the exploit 10 times to /opt/race/;

2) Run sudo /root/leclerc;

3) Collect the length of the execution and deletion windows;

4) Repeat steps 1-3 with gradually more files.

In practice…

image

Here are the results of the experiment:

Files Started deleting files Finished deleting files Deletion Δ Started Executing files Finished executing files Execution Δ
1 1710590071065 1710590071066 1 1710590071065 1710590071067 2
10 1710591111140 1710591111142 2 1710591111140 1710591111148 8
100 1710591215249 1710591215266 17 1710591215249 1710591215335 86
1’000 1710591235234 1710591235399 165 1710591235234 1710591235851 617
10’000 1710591263709 1710591265325 1’616 1710591263709 1710591265644 1’935
30’000 1710592360610 1710592365380 4’770 1710592360610 1710592365814 5’204
60’000 1710592621064 1710592631090 10’025 1710592621065 1710592631257 10’192
90’000 1710592938159 1710592953692 15’533 1710592938159 1710592953983 15’824
200’000 1710594998488 1710595035416 36’928 1710594998488 1710595035422 36’934
300’000 1710595954811 1710596010562 55’751 1710595954811 1710596010571 55’760

My guess would be that for a very large number of files, the execution delta will gradually be closer and closer to the deletion delta, and eventually lower than it. I didn’t try with more than 300k files as the copy takes a long time, but that was enough so that the exploit worked.

image

I left the source code in the /root/ folder, inside the file called lecler.c

image

The main function simply starts two threads almost simultaneously - one deleting files and another one executing files, both in the /opt/race/ directory.

image

The vulnerability is in the delete_files() function. I added a very short sleep, which after a bunch of loops should accumulate and create a considerable delay. 9 microseconds was the value that would delay the program just enough to be able to exploit it with at least a couple of hundred thousand files.

image

My paint masterpiece above isn’t 100% in line with the experiment results. But I still managed to execute the exploit.

I need answers

I almost ended the post there, but I decided to give it another go since I’m not very confident on these results. Let’s clear things up.

I booted a fresh vroomnerable and rooted it again. I increased the usleep(9) to usleep(10000) in the source code, compiled it again and repeated the experiment. The deletion window should increase a lot along with the number of files. The exploit should be successful with less files too.

image

Here are the results with a larger sleep value.

Files Started deleting files Finished deleting files Deletion Δ Started Executing files Finished executing files Execution Δ
10 1710610616066 1710610616297 231 1710610616066 1710610616087 21
100 1710610721735 1710610722818 1’083 1710610721735 1710610721913 178
1’000 1710610769770 1710610781409 11’639 1710610769770 1710610771492 1’722

The exploit worked with just 10 files. And the deletion delta increases a lot more than the execution delta, this is exactly what I was trying to achieve initially.

So everything is clear now, the sleep function had a very small value. I was just lucky enough that somewhen (what a cool word) during the execution, thread 2 made the overtake and executed one of the files that hadn’t been deleted yet. Or maybe 9 microseconds is such a small value that the timestamps are not accurate enough.

Who knows, the windows were too small and the results too inconsistent to draw any accurate conclusions… Actually, that’s what made it extra fun.

image

Updated: