Room link
https://tryhackme.com/room/thegreatescape
Summary
This room was a competition with some great prizes on Tryhackme. It has an interesting SSRF for foothold, then we need to enumerate the system through command injection in a hidden API. Once done with that, we can open an exposed docker daemon via port knocking and exploit that to escape the containerized enviroment.
Exploring the target
First things first, after deploying the box and getting an IP, I started with an nmap scan. In fact, I usually run 2 nmap scans, one to quickly grab the open ports, then one to enumerate the running services in more details, so I’ve ran the following two commands:
|
|
To grab the open ports, then:
|
|
To enumerate service headers and versions.
This gave us the following result:
Nmap result
The ssh looked quite interesting, since nmap had some issues deciding if it’s really ssh or not, so I’ve decided to poke it a bit. First I connected to it with netcat and telnet to see if I get something back, but nothing interesting happened.
My next idea was to connect to the port via an actual ssh client in verbose mode, and see if i get back anything.
The command to do this is very simple, just do the following:
|
|
Since root is an existing user most of the time, we can try connecting with it, -v tells ssh to be verbose about what’s happening in the background.
The server seemed to send back random looking strings with equal time intervals between them. It immediately clicked, since I’ve personally used this tool before, when hosting our first CTF that this HAS to be endlessh, which is kinda like an ssh honeypot that “captures” ssh bruteforcing bots. Endlessh works by sending back infinitely long ssh banners, which happens before the authentication, so the client trying to connect to the server never gets to actually authenticate. The room creator probably used this to troll less experienced hackers, who just bindly fire away tools at every service they see.
After realising that the ssh is a troll, I immediately decided to move on to enumerating the service on port 80.
I was greeted by a web application, and the main page said Photo classroom
. Looks like we have some kind of an online course app, maybe with an LMS behind it. A quick look at Wappalyzer
told me that the app is most likely running with a Node.js
backend, and Vue.js
and Nuxt.js
frontend. Looks like we have an app made with these modern fancy JS frameworks, I shall have fun breaking it :).
After clicking all the visible links, it looked like we need to log in to be able to do anything, but we didn’t know any username password combos, and the sign up function was disabled (tried to bypass it, didn’t really work).
Foothold
First of all, if you are looking for the web flag, there’s more on that at the end of the writeup, since I’ve only found it after getting full root access and enumerating.
After looking at the obvious stuff, i usually check robots.txt
, so i did exactly that (can literally see it from the nmap scan too).
This gave me the following data:
|
|
Okay, this looks interesting! So I started looking at the directories found here, after checking out /api, it looked like it isn’t that interestign for now. I’ve tried to fuzz the /api endoint for a bit, but do NOT fuzz this box. Now I know the rate limiting on the box is intentional because found some config on it later, but the box actually crashes completely if you send requests too fast (nice lesson on having to be careful when poking at live targets). Not sure if the crashing was intentional or not, but I had to restart the box a few times in the process, so if you are doing this box, I’d recommend not fuzzing it (limiting the requests to 2 sec intervals in burp pro intruder worked, but was extremely slow and painful, and couldn’t find anything anyways).
So no fuzzing, let’s see the other endpoint. After checking out /exif-util, it looks like it’s a tool to get exif data out of files. Nice, this is the only actual functionality aviable on the site for us. We can upload files, or provide an URL for a file. I’ve tried uploading Node.js shells with changing the extension and the magic bytes and stuff, but none of that worked. I’ve also uploaded a file via URL from my attacker box, and saw the request actually hit my box, so I tried all the SSRF payloads I know (I have a decent methodology on it in my private notes). None of the SSRF bypasses or tricks I know worked. I also tried to do some command injection, since the app was running some kind of a java app in the background (got this from poking the app a bit), and it downloaded the files when providing an URL using curl. None of these worked, there was proper filtering. After getting stuck here for a while, I’ve moved on.
Well, it was time to check out the last entry from robots, which is a simple regex for all files ending in .bak.txt
. Honestly, first I thought there’s gonna be some password backup or something on the server, so I started fuzzing it with the previously mentioned 2 sec delay between requests, but after waiting quite a while, I got nothing. Well, as a last resort, I’ve tried combining the stuff that was in front of me. I’ve tried /exif-util.bak.txt
and got a promising-looking result!
The request returned this.
It’s easy to spot the relevant stuff for us, it’s in row 44:
|
|
Well that to me looks like a perfect target for SSRF in the exif-util URL parameter.
First I’ve sent a request to http://api-dev-backup:8080
, and even though it returned an error, after the error message I’ve got the following:
|
|
Looks like we have something interesting! Based on the name api-dev-backup
it looks like a developer version of the publicly accessible api. The public api sends requests when providing an URL like this:
|
|
Since we got the Nothing to see here
message by sending a request to the root of the internal server, it looks like we don’t have a /api there, and the api is actually accessible at the root. Using this knowledge we can make the following request:
|
|
This broke the app and once again confirmed that curl is being used. Often times, developer applications don’t have proper security implemented, this is seen in real life scenarios many times too. I’ve tried the stuff I’ve tried on the public api, and almost immediately got a hit with a really simple OS command injection payload:
|
|
This gave me an error, but under the error I had directory listing information:
|
|
I’ve tried to get a reverse shell to be able to enumerate easily, but it looked like the internal developer api had no outbound communication allowed. Not sure if I could have made it work, but at that time I’ve decided to just enumerate the system via this web based command injection.
User
At this point I still haven’t gotten the web flag and had no idea where to look for it. There was a hint about it: Some well known files may offer some help
, I just assumed the well known file is robots.txt, so wasn’t sure what to do with it, so I just continued with the command injection.
After running id
, it looked like we had root! This is great, but from the room challenge names I knew it’s most likely just root in a docker container, and not “real” root. Anyways, I’ve listed root’s directory with the following command:
|
|
And I’ve got:
|
|
Let’s see that dev-note.txt :)
|
|
Okay, we got a username password combination, since I knew that ssh is not a real ssh server, I’ve tried loggin in with it on the webpage, but nothing (spoiler: this username and password is completely useless). Okay fine, let’s see the hidden .git directory, and let’s see the commit history:
|
|
Okay now this is interesting:
|
|
Let’s see that a3d30a7d0510dc6565ff9316e3fb84434916dee8
commit!
|
|
Boom, easy flag:
|
|
Not only did we get the 2nd flag, we also see that after performing some port knocking, we can open a docker daemon for remote administration, which should make escaping the docker enviroment possible.
I’ve used this tool to do the port knocking, since it’s simple and fast. Command is:
|
|
It’s known that the docker daemon uses port 2375 by default, and after performing the knocking and running a single port nmap scan, I could confirm that the port appeared open!
Root
To be honest there wasn’t much work to get “real” root after this. If someone knows docker escaping methods (great room for it on THM is the docker rodeo), then the path to real root is obvious. If there’s an open docker daemon, we can run remote docker commands on it with the following syntax:
|
|
I’ve ran
|
|
to see if we have an easy win, and indeed we do! There’s alpine listed among the aviable images, so we don’t even need to put it on the box, we can just use a trick that mounts the host system to get root on the “real” file system:
|
|
And that is all to it! This drops us into an sh
shell on the host system, and we can grab the final flag from /root/flag.txt
:
|
|
Late web flag :/
So I just couldn’t find the web flag the “proper way”. Wappalyzer showed me that nginx is the runnign webserver, so I knew where to look for server files:
It was in the frontend docker at /usr/share/nginx/html/
.
I attached to the container with:
|
|
I could see that there is a .well-known
directory, with security.txt
inside. This is a file that is used by companies to tell white hat hackers how to properly report a bug they might have found on the company’s site. Should have guessed it from the hint, but I’ve found it still, just with another method and after rooting. This didn’t give an immediate flag, the file contained the following text:
|
|
This can be very easily done:
|
|
And this gave:
|
|
The room is officially completed!
Credits
Great box, foothold was a little bit guessy for me, but everythign after was really interesting and real-life-like. Room made by hydragyrum, and the giveaway provided by Fawaz, I’d like to thank both of them, especially Fawaz for the giveaway!