// 01
Reconocimiento
El primer paso siempre es confirmar que la máquina objetivo está activa y responde a través de la VPN de HackTheBox. Usamos ping con el flag -c 3 para limitar el envío a exactamente 3 paquetes ICMP — sin este flag el ping sería infinito. La IP objetivo es 10.129.238.31.
El TTL de 63 en la respuesta es un indicador del sistema operativo: Linux inicia con TTL 64 y pierde 1 al pasar por cada hop de red, por lo que recibir 63 indica que hay exactamente un router entre nosotros y la máquina. Windows usaría TTL 128, por lo que este resultado confirma que es Linux.
Con conectividad confirmada, lanzamos un escaneo exhaustivo con nmap guardando resultados en XML para poder transformarlos luego en HTML. Desglosando cada flag:
-p- escanea los 65,535 puertos TCP en lugar de solo los 1,000 más comunes. Muchos servicios interesantes corren en puertos no estándar — un escaneo básico los pasaría por alto. Es más lento pero garantiza cobertura completa.
-sSCV combina tres flags en uno: -sS (SYN scan — envía solo el paquete SYN sin completar el handshake TCP, más rápido y sigiloso que un connect scan completo), -sC (ejecuta los scripts por defecto de NSE — Nmap Scripting Engine — que detectan configuraciones, banners y vulnerabilidades comunes), y -sV (detecta la versión exacta del servicio en cada puerto abierto).
-n desactiva la resolución DNS inversa. Cuando nmap intenta resolver cada IP a un nombre de dominio, añade latencia innecesaria en un entorno de laboratorio donde esa información no aporta valor.
-Pn deshabilita el host discovery previo al escaneo.
Por defecto nmap primero comprueba si el host está activo enviando paquetes ICMP.
En algunas máquinas HTB el firewall bloquea ICMP pero los puertos TCP están abiertos —
sin -Pn nmap
asumiría que el host está caído. Ya confirmamos conectividad con ping, así que lo saltamos.
--min-rate=5000 fuerza a nmap a enviar al menos 5,000 paquetes por segundo, acelerando significativamente el escaneo de 65,535 puertos de varios minutos a menos de un minuto. Aceptable en entornos de laboratorio.
--open filtra la salida para mostrar solo puertos en estado abierto, descartando los cerrados y filtrados para una lectura limpia.
-oX conversor.xml guarda los resultados en formato XML.
Este formato estructurado permite transformarlo posteriormente con
xsltproc en un
HTML visual con tablas y colores, útil para reportes profesionales.
// ANÁLISIS DE RESULTADOS
Solo 2 puertos abiertos: 22 (SSH) con OpenSSH 8.9p1 y 80 (HTTP) con Apache 2.4.52. La línea clave es http-title: Did not follow redirect to http://conversor.htb/ — nmap detectó que el servidor redirige a un dominio virtual. Además el campo Service Info: Host: conversor.htb nos confirma el nombre de dominio que debemos configurar en nuestro sistema.
xsltproc es un procesador de hojas de estilo XSLT de línea de
comandos. Nmap incluye una hoja de estilo
(/usr/share/nmap/nmap.xsl)
que transforma el XML generado en un HTML tabular con colores y estilos, perfecto para
incluir en reportes o revisar escaneos grandes cómodamente en el navegador. Este paso
también es relevante para entender conceptualmente qué hace Conversor — la máquina que
estamos atacando.
// 02
Configurar /etc/hosts
El servidor Apache usa Virtual Hosting: sirve diferente contenido
según el valor del header HTTP Host
de cada petición. Si accedemos directamente por IP, Apache no reconoce el sitio y redirige
al dominio correcto. Como conversor.htb no existe en DNS
público (es un dominio ficticio de HTB), debemos indicarle manualmente a nuestro sistema
cómo resolverlo.
El archivo /etc/hosts es el resolver DNS local del sistema operativo —
se consulta antes que cualquier servidor DNS externo. Al agregar la línea
10.129.238.31 conversor.htb,
cualquier petición a ese dominio va directamente a la IP de la máquina sin consultar DNS.
Requiere sudo porque /etc/hosts es un archivo del sistema.
// ¿POR QUÉ VIRTUAL HOSTING?
Virtual Hosting permite a un servidor alojar múltiples sitios web en la misma IP. Apache decide qué sitio mostrar leyendo el header Host de cada petición HTTP. Si accedemos por IP, el header Host es la IP y Apache no encuentra coincidencia con "conversor.htb", por eso redirige. Al configurar /etc/hosts, el navegador envía Host: conversor.htb y Apache sirve el sitio correcto.
// 03
Exploración Web · Puerto 80
Al acceder a http://conversor.htb encontramos una aplicación web llamada Conversor: convierte archivos XML en HTML visualmente atractivo utilizando hojas de estilo XSLT. La app requiere registro e inicio de sesión. La página tiene dos inputs: un XML File y un XSLT File.
Navegamos a la página /about donde encontramos a los desarrolladores del proyecto. Hay un botón muy interesante: Download Source Code. Esta es una mala práctica de seguridad gravísima: exponer el código fuente de la aplicación a cualquier visitante permite a un atacante auditar la lógica interna y encontrar vulnerabilidades sin necesidad de técnicas de fuzzing o OSINT.
Con curl hacemos una petición GET a /about y filtramos las URLs
con grep buscando los términos "download", "source" y "href"
con el flag -i (case insensitive). El pipe
| redirige la salida
estándar de curl como entrada de grep, evitando guardar el HTML en disco.
// EXPOSICIÓN DE CÓDIGO FUENTE
Encontramos que el código fuente está disponible en /static/source_code.tar.gz. Exponer el código fuente de una aplicación web en producción es una vulnerabilidad de Information Disclosure crítica: permite al atacante entender la lógica de negocio, encontrar credenciales hardcodeadas, identificar rutas internas, y descubrir cómo se procesan los datos — todo sin disparar ninguna alerta en el servidor.
// 04
Source Code Analysis
Descargamos el archivo comprimido con curl usando el flag
-o para especificar el nombre del archivo de salida.
Sin -o, curl
imprimiría el contenido binario por pantalla sin guardarlo en disco.
Verificamos el tipo de archivo con file y luego lo extraemos con tar. Los flags de tar: -x (extract, extraer), -v (verbose, muestra cada archivo extraído), -f (file, especifica el archivo a procesar). El resultado nos revela la estructura completa de la aplicación Flask.
El archivo más importante es app.py, el corazón de la aplicación
Flask. Revisamos cómo procesa los archivos subidos por el usuario. En la ruta
/convert encontramos
el código crítico:
// VULNERABILIDAD — XSLT INJECTION
El XML se parsea con restricciones de seguridad (resolve_entities=False,
no_network=True), bloqueando ataques XXE. Sin embargo, el XSLT se
parsea sin ninguna restricción: etree.parse(xslt_path) sin argumentos
de seguridad. XSLT es un lenguaje de programación completo — la extensión
exsl:document permite escribir archivos arbitrarios en el
sistema de archivos del servidor. Un atacante que controla el XSLT puede escribir
cualquier contenido en cualquier ruta accesible por el proceso www-data.
El archivo install.md documenta cómo se despliega la aplicación en el servidor. Contiene una línea de crontab que revela algo fundamental para la explotación:
// EL CRON JOB — PIEZA CLAVE DE LA CADENA
Este cron job ejecuta cada minuto (indicado por los cinco asteriscos
* * * * *) todos los archivos .py que existan en el directorio
/var/www/conversor.htb/scripts/, como el usuario www-data.
La combinación es letal: si podemos escribir un archivo .py en esa ruta (usando la XSLT
Injection), el cron lo ejecutará automáticamente en el próximo ciclo — sin necesidad de
ninguna interacción del servidor o de un usuario.
// 05
XSLT Injection · Escritura de Archivo
Antes de ir directamente a la reverse shell, verificamos que la XSLT Injection funciona usando exsl:document. Esta función de la extensión EXSLT permite crear y escribir archivos en el sistema de archivos del servidor durante la transformación XSLT.
La ruta de escritura elegida para la prueba es
/var/www/conversor.htb/static/probe.txt — está dentro del
directorio estático de Apache, lo que significa que podremos descargarlo directamente con
curl para confirmar que la escritura funcionó. El contenido es simplemente
EXSLT_OK.
El XML de prueba (test.xml) es el documento mínimo válido que XSLT necesita para procesar: una declaración XML y un nodo raíz con cualquier contenido. Sin un XML válido, la aplicación rechazaría la transformación antes de procesar el XSLT.
Subimos ambos archivos desde la interfaz web de Conversor. Al completarse la transformación, la aplicación devuelve el resultado — pero lo importante no es ese HTML, sino que el XSLT ya ejecutó el exsl:document y escribió el archivo probe.txt en el servidor. Verificamos accediendo directamente a la ruta estática con curl:
// ¿QUÉ CONFIRMA ESTE RESULTADO?
La respuesta EXSLT_OK confirma que tenemos escritura arbitraria
de archivos como el usuario www-data en el sistema de archivos del servidor.
Ahora conocemos exactamente qué necesitamos: escribir un script Python de reverse shell
en el directorio /var/www/conversor.htb/scripts/ para que el cron lo ejecute
en el próximo minuto.
// 06
Reverse Shell · www-data
Ahora preparamos el XSLT final con el payload real. Usando el mismo mecanismo exsl:document, escribimos un script Python de reverse shell directamente en la ruta que el cron job ejecuta: /var/www/conversor.htb/scripts/shell.py.
El código Python usa el módulo socket para crear una conexión TCP saliente
hacia nuestra IP y puerto. Luego con os.dup2 redirige los descriptores de
archivo de stdin (0), stdout (1) y stderr (2) al socket — de esta forma toda la entrada y
salida del proceso va a través de la conexión de red. Finalmente
pty.spawn("/bin/bash")
lanza una bash interactiva sobre esa conexión. Debes reemplazar la IP
10.10.16.7 con tu IP de HTB (visible en
ip a en la interfaz
tun0).
Antes de subir el XSLT malicioso, ponemos en escucha nuestro listener con netcat. Los flags: -l (listen, modo escucha), -v (verbose, muestra mensajes de conexión), -n (no DNS, no resolver nombres), -p 4444 (puerto en el que escuchar). Es fundamental que el listener esté activo antes de que el cron ejecute el script, ya que si la conexión TCP no tiene destino, el script fallará silenciosamente.
Una vez recibida la conexión, obtenemos una shell como
www-data. La mejoramos con
python3 -c 'import pty;pty.spawn("/bin/bash")' para obtener
una bash interactiva completa con historial, autocompletado y control de trabajos.
Luego export TERM=xterm configura el tipo de terminal
para que comandos como clear funcionen correctamente.
// ¿POR QUÉ MEJORAR LA SHELL?
Una shell de netcat básica no tiene TTY (teletypewriter). Sin TTY no funcionan:
el historial con las flechas, el autocompletado con Tab, los comandos como su,
sudo y editores de texto interactivos. pty.spawn crea un
pseudo-terminal que simula una terminal real, desbloqueando todas esas funcionalidades.
Es un paso fundamental tras obtener cualquier reverse shell.
// 07
SQLite · Extracción de Hashes
Con shell como www-data, enumeramos el directorio de la aplicación. En el directorio /var/www/conversor.htb/instance/ encontramos el archivo users.db — la base de datos SQLite de la aplicación que ya habíamos visto en el código fuente descargado.
sqlite3 es el cliente de línea de comandos para bases de datos SQLite.
La sintaxis es sqlite3 <archivo.db> "consulta SQL".
Con SELECT * FROM users
recuperamos todos los registros de la tabla de usuarios, incluyendo sus hashes de contraseña.
Los hashes obtenidos tienen 32 caracteres hexadecimales — formato característico de MD5. MD5 es un algoritmo de hash criptográfico diseñado en 1991, considerado completamente roto para el almacenamiento de contraseñas: es computacionalmente barato, no usa salt por defecto, y existen bases de datos masivas de hashes precomputados.
CrackStation (https://crackstation.net) es un servicio online que dispone de bases de datos rainbow tables con miles de millones de pares hash↔password. Introducimos el hash de fismathack: 5b5c3ac3a1c897c94caad48e6c71fdec y obtenemos la contraseña en texto plano en segundos.
// 08
SSH como fismathack · User Flag
Con las credenciales crackeadas intentamos acceso SSH directamente a la máquina. Esta técnica se llama Password Reuse: la contraseña de la cuenta web también sirve como contraseña del usuario del sistema operativo. SSH (Secure Shell) proporciona una conexión cifrada y una terminal interactiva completa — mucho más estable que una reverse shell de netcat.
Tras autenticarnos encontramos el archivo user.txt en el home de fismathack con la User Flag de la máquina.
// 09
Escalada de Privilegios · needrestart
El primer paso en la escalada de privilegios es revisar qué puede ejecutar el usuario
actual con privilegios de root. El comando sudo -l lista todos
los comandos que fismathack tiene permiso de ejecutar con sudo, sin necesidad de contraseña
si aparece NOPASSWD.
// CVE-2024-48990 — NEEDRESTART PRIVILEGE ESCALATION
needrestart es una utilidad de Ubuntu/Debian que detecta servicios que
necesitan reiniciarse tras actualizar librerías. La versión instalada es vulnerable a
CVE-2024-48990: needrestart ejecuta el intérprete Perl para procesar su
archivo de configuración. Si la configuración especifica un archivo arbitrario con
-c, Perl ejecuta ese archivo como código Perl con los privilegios de quien
invoca needrestart — en este caso, root (porque lo ejecutamos con sudo).
Podemos escribir un script Perl malicioso que se ejecutará como root.
Escribimos un script Perl en /dev/shm/pwn.pl.
El directorio /dev/shm
es un sistema de archivos en RAM (shared memory) con permisos de escritura para todos los
usuarios — es el lugar ideal para archivos temporales en explotación porque no requiere
acceso especial y no deja rastro en disco tras un reinicio.
El payload usa
system('/usr/bin/install', '-m', '04755', '/bin/bash', '/usr/local/bin/bsh').
Desglosando: install copia un archivo con permisos específicos.
El flag -m 04755 establece permisos
SUID (el 04 activa el setuid bit) más rwxr-xr-x normal.
El SUID bit en un ejecutable hace que se ejecute con los privilegios del propietario
del archivo en lugar del usuario que lo invoca. Como needrestart corre como root,
/bin/bash se copiará a /usr/local/bin/bsh siendo propiedad
de root y con SUID activado — lo que nos permitirá obtener una shell de root.
// 10
Root Flag
Ejecutamos needrestart con sudo especificando nuestro script Perl malicioso como archivo de configuración con el flag -c. El flag -r l indica el modo de reinicio "list" — útil para que needrestart procese la configuración sin intentar reiniciar servicios reales. Al procesar el archivo de configuración, needrestart (corriendo como root) ejecuta el script Perl, que instala bash con SUID.
Verificamos que el binario se creó correctamente con ls -la. Los permisos -rwsr-xr-x confirman el SUID activado (la s en la posición del bit de ejecución del propietario). El propietario es root. Ejecutamos bsh -p donde el flag -p (privileged) indica a bash que no descarte los privilegios SUID — sin este flag, bash moderno detecta que el UID real no coincide con el efectivo y los descarta como medida de seguridad.
// 11
Referencia de Comandos
- -p- — todos los 65,535 puertos TCP
- -sSCV — SYN scan + scripts NSE + versiones
- -n / -Pn — sin DNS / sin ping previo
- --min-rate=5000 — mínimo 5,000 pkt/s
- -oX — output en formato XML
- nmap.xsl — hoja de estilo XSLT de nmap
- > out.html — redirige salida a archivo
- "SELECT * FROM users" — recuperar todos los registros
- .tables — listar tablas disponibles
- .schema — ver estructura de la tabla
- -c — archivo de configuración alternativo (Perl)
- -r l — modo list, sin reiniciar servicios
- -m 04755 — SUID + rwxr-xr-x
- 04000 — bit setuid en octal
- -l — modo escucha (listen)
- -v — verbose, muestra mensajes
- -n — sin resolución DNS
- -p 4444 — puerto de escucha
// 12
Glosario
XSLTSecurityPreferences
que restrinja el acceso al sistema de archivos y la red desde las transformaciones.
/etc/crontab) define qué comandos ejecutar, con qué frecuencia y
bajo qué usuario. Si una tarea cron ejecuta scripts de un directorio donde un usuario
sin privilegios tiene permisos de escritura, ese usuario puede colocar código malicioso
que se ejecutará automáticamente con los privilegios del usuario de la tarea. En Conversor,
la combinación de escritura arbitraria de archivos (via XSLT Injection) en el directorio
scripts/ más el cron que ejecuta todo .py en ese directorio como www-data
crean una cadena de explotación completa sin necesidad de ninguna interacción adicional.
4000, visible como la letra
s en la posición de ejecución del propietario (-rwsr-xr-x).
La técnica de copiar bash con SUID es clásica en CTF: bash -p activa el modo
privilegiado que respeta los privilegios del propietario.
resolve_entities=False y
no_network=True. Sin embargo, no aplicó las mismas restricciones al parser
XSLT — que es donde reside la vulnerabilidad real. La lección: proteger solo una parte
del pipeline de procesamiento no es suficiente. Cada componente que procesa input del
usuario debe ser auditado y restringido independientemente.
-c permite especificar un archivo de configuración alternativo.
Como needrestart suele ejecutarse con sudo (o como root via systemd), un usuario que
pueda ejecutarlo con sudo -c /ruta/payload.pl puede hacer que root ejecute
código Perl arbitrario. La vulnerabilidad es especialmente peligrosa porque needrestart
es un paquete común en Ubuntu instalado por defecto en muchas configuraciones. La
mitigación es actualizar a needrestart 3.8 o restringir el uso de -c en
las reglas sudo.
/static/source_code.tar.gz sin
autenticación permite a cualquier visitante leer la lógica interna, encontrar las rutas
de archivos del servidor, identificar la base de datos SQLite incluida en el paquete
(instance/users.db), y descubrir el cron job en install.md.
Esta única vulnerabilidad fue la que reveló toda la cadena de ataque necesaria para
comprometer la máquina.
su o sudo fallan porque requieren un terminal real para pedir
contraseñas. pty.spawn("/bin/bash") crea un pseudo-TTY (par de
dispositivos pts/ptmx) que emula un terminal real, habilitando autocompletado, historial
de comandos, control de trabajos y compatibilidad con programas interactivos.