Resource-Based Constrained Delegation
Constrained delegation works by configuring SPNs on the frontend service
under the msDSAllowedToDelegateTo property.
Configuring constrained delegation also requires the
SeEnableDelegationPrivilege privilege on the domain controller,
which is typically only enabled for Domain Admins.
With the release of Windows Server 2012,
Microsoft introduced resource-based constrained delegation,
which is meant to remove the requirement of highly elevated access rights
like SeEnableDelegationPrivilege from system administrators.
RBCD works by essentially turning the delegation settings around.
The msDS-AllowedToActOnBehalf OfOtherIdentity property
controls delegation from the backend service.
To configure RBCD, the SID of the frontend service
is written to the new property of the backend service.
One advantage of this approach is that
SeEnableDelegationPrivilege permissions are no longer required
and RBCD can typically be configured
by the backend service administrator instead.
Once RBCD has been configured, the frontend service can use S4U2Self
to request the forwardable TGS for any user to itself
followed by S4U2Proxy to create a TGS for that user to the backend service.
Unlike constrained delegation,
under RBCD the KDC checks if the SID of the frontend service is present
in the msDS-AllowedToActOnBehalf OfOtherIdentity property
of the backend service.
One important requirement is that the frontend service
must have an SPN set in the domain.
A user account typically does not have an SPN set but all computer accounts do.
This means that any attack against RBCD
needs to happen from a computer account
or a service account with a SPN.
The same attack we performed against constrained delegation applies to RBCD
if we can compromise a frontend service that has its SID configured
in the msDSAllowedToActOnBehalf OfOtherIdentity property
of a backend service.
We’ll cover a RBCD attack in this section that leads to code execution on appsrv01.
This specific vector starts by compromising a domain account
that has the GenericWrite access right on a computer account object.
This technique is the only known way
of turning GenericWrite on a computer object into code execution.
As usual, we begin with enumeration.
In this case, we start with the dave domain user
from the Windows 10 client machine.
We’ll reuse our enumeration technique from the prior sections.
This time, we’ll replace Get-DomainUser with Get-DomainComputer
to target computer accounts instead.
The output reveals that the dave user has GenericWrite to appsrv01.
Since we have GenericWrite on appsrv01,
we can update any non-protected property on that object,
including msDS-AllowedToActOn BehalfOfOtherIdentity
and add the SID of a different computer.
Once a SID is added, we will act in the context of that computer account
and we can execute the S4U2Self and S4U2Proxy extensions
to obtain a TGS for appsrv01.
To do this, we either have to obtain the password hash of a computer account
or simply create a new computer account object
with a selected password.
By default, any authenticated user can add up to ten computer accounts to the domain
and they will have SPNs set automatically.
This value is present in the ms-DS-MachineAccountQuota property
in the Active Directory domain object.
We can enumerate ms-DS-MachineAccountQuota
with the PowerView Get-DomainObject method.
Normally, the computer account object is created
when a physical computer is joined to the domain.
We can simply create the object itself with the
New-MachineAccount method of the Powermad.ps1 PowerShell script.
To use it, we’ll specify the target computer account name,
and the password.
The password must be supplied as a SecureString,
which we can generate with ConvertTo-SecureString.
The output shows that the command was successful
and subsequent enumeration with Get-DomainComputer,
reveals that the computer account object is present.
The msDS-AllowedToActOn BehalfOfOtherIdentity property
stores the SID as part of a security descriptor in a binary format.
We must convert the SID of our newly-created computer object
to the correct format in order to proceed with the attack.
To do this, we must first create a new security descriptor with the correct SID.
In the beginning of this module,
we determined that the SID is the last portion of a security descriptor string
so we can reuse a working string, replacing only the SID.
Fortunately, security researchers have discovered
a valid security descriptor string that we can use.
We can use the RawSecurityDescriptor class,
to instantiate a SecurityDescriptor object.
With the SecurityDescriptor object created,
we must convert it into a byte array
to match the format for the
msDS-AllowedToActOnBehalfOf OtherIdentity property.
After the SecurityDescriptor has been converted to a byte array,
we can use Get-DomainComputer to obtain
a handle to the computer object for appsrv01,
and then pipe that into Set-DomainObject,
which can update properties by specifying them with -Set options.
Remember that it is not normally possible to set the
msDS-AllowedToActOnBehalf OfOtherIdentity property
for an arbitrary computer account.
However, since our dave user has the GenericWrite access right to appsrv01,
we can set this property.
We could also use this attack vector with GenericAll,
WriteProperty, or WriteDACL access rights to appsrv01.
After writing the SecurityDescriptor to the property field, we should verify it.
We can do this by reading the binary version of it with Get-DomainComputer,
then instantiating a SecurityDescriptor object with RawSecurityDescriptor,
and finally displaying the DACL.
The SecurityDescriptor was indeed set correctly in the
msDSAllowedToActOnBehalfOf OtherIdentity property for appsrv01.
Now we can begin our attack in an attempt to compromise appsrv01.
We’ll start by obtaining the hash of the computer account password with Rubeus.
In the previous section, we used the Rubeus asktgt command
to request a TGT before invoking the s4u command.
We can also directly submit the username and password hash to the s4u command,
which will implicitly call asktgt and inject the resultant TGT,
after which the S4U \extensions will be invoked.
After obtaining the TGT for the myComputer machine account,
S4U2Self will then request a forwardable service ticket
as the administrator user to the myComputer computer account.
Finally, S4U2Proxy is invoked to request a TGS for the CIFS service on appsrv01
as the administrator user, after which it is injected into memory.
To check the success of this attack,
we’ll first dump any loaded Kerberos tickets with klist.
Now that we have a TGS for the CIFS service on appsrv01 as administrator,
we can interact with file services on appsrv01
in the context of the administrator domain admin user.
Our access to appsrv01 is in the context of the administrator domain admin user.
We can use our CIFS access to obtain code execution on appsrv01,
but in the process we will perform a network login
instead of an interactive login.
This means our access will be limited to appsrv01
and cannot directly be used to expand access towards the rest of the domain.