Umbrella
Breach Umbrella Corp's time-tracking server by exploiting misconfigurations around containerisation. - by brunofight
The following post by 0xb0b is licensed under CC BY 4.0
Recon
We start with a Nmap scan and discover four open ports. On port 22 SSH, on port 3306 we have access to a MySQL database, on port 5000 a Docker registry connection, and on port 8080 an HTTP server running Node.js
.
When visiting the website on port 8080 we only have a login page available. Unfortunately, Gobuster did not provide any further directories. So let's continue.
Next, we focus on the Docker registry that is exposed on port 5000.
A Docker registry is a storage and distribution system for named Docker images. The same image might have multiple different versions, identified by their tags. A Docker registry is organized into Docker repositories , where a repository holds all the versions of a specific image. The registry allows Docker users to pull images locally, as well as push new images to the registry (given adequate access permissions when applicable).
Hacktricks provides us with all kinds of ways to enumerate this:
We use cURL to enumerate and find all kinds of useful information. We see it is configured for HTTP. Furthermore, we display the available repositories via _catalog
. The repository umbrella/timetracking
is available to us.
Next, we pull the tags of the umbrella/timetracking
repository and get the tag latest.
With the tag and the repository, we can now pull the manifests. Inside the manifest is the history and the blobs used by Docker. The history refers to the commands or instructions that were used to build each layer of the Docker image. The blobs refer to binary large objects, which are essentially the individual layers that compose a Docker image.
In the first entry of the history, we see the database password used, and can also answer the first question of the room.
We log in to the database with the credentials from the history and find credentials for claire-r
, chirs-r
, jill-r
and barry-b
. Those are MD5 encoded and can easily be cracked via hashcat for example.
hashcat -a 0 -m 0 hashes /usr/share/wordlist/rockyou.txt
.
Foothold
I have divided this section into two parts, as we need two footholds for the rest of the challenge. The first part shows how the challenge should actually be solved in terms of the sequence of events. Unfortunately, I had entered the credentials manually when enumerating and mistyped a password and thus only focused on Part II initially. But first, move on to Part I.
Part I
Since we have credentials, we can try them out not only directly on the website but also with SSH access. With the creds from claire-r
we have access to both the time tracking app on 8080...
... as well as access to the machine via SSH. We are the user claire-r
and find the first flag in the user's home directory.
In the timeTracker-src
directory, we find the sources for the app on port 8080. We can see that this is set up via a Docker container. We could now look in app.js
to see exactly how the app works and identify a vulnerability there. However, we will deal with this in Part II. Because it is also possible to get a view on app.js
without access via claire-r
.
Inside the Docker compose file, we see that the /logs
folder is mounted. That might be interesting later. Seeing this after already having exploited 8080, the way of thinking could be about a misconfigured Docker Container running the application. So, the next steps could therefore be to get a foothold over 8080 and escalated our privileges from the vulnerable Docker container. And that is exactly what we will do.
Part II
We log in to the website with the claire-r
credentials. We see a time entry tool with the option of using mathematical operations to update our tracked times. When we enter a numerical value, the time spent value increases.
We go back a few steps and remember the manifests file. From this, we can pull the blobs via curl http://umbrella.thm:5000/v2/umbrella/timetracking/blobs/sha256:<HASH> -o a.tar
. We can then unpack these via tar -xf tar.a
. It is important here that the extracted files are overwritten when unpacking multiple blobs. So to avoid confusion, we delete the extracted blob before pulling a new one.
With the following hash, we get access to app.js
.
Here, we notice in line 71 let timeCalc = parseInt(eval(request.body.time));
that the entered time value is evaluated using eval()
. Here we have our entry point for injecting our own commands.
We try out the payloads on the following page (An excellent resource on Node.js
Command Injection Payloads):
With arguments[1].end(require('child_process').('cat /etc/passwd'))
we are able to retrieve the /etc/passwd
file.
After a good batch of payloads, only Perl worked. To avoid bad characters, we encode the payload as base64.
Our payload, then, looks like the following:
With that, we are able to gain a reverse shell on the Docker container, and we are directly root on the container. This begs for a specific privilege escalation on Docker. But to be fair, after initially getting access, the main idea was to escape from the container to a user on the host (not having the SSH access).
Not much could be done in the Container, usual binaries like ps
were not available.
Which may be helpful in other challenges:
In order to somehow still be able to enumerate with the usual scripts, I base64 encoded them and wrote them directly to a file, which I then decoded and wrote the contents to a script file afterwards. Then I had to remove carriage returns. But this is not practicable for binaries.
$> cat <<EOF > b64.txt
BASE64_INPUT
EOF
$> base64 -d 64.txt > script.sh
$> sed -i -e 's/\r$//' script.sh
$> chmod +x script.sh
Privilege Escalation
In the root directory, we find the folder /logs
. This was very interesting from the start, even without knowing that claires-r
's home directory belongs to UID 1001
. One that is not present on the Docker container.
To confirm that this is the log directory from clair-r
we look at the contents and could also create a file in there.
Inboth we found the tt.log
.
We make use of the following privilege escalation technique:
If you have access as root inside a container that has some folder from the host mounted and you have escaped as a non privileged user to the host and have read access over the mounted folder. You can create a bash suid file in the mounted folder inside the container and execute it from the host to privesc.
We copy /bin/bash
to /home/claire-r/timeTracker/logs
as claire-r
session.
Next, we modify the permissions inside the container, since we are root.
Now we have a SUID bash binary, owned by root in /log
. After executing /bin/bash -p
as claire-r
we get a root shell and have access to the final flag in /root
.
Last updated