HTB Cyber Apocalypse 2023 - (Web) Orbital
‘Orbital’ was one of the challenges in the ‘Web’ category at HTB’s Cyber Apocalypse 2023.
Its difficulty was ‘Easy’ and it involved exploiting SQL Injection and a path traversal vulnerability.
Challenge description
For this challenge, we got access to a Docker container running a web app and the source files of that webapp.
This is how the web app looks like:
And these are the files that we have:
Code analysis
First, let’s take a look at the code for this app.
The app is built using python & Flask.
The first interesting thing that I seen was in the database.py file.
Identifying the first vulnerability SQL Injection
The query appends the username without any kind of filtering or sanitization; this makes the query vulnerable to SQL Injection.
The first result of the query is taken and the user-supplied password is checked against the retrieved one (MD5 hashes match).
This will make exploiting this a bit trickier but it shouldn’t be that hard.
Logging extra information
First, let’s make the application print some extra information so that we can build our malicious query easier.
We’ll change the login function like this:
In order to make the output of those print statements visible, we also have to modify supervisord.conf
For that, we change this line
Into this
This is a quick solution found here
Next, I build and run the container by running the ./build-docker.sh script.
And try logging in with test/test
The SQL Injection attack
Now let’s build our SQL Injection attack.
First, let’s assume we don’t know the account so we append “or 1=1” to the query.
Note: Since we see how the container is built, we know that the account is admin. This is probably the same in the live version of the container.
However, ignoring that information will lead to building a more robust attack vector.
First, we’ll use this simple query
Then, we want to control the first value that’s returned.
For that, we add an UNION to the query and append our own data.
The real data from the database will still come first, but we can add ‘ORDER BY username DESC’ to sort the returned data and make our injected result be the first one.
Success!
Now we’ll need to replace the second injected “test” with the MD5 value of “test” (since that’s what I used as my password here).
And we’re in.
The flag isn’t right on this page, so we have to keep looking.
The path traversal attack
In the routes.py file we see the following code
communicationName is appended to the path without any kind of sanitization.
This makes that function vulnerable to path traversal.
Let’s try making a crafted request using BurpSuite to read the content of /etc/passwd.
According to the Dockerfile, the working directory is /app. So the path to /etc/passwd should be ../etc/passwd (Full path:/app/../etc/passwd)
It works without any issue.
Getting the flag
Now let’s use the same technique to get the flag.
First, we have to find out where we can find the flag in the container.
In the dockerfile, we can see the following lines.
So the flag is copied at /signal_sleuth_firmware
By requesting ‘../signal_sleuth_firmware’ we got the flag (the fake one, for now)
Now, let’s use the the same two attacks on the live app.
The two attacks work flawlessly for the live app too.
And we get the flag: HTB{T1m3_b4$3d_$ql1_4r3_fun!!!}
Wait … time based?