Intro
So I wasn’t able to get Cyberforce writeups done this week, but I was able to participate in this year’s HackTheBox University CTF. Since the rest of my team was busy with real-life stuff, I was on my own, but managed to get a spot in the top 100, which I’ll take as a win. There were so many well designed challenges, and I’ll be doing writeups on as many as I can (or at least just my favorites).
GoodGames was an easy rated machine because it tested your ability to apply basic vulnerabilities and use situational awareness to find the next step, as opposed to figuring out some complex chain of commands. I’ll begin by walking the application, and finding SQL injection in the login page. From there, I’ll extract some password hashes to get the admin’s password and move into the admin panel, which is vulnerable to SSTI. We then find ourselves in a container, where one of the home directories on the original box is mounted. We can create a program to give us elevated privileges, then breakout of the container with ssh, and then escalate to root using our program.
Recon
nmap
Do I even need to say it at this point? Yes? Ok, nmap.
It appears that only port 80 is open. This nmap scan actually tells us a good number of things even before we look at the box.
- The hostname is
goodgames.htb
, so I’ll add that to my/etc/hosts
file in case there’s additional vhosts - The website is built with Python. This eliminates a number of things to think about when going through the website (e.g. we can’t use the pentestmonkey php reverse shell, LFI is not likely)
- It’s highly likely that we’re working with a *nix system because only 80 is open. Doing a
ping
will confirm this based on the ttl.
At this time, I also tried enumerating for additional virtual hosts/subdomains and couldn’t find any given my usual wordlist.
Walking the Website
When we first open the website, we’re greeted with the following:
I guess the name makes sense. Wappalyzer also confirms some of our observations from earlier.
I’ll run feroxbuster
, but I don’t find anything of interest.
Many of the pages I found were pretty much useless. The /coming-soon
and /blog
pages had very little going on and were rabbit holes if you were extra stubborn. This also led me to looking into if this is a previously created template (because of how intrictate it is), which it is.
This doesn’t help that much, but I just wanted to point that out.
I’ll make an account to see what else is going on, and I’ll call it “ippsec” because maybe his energy will make me solve this box way faster. The profile page looks like this:
Again, there’s really not much that immediately sticks out.
Shell as root in Container
SQL Injection
I was stuck here for some time until I realized I needed to go around and actually start trying to break things so I could see weird behavior. I noticed that when you login, it takes you to a welcome page for an awkward amount of time, so I’ll send my login request to Burp and see if I can do some basic SQL stuff.
It worked! From here, there are two approaches you could take: (1) Do the whole SQL injection by hand, trying to find the table name and get the right output and everything, or (2) just use sqlmap
.
Normally, I refrain from using sqlmap
because it can blow up a system if not used responsibly, but because this was a time crunch, I have to make use of my resources as best as possible. The easiest way to use it here is to take the request, put it in a text file, and run the following command:
The command can be a little finnicky at times, so playing around with the flags might be necessary (read as: I didn’t document what sqlmap command actually worked). But, we’re given a txt file of potential hashes, which are the actual hashes.
We can then crack these hashes in Crackstation to find the passwords “superadministrator” and “password1” respectively. The second password is what I used to register “ippsec”, so “superadministrator” must be the admin password. I can use this to sign in as admin.
SSTI in Administrator Panel
Unlike before, there’s a small gear at the top right. Clicking on it brings us here.
I’ll add this new subdomain to my /etc/hosts
file and try again.
We can login with the credentials we found earlier (admin:superadministrator) to get access to the admin panel.
This part of the typical HTB challenge is always difficult because a lot of the functions here are absolutely useless. However, if there’s one thing I know about CTF creators and Flask, they love putting in some kind of SSTI.
Server-side Template Injection (SSTI for short), is when a user is able to modify some parameter that can control the template engine that is running on the server. This can allow for XSS to RCE. The most common examples I’ve seen are in Jinja2 from Python and Pugify in Nodejs. There’s a nice table to test certain payloads to enumerate the engine, but since we know it’s Flask, we can reasonably assume it’s Jinja, and can work from there.
If we go to the “My Profile” section, and test the “Full Name” field for SSTI, we get success.
Normally, the process of exploiting SSTI to get code execution involves enumerating for a module that will give you that ability, and then inserting your payload. Luckily, the field we’re exploiting allows us to input as much text as we want, so we can use the following payload that I picked up from 0xdf’s blog a while ago.
The engine I use to render the markdown for the blog didn’t like the syntax, but the payload is on 0xdf’s Doctor writeup, and I think PayloadsAllTheThings as wellThis will iterate over every possible module and check if we can get execution. If we can, we run the reverse shell payload. If we run this against the target, we get a shell back on our listener.
Shell as augustus
user.txt
I’ll start by stabilizing my shell using the python trick.
It appears that we are root, but when we go to the /root
directory, there is no flag.
In the home directory, we find the augustus
user, and the user flag.
But, if I read /etc/passwd
, there is no augustus
user, or frankly a user with the id of 1000.
Enumeration
I was stuck here for a long time. It was pretty easy to figure out that I was in a container given the hostname and the lack of processes when typing ps aux
. I went through my notes on typical escape techniques, like shared namespaces, exposed sockets, etc, but nothing really applied to what was here. Linpeas didn’t return much either. I then went to look for container enumeration scripts and found some, namely, deepce.
I’ll transfer deepce to the container and see what I get back.
From here, we conclude a couple of things. The reason that /home/augustus directory is there is because it’s from the actual box, mounted inside the container. Additionally, there’s nothing we can really exploit docker-wise. However, if we think about this in context, why would a user have their home directory mounted in the container?
Breakout
The 172.19.0.0/24 subnet is used to make a virtual network for all of the containers on the machine. This means the 172.19.0.1 address refers to the actual box itself, because it’s like the gateway. What if we can access the original box from the container via something like ssh?
Shell as root
root.txt
I noticed that the files that I originally transferred in augustus’ directory were all completely owned by root.
If I put something like bash
in the container, I could easily use that as augustus to give myself an elevated shell. Rather than struggle with kernel versions and all of that nonsense, I remembered I saw that gcc
was in the container, so I compiled my own program.
Then, as augustus, I can execute the program, and grab the flag.