45+ boxes adentro. Este es el workflow de enumeración en el que me asenté. No el “correcto” — el que realmente funciona bajo presión de tiempo sin saltarse cosas.
El Enfoque de Dos Pasadas de Nmap
Antes corría un solo nmap scan grande. Perdía tiempo. Ahora hago dos pasadas cada vez.
Pasada 1 — TCP Scan Rápido
nmap -sC -sV -oN scans/initial.nmap $IP
Esto pega a los top 1000 puertos. Toma como 30 segundos. Me da algo con qué trabajar inmediatamente mientras corre el scan completo.
Pasada 2 — Full Port Scan (Background)
nmap -p- -sV -oN scans/allports.nmap $IP
Correr esto en otra terminal. Captura servicios escondidos en puertos altos raros — y casi siempre hay uno. Perdí tiempo en boxes porque una web app estaba corriendo en el puerto 8443 o 50000 y no la encontré hasta 45 minutos después.
Cuándo También Corro UDP
nmap -sU --top-ports 50 -oN scans/udp.nmap $IP
Solo top 50. Full UDP scan tarda una eternidad y rara vez paga. Los puertos que importan:
- 53 — DNS (zone transfers)
- 161 — SNMP (community strings = info gratis)
- 389 — LDAP
- 123 — NTP (a veces útil para enumeración de dominio)
Regla: Si es una box de AD, siempre correr UDP. SNMP y DNS sobre UDP me han dado footholds más de una vez.
Servicio por Servicio — Lo Que Realmente Reviso
El orden importa. Voy recorriendo servicios por probabilidad de darme un foothold.
HTTP/HTTPS (80, 443, 8080, 8443)
Web es casi siempre la superficie de ataque. La pego primero.
Revisiones inmediatas:
- Navegar el sitio manualmente. Leer todo. Revisar código fuente.
- Revisar
/robots.txt,/sitemap.xml - Identificar el tech stack — Wappalyzer o response headers
whatweb http://$IP
Directory brute force:
feroxbuster -u http://$IP -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,txt,html -o scans/ferox.txt
Ahora uso feroxbuster sobre gobuster. Recursivo por defecto, más rápido, mejor output.
Árbol de decisión para extensiones:
- Box Linux →
-x php,txt,html,sh - Box Windows →
-x aspx,asp,txt,html,config - Framework identificado → agregar extensiones específicas del framework (
.jsp,.do, etc.)
Errores comunes que he cometido:
- No agregar un hostname a
/etc/hosts. Virtual hosting está en todos lados. Si ves un nombre de dominio en algún lado — headers, contenido de página, certificado — agregalo y scanneá de nuevo. - Solo escanear con una wordlist. Si
directory-list-2.3-mediumno encuentra nada, probarraft-medium-words.txt. Diferentes listas capturan diferentes cosas. - Ignorar 403s. Un 403 significa que algo existe. Probar bypasses, probar agregar extensiones, probar enumeración más profunda en esa ruta.
Enumeración de subdominios/vhost:
ffuf -u http://$IP -H "Host: FUZZ.domain.htb" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fs <default_size>
El flag -fs es crítico. Filtrar el tamaño de respuesta default o te vas a ahogar en falsos positivos.
SMB (139, 445)
Segunda cosa que reviso. SMB filtra info como loco.
# Revisión null session
smbclient -N -L //$IP/
# Enum4linux (ruidoso pero completo)
enum4linux -a $IP
# Listar shares con credenciales después
smbmap -H $IP
smbmap -H $IP -u 'guest' -p ''
Lo que estoy buscando:
- Shares legibles — Descargar todo.
smbget -R smb://$IP/sharename/ - Shares escribibles — Potencial para ataques de archivos SCF, tirar payloads
- Listas de usuarios — RID brute forcing si null sessions funcionan
# RID brute force para usernames
crackmapexec smb $IP -u '' -p '' --rid-brute
Ojo: smbclient -N y smbmap se comportan diferente con null sessions. Si uno falla, proba el otro. He tenido boxes donde smbclient no muestra nada pero smbmap con guest / password vacia lista shares bien.
FTP (21)
Revisión rápida. O es útil o no.
# Login anonimo
ftp $IP
# Username: anonymous
# Password: (vacio o cualquier cosa)
Si anónimo funciona:
- Descargar todo:
mget * - Revisar si podes subir. Subir un archivo de prueba. Si también está sirviendo HTTP, podes tener una ruta de webshell.
- Revisar archivos ocultos:
ls -la
Si anónimo no funciona: Avanzar. Volver con creds después.
DNS (53)
Importa mucho en boxes de AD. Menos en standalone.
# Intento de zone transfer
dig axfr @$IP domain.htb
# Reverse lookup
dig -x $IP @$IP
# Any records
dig any domain.htb @$IP
Zone transfers son raros en engagements reales pero aparecen en boxes de HTB/OSCP regularmente. Siempre intentarlo.
Subdomain brute force via DNS:
dnsenum --dnsserver $IP --enum -f /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt domain.htb
LDAP (389, 636)
Si LDAP está abierto, es una box de AD. Actuar acorde.
# Anonymous bind
ldapsearch -x -H ldap://$IP -b "DC=domain,DC=htb"
# Obtener naming contexts primero si no sabés el dominio
ldapsearch -x -H ldap://$IP -s base namingcontexts
Lo que querés de LDAP:
- Lista completa de usuarios
- Membresias de grupo
- Campos de descripción (a los admins les encanta poner passwords en las descripciones)
- Service accounts
# Sacar todos los usuarios
ldapsearch -x -H ldap://$IP -b "DC=domain,DC=htb" '(objectClass=person)' sAMAccountName description memberOf
Ojo: Anonymous bind no siempre significa que obtenés datos. Algunos DCs permiten bind pero no retornan nada. Probar con un usuario valido después.
Kerberos (88)
Kerberos abierto = box de AD. Dos cosas a hacer inmediatamente sin creds.
Enumeración de usuarios:
kerbrute userenum -d domain.htb --dc $IP /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt
Esto no es ruidoso de la forma que lo es brute forcing. No genera eventos de logon fallido para usuarios válidos. Excelente para armar una lista de usuarios.
AS-REP Roasting (sin creds):
impacket-GetNPUsers domain.htb/ -dc-ip $IP -usersfile users.txt -no-pass
Si alguna cuenta tiene “Do not require Kerberos preauthentication” seteado, obtenés un hash. Crackealo.
MSSQL (1433)
# Creds default
impacket-mssqlclient sa:sa@$IP
impacket-mssqlclient sa:''@$IP
# Con creds encontradas después
impacket-mssqlclient domain.htb/user:password@$IP -windows-auth
Si entras:
xp_cmdshellpara ejecución de comandos del OS (puede que necesites habilitarlo)xp_dirtreepara forced authentication / hash capture- Revisar linked servers —
EXEC sp_linkedservers
-- Habilitar xp_cmdshell
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
EXEC xp_cmdshell 'whoami';
SNMP (161/UDP)
Criminalmente subestimado. SNMP con community string “public” dumpea todo.
# Revisar community strings
onesixtyone -c /usr/share/seclists/Discovery/SNMP/snmp.txt $IP
# Recorrer el arbol MIB
snmpwalk -v2c -c public $IP 1.3.6.1
OIDs útiles:
1.3.6.1.2.1.25.4.2.1.2— Procesos corriendo1.3.6.1.4.1.77.1.2.25— Cuentas de usuario1.3.6.1.2.1.25.6.3.1.2— Software instalado
Uso snmpwalk con OIDs específicos en vez de recorrer el árbol completo. Más rápido, menos ruido.
Ojo: Si SNMPv1/v2c está corriendo, los community strings se envían en cleartext. Default es casi siempre public. Pero también probar private, manager, internal.
WinRM (5985, 5986)
No se puede hacer mucho con WinRM sin creds, pero anotar que está abierto. Una vez que tengas credenciales:
evil-winrm -i $IP -u 'user' -p 'password'
Si el puerto 5985 está abierto y tenes creds válidas → shell instantánea. Este es el go-to para lateral movement en Windows.
SSH (22)
No hay mucho que enumerar sin creds. Anoto la versión (a veces revela versión del OS) y avanzo.
# Banner grab
nmap -sV -p22 $IP
Volver con credenciales encontradas. Probar reutilización de usernames. Probar keys encontradas en otros servicios.
Una cosa: Si encontras una private SSH key en cualquier lado — directorio web, share SMB, FTP — probala inmediatamente contra cada usuario que conozcás.
El Workflow en Práctica
Así se ven los primeros 15 minutos en una box:
- Terminal 1: Nmap scan rápido → empezar a trabajar resultados inmediatamente
- Terminal 2: Full port scan corriendo en background
- Terminal 3: Si HTTP encontrado → browser abierto, whatweb, feroxbuster corriendo
- Toma de notas: Registrar cada hallazgo, cada username, cada credencial potencial
Target: $IP
Hostname: ???
OS: ???
Domain: ???
Open Ports:
-
Users Found:
-
Credentials:
-
Interesting Findings:
-
Mantengo esta nota corriendo y la actualizo constantemente. Suena básico. Es lo que más tiempo me ahorra.
Cuándo Avanzar vs. Profundizar
Esta es la habilidad más difícil y todavía la estoy aprendiendo.
Profundizar cuando:
- Encontraste una web app custom (casi seguro hay una vuln ahí)
- Encontraste usernames pero no passwords (hora de password spraying)
- Un servicio retornó información parcial (probablemente hay más)
- Tenés un set de creds pero no podés conseguir shell (reutilizalas en todos lados)
Avanzar cuando:
- Página default, sin contenido custom, directory busting no retorna nada después de dos wordlists
- Servicio requiere autenticación y tenes cero creds
- Llevás 30+ minutos en un servicio sin nada que mostrar
El mayor desperdicio de tiempo: Intentar explotar algo antes de terminar la enumeración. He quemado horas intentando conseguir un foothold a través de un servicio mientras la ruta real era por un puerto que todavía no había escaneado.
Enumerar todo primero. Explotar segundo. Cada vez que rompo esta regla, me arrepiento.
Referencia Rápida — Puerto a Acción
| Puerto | Servicio | Primer Movimiento |
|---|---|---|
| 21 | FTP | Login anónimo |
| 22 | SSH | Anotar versión, avanzar |
| 25 | SMTP | Enumeración de usuarios (VRFY/EXPN) |
| 53 | DNS | Zone transfer |
| 80/443 | HTTP | Navegar, tech stack, directory brute |
| 88 | Kerberos | User enum, AS-REP Roast |
| 110/143 | POP3/IMAP | Probar creds default |
| 135 | MSRPC | rpcclient null session |
| 139/445 | SMB | Null session, listar shares |
| 161 | SNMP | Community string brute, walk |
| 389/636 | LDAP | Anonymous bind, dumpear usuarios |
| 1433 | MSSQL | Creds default (sa:sa) |
| 3306 | MySQL | Creds default (root:root) |
| 5985 | WinRM | Necesitas creds → evil-winrm |
| 5432 | PostgreSQL | Creds default (postgres:postgres) |
| 6379 | Redis | Sin auth? → RCE |
| 8080 | HTTP Alt | Igual que 80 |
Este workflow no es perfecto. Lo actualizo después de cada box que me enseña algo nuevo. El principio central no cambia: escanear rápido, enumerar amplio, tomar notas, no explotar hasta que hayas visto el panorama completo.