Kerberos IV - Delegations

In the third part of this Kerberos series, we focused on leveraging user credential material for impersonation through techniques such as Pass-the-Key/Ticket/Cache/Certificate, and Shadow Credentials. Additionally, we explored how to manage and forge Kerberos tickets to facilitate lateral movement, privilege escalation, and establish persistence within the domain.

In this fourth post, and last of the series, we will explore Kerberos delegations.

There are many great articles about delegations, even though the abuse of this feature is an old attack vector, here at Lares, we wanted to deep dive and analyze the whole flow of what Kerberos delegations entail. In this post, we’ll review the different types of delegation, providing a detailed understanding of their use cases and potential security implications.

While this post primarily focuses on the functionality of Kerberos delegations and abuse cases, we have also included brief notes and best practices for detecting potential abuses and misconfigurations, which will in turn help to enhance security postures against these risks.

Note: In each delegation scenario, the communication flow will be analyzed. For a review of the Kerberos communication process, refer to the first post in this series..

Kerberos I - Overview
This post, is the first in the series and will aim to provide an overview of the protocol, from its beginnings to the different (ab)use techniques.

Kerberos Delegations:

Microsoft introduced the Kerberos delegation feature with the first implementation of Active Directory in Windows 2000. Defined by Microsoft; Kerberos delegation is a setting that allows applications to request end-user access credentials to access resources on behalf of the originating user.

This feature was intended to solve the double-hop issue. ‘Double Hop’ issues can occur when a user needs to access a resource on a second server through an initial server. After authenticating to the first server (Server A), the user’s credentials are used to get a service ticket for Server A; however, when Server A needs to access resources on Server B on behalf of the user, it faces a problem because it cannot use the user’s credentials or tickets to authenticate to Server B. This prevents proper authentication on the second hop, resulting in access failures.

Below is a classic example of a basic double-hop scenario and would be why delegation is necessary:

  • The user Elliot.A wants to access company documents, so he authenticates with his domain credentials on the web application hosted on the server DEV.Lareslabs.local. This application runs in the context of its own service machine account (DEV$).
  • The folder that the web application has to retrieve is located on another server shared path (\\FS1\Data) and this folder has the following security descriptor, defining access rights for Elliot.A, and other ACEs that do not grant access to DEV$:
\\FS1\Data folder security descriptor.
  • When the web application tries to retrieve the files through the CIFS service on the FS1 server, it will do so under its context and will not have access, preventing Elliot.A from accessing the files through the web application:
Kerberos double-hop issue.
  • Kerberos delegation helps solve this problem, in the following example, DEV.lareslabs.local (DEV$) is configured with unconstrained delegation (the most insecure kind of delegation, which we will see next), so it can gain access to the shared folder impersonating Elliot, using Elliot´s Kerberos Tickets as proof that it has permission to impersonate, then placing a copy of the user token in a new thread of its executing process so that thread can act on behalf of Elliot.A and is subject to the restrictions imposed by ACLs:
Kerberos Delegation.

There are three kinds of delegations:

  • Unconstrained Delegation.
  • Constrained Delegation, (Kerberos Only and Protocol Transition).
  • Resource-based Constrained delegation (RBCD).

Note: The ASP.NET Core Web API application "KerbApp" by @_RastaMouse has been used for the lab configuration. The application allows the authentication scheme to be configured through HTTP.sys.

Unconstrained Delegation:

Unconstrained Delegation allows an entity to impersonate a Principal to any chosen service. When this delegation is configured in a service, the client delegates a copy of its TGT to this service, so this service can act on behalf of the client in the network by using that TGT.  

So, it allows an attacker who has gained control of a domain account configured with delegated permissions to impersonate any user or service within the domain.

To configure graphically, it is required to enable the option "Trust this user for delegation to any service (Kerberos Only)":

Unconstrained Delegation configured on DEV

Using the PS Active Directory module, it is possible to verify whether an account is configured with Unconstrained Delegation by checking the attribute "TrustedForDelegation":

Unconstrained enumeration using Get-ADComputer function.

To better understand this kind of delegation, let's examine step-by-step the traffic generated during the scenario described above:

  • The user Elliot.A wants to access company documents, so he authenticates using his domain credentials on the web application hosted on the server DEV.lareslabs.local. Since DEV$ does not have direct permissions to access these files, which are hosted on another server called FS1.lareslabs.local, it is configured with unconstrained delegation. This allows it to impersonate Elliot.A and access the files on his behalf.

The communication flow in this scenario will be as follows:

Unconstrained Delegation flow.

The following shows Elliot.A performing an HTTP request to DEV webapp:

HTTP request.

Using Wireshark to capture and review this network traffic, we will be able to identify the following network flow:

Unconstrained Delegation network traffic.

Let's break things down step by step:

  1. The client, Elliot.A, requests a TGT from the KDC, if credentials are valid, the KDC will return the TGT, just the normal Kerberos flow, AS-REQ/AS-REP.
  2. The client requests an HTTP Service Ticket (HTTP/DEV.lareslabs.local) in the first TGS-REQ, sending his TGT and authenticator:
HTTP/DEV.lareslabs.local TGS-REQ

3. The KDC provides the Service Ticket with the flag ok-as-delegate flag set, which indicates that the requested service is configured to allow delegation:

TGS-REQ, KDC confirm the service is suitable as a delegate.

4. Since the client verified that the target service is enabled for delegation, it will send a new TGS-REQ request for an additional Service Ticket (Elliot.A`s TGT), again sending its TGT and Authenticator, but this time it will ask for a copy of his TGT with the flag forwarded set:

TGS-REQ, client asks for a forwarded TGT.

The ticket-granting service (TGS) then provides a (delegated) TGT with the forwarded flag, the KDC expects this request as a follow-up of the previous one, as the service is configured with Unconstrained Delegation:

TGS-REP, TGT with forwarded flag.

5. Once with a forwardable TGT, the client will perform an AP-REQ message within the HTTP request, sending the service ticket for the HTTP service in addition to the delegated TGT. The forwardable TGT is contained within the HTTP request:

HTTP request.

6. The web server, (DEV$), uses the client's cached TGT to request a Service Ticket from the KDC for the CIFS/FS1 on behalf of Elliot through a regular TGS-REQ message:

TGS-REQ regular TGS-REQ on behalf of Elliot.A.

The KDC provides a valid Elliot.A Service Ticket for CIFS/FS1 service to DEV$:

TGS-REP.

7. DEV$ will send an AP-REQ message via SMB on behalf of Elliot.A, sending the CIFS service ticket and the authenticator:

AP-REQ, mutual authentication.

FS1$, will in turn, perform a mutual authentication process with DEV$, via SMB, through an AP-REP message:

AP-REP, mutual authentication.

8. Finally, the client (Elliot.A) and DEV$ will complete the mutual authentication process via an AP-REP message over HTTP, resulting in displaying the FS1$ shared files that the client wanted to access:

AP-REP through HTTP, mutual authentication.
Abusing Unconstrained Delegation:

As we already know, any principal connecting to a machine configured with unconstrained delegation will drop a copy of its TGT in memory.

From a threat actors’ stance, the objective will be to gain privileged access to these servers and dump these delegated TGTs that are cached in memory. Alternatively, they/we can force the authentication of a privileged account against this machine, to obtain clients delegated TGTs.

From Linux, the first step will be adding a "fake" spn pointing to our listener, our attacker machine. To do this, addspn can be used, as machine accounts can edit their own msDS-AdditionalDnsHostName attribute.

Note: Depending on the chosen authentication coercion method (e.g., PrinterBug, Coercer), different types of services will be required.

addspn.py

It is also required to create a DNS record with the name of the fake registered spn pointing to the attacking machine, dnstool from @_dirkjan's krbrelayx suite can be used to perform this operation.

dnstool.py

Finally, krbrelayx will be used, in conjunction with the Kerberos key or the password and salt of the account configured with Unconstrained Delegation that has been compromised, forcing authentication with the chosen method. In this example, Coercer:

krbrelayx & coercer.

Once the delegated TGT (.ccache) is obtained, it can be exported and used to impersonate that coerced account:

secretsdump using delegated TGT.

From Windows computers, the Rubeus monitor function performs the same role. After forcing authentication with the desired method, the cached .kirbi in memory can be obtained and used for impersonation.

Constrained Delegation:

Kerberos Constrained Delegation, Introduced in Windows Server 2003, aims to provide a safer form of delegation that could be used by services.

While Unconstrained Delegation allows the impersonation of any domain principal on any network service, for accounts configured with Constrained Delegation, target services are limited by configuration. This approach enhances security by limiting the scope of delegation to only explicitly allowed services, thus minimizing potential exposure from compromised services.

In addition, to the above, in Constrained Delegation, it is not necessary to have a user's TGT within the TGS request. The service itself can request service tickets for other services on behalf of a user as long it has proof that it has received a request from the user.

For this purpose, Microsoft issued the S4U [MS-SFU] extension that allows the KDC to issue a new TGS to the service using a valid TGS through two subprotocols, S4U2Self and S4U2Proxy.

  • S4U2Self: allows a service to obtain a service ticket to itself on behalf of a user.
  • S4U2Proxy: allows a service to obtain a service ticket on behalf of a client to a different service using a service ticket as evidence that the client has authenticated.

Constrained delegation can be configured to accept only Kerberos authentication, or other protocols, such as NTLM, in scenarios where Kerberos authentication is not possible.

Kerberos Only:

A visual depiction of the Constrained Delegation configuration, using Kerberos only, can be seen in the image below. In this instance it is configured for only delegation allowed for FS1.lareslabs.local CIFS service:

Constrained Delegation configured on DEV.

Following the same scenario, but now, DEV$ configured with Constrained Delegation and Kerberos Only:

Constrained Delegation - Kerberos Only Flow.
Kerberos Only - Constrained Delegation.
  1. Kerberos pre-auth, usual flow, obtaining a TGT via AS-REQ/AP-REP messages.
  2. The client (Elliot.A) requests a service ticket for HTTP/DEV.lareslabs.local (TGS-REQ message). Then, the KDC provides the service ticket through the TGS-REP message.
  3. The client sends an HTTP AP-REQ message to the web server, including the service ticket within the request.
  4. Through the next TGS-REQ message sent by DEV$ to the KDC, the service can request additional service tickets using an S4U2Proxy request. Through this extension, the web server will send a request to the KDC for a CIFS/FS1.lareslabs.local service ticket sending its TGT and authenticator and the previous HTTP/DEV service ticket from Elliot.A, within the "additional tickets" block:
CIFS - TGS-REQ (S4U2Proxy).

In the following image, it is shown how, in the same request, the client (DEV$), requires the KDC to check if the constrained delegation is configured and viable:

TGS-REQ constrained delegation flag.

The KDC will check the additional ticket, verify that it is forwardable, and further verify that DEV$ can delegate to FS1$, by checking the msDS-AllowedToDelegateTo an attribute:

DEV$ msDS-AllowedToDelegateTo attribute.

Forwarding Elliot.A's CIFS/FS1 ST + Session Key through the TGS-REP response to DEV$:

TGS-REP (S4U2Proxy).

5. DEV$ requests CIFS access to FS1$ on behalf of Elliot.A through the AP-REQ message and FS1$ will reply to DEV$, performing the mutual authentication process via the AP-REQ message.

6. Finally, the web application will return the information retrieved for Elliot.A through an AP-REP message via HTTP (Mutual authentication between
the DEV$ and Elliot.A):

HTTP AP-REP.
Abusing KCD Kerberos Only:

Kerberos Only requires an additional ticket as a requirement to invoke S4U2Proxy. The first approach should be user S4U2Self but required TRUSTED_TO_AUTH_FOR_DELEGATION flag set, which is not True, since Kerberos Only was used and the resulting ST will be non-forwardable.

Although it is possible to abuse Kerberos Only through the “bronze bit” (CVE-2020-17049)  technique or by forcing authentication, the most direct method would be through RBCD.

To abuse this, it is first necessary to use an indirect method that exploits Resource Based Constrained Delegation first.

Protocol Transition:

In Constrained Delegation Kerberos Only, the service can invoke S4U2Proxy using Elliot.A’s service ticket (ST) as an additional ticket; however, if the service does not have this additional ticket, such as via NTLM authentication, it needs another way to obtain proof (a ticket) that the client is delegating on its behalf. In this case, a service can invoke S4U2Self to ask the authentication service to produce a TGS for arbitrary users to itself, which can then be used as “evidence” when invoking S4U2Proxy. This feature allows impersonating users on an ad-hoc basis, and it is only possible when the TrustedToAuthForDelegation flag is set for the service account that invokes S4U2Self.

In the following image, we can see a visual representation of the configuration of Constrained Delegation using protocol transition:

Protocol Transition - Constrained Delegation, DEV$.

Following the same scenario, with DEV$ configured with Constrained Delegation and Protocol Transition, the flow looks as follows:

Constrained Delegation - Protocol Transition Flow.
Protocol Transition - Constrained Delegation network traffic.

1. Client Elliot.A will send an HTTP request to the DEV web server. Authentication will be performed using NTLM since the application does not accept Kerberos:

NTLM authentication.

2. To get CIFS/FS1 service ticket (using the S4U2Proxy request) DEV$ has to provide a proof, a service ticket from the user. Since there is no service ticket provided by Elliot.A (NTLM authentication was used) the server should ask for it using the S4U2Self extension.

In the TGS-REQ message that DEV$ will send to the KDC, the structures specific to the S4U2Self extension, pA-FOR-X509-USER, and pA-FOR-USER are present, with the name as the client to impersonate (Elliot.A) and its’ own SPN, e.g. the DEV$ target spn:

TGS-REQ (S4U2Self)

The KDC will then verify that DEV is configured with constrained delegation enabled (TRUSTED_TO_AUTH_FOR_DELEGATION) and will provide a service ticket on behalf of Elliot.A with the forwardable flag set to True through the TGS-REP message to DEV$ server:

TGS-REP (S4U2Self)

3. DEV$ sends its TGT and authenticator to request a service ticket on behalf of Elliot.A for CIFS/FS1 to the KDC. This process, will include the forwardable service ticket previously obtained through S4U2Self:

S4U2Self TGS-REQ

The additional ticket (forwardable ST) is pointing to DEV$ instead of HTTP/DEV.lareslabs.local, confirming that it was obtained through S4U2Self:

S4U2Self TGS-REQ

In this request, the flags for constrained delegation and resource-based constrained delegation are set. This is significant because RBCD will serve as a fallback if constrained delegation fails, for example, if the additional ticket is not forwardable.

KCD and RBCD flags set

The KDC checks if DEV is authorized to delegate to FS1 by verifying the msDS-AllowedToDelegateTo attribute. It also ensures that the Additional Ticket is forwardable. If all checks are correct, the KDC responds with Ellio.A’s service ticket (ST) and the session key in the TGS-REP message:

TGS-REP

Note: An important fact here, and as @elad_shamir describes in his awesome = post Wagging the dog , in S4U2Proxy, the ticket should always be forwardable, and will have the bit "resource-based-constrained-delegation" to check if RBCD is configured in case KCD does not work, as a fallback (when the ticket is not forwardable for example).

KCD and RBCD flags set

5. DEV request CIFS access to FS1 on behalf of Elliot.A through the AP-REQ message and FS1 will reply to DEV, performing the mutual authentication process via the AP-REQ message.

6. Finally, the web application will return the information retrieved for Elliot.A through an AP-REP message via HTTP (Mutual authentication between
the DEV and Elliot.A):

DEV web application response
Abusing KCD Protocol Transition:

To abuse the KCD with any protocol (protocol transition), it is necessary to request a service ticket, using the S4U2self extension and then request the final TGS ticket using S4U2Proxy. On Linux, Impacket's getST handles all the work behind the scenes

impacket-getST
impacket-secretsdump

On Windows systems, Rubeus, through the S4U function, requests a Ticket Granting Ticket (TGT) on behalf of DEV using the specified Kerberos key. It then invokes S4U2Self to obtain a Service Ticket (ST) in the name of Lares.DA. The resulting  in a forwardable service ticket:

Then, Rubeus will use the forwardable ticket to invoke S4U2Proxy:

Once ptt is performed, the target user can be impersonated in the chosen service.

Resource-Based Constrained Delegation:

Resource-based constrained delegation, introduced in Windows Server 2012, operates similarly to classic constrained delegation by using S4U extensions; however, the key difference is that in RBCD, delegation is configured directly on the service receiving the delegated credentials, managed through the msDS-AllowedToActOnBehalfOfOtherIdentity attribute, rather than on the delegating service.

This attribute is a security descriptor that contains an ACE for every account the user can delegate to.

To configure Resource-Based Constrained Delegation (RBCD), it is necessary to use the command line, as there is no graphical interface available for this configuration:

RBCD configuration and enumeration

In the classic Constrained Delegation, the delegation is configured in DEV$ via msDS-AllowedToDelegateTo:

"Outgoing" Classic Constrained Delegation.

In contrast,RBCD is configured in FS1$, through the attribute msDS-AllowedToActOnBehalfOfOtherIdentity:

"Incoming" Resource-Based Constrained Delegation.

So, in case of compromising an account with enough privileges to write to that attribute (GenericAll/GenericWrite/WriteDacl/WriteProperty/...) of the target account, it would be possible to configure RBCD.

Note: If you need to review attacks related to ACLs, we recommend taking a look at our post:

The Phantom Menace: Exposing hidden risks through ACLs in Active Directory
The abuse of misconfigured Access Control Lists is nothing new. However, it is still one of the main ways of lateral movement and privilege escalation within an active directory domain.
RBCD (Protocol Transition) network traffic

1. Client Elliot.A will send an HTTP request to the DEV web server. Authentication will be performed using NTLM since the application does not support Kerberos.

2. To obtain a service ticket for CIFS (using the S4U2Proxy request), DEV$ needs to provide proof of a service ticket from the user. Since Elliot.A did not provide a service ticket (due to NTLM authentication), the server will request it using the S4U2Self extension.

In the TGS-REQ message that the DEV server sends to the KDC, the S4U2Self-specific structures pA-FOR-X509-USER and pA-FOR-USER are included. These structures specify Elliot.A as the client to impersonate and DEV$ as the target SPN:

TGS-REQ (S4U2Self)

The KDC responds with the Elliot.A´ST and the session key:

TGS-REP (S4U2Self)

3. DEV sends its own TGT, an authenticator, and the S4U2Self additional ticket, asking the KDC for an CIFS/FS1 service ticket:

TGS-REQ (S4U2Proxy)

Here is the difference from the classic constrained delegation, the KDC checks:

  • If FS1$ is listed in DEV$ msDS-AllowedToDelegateTo. Since RBCD has been configured and not KCD , Constrained Delegation cannot be applied.
  • Then, as a "fallback" if DEV$ is listed in msDS-AllowedToActOnBehalfOfOtherIdentity attribute of FS1$. RBCD can be applied.

4. DEV request CIFS access to FS1 on behalf of Elliot.A through the AP-REQ message and FS1 will reply DEV, performing the mutual authentication process via the AP-REQ message.

5. Finally, the web application will return the information retrieved for Elliot.A through an AP-REP message via HTTP (Mutual authentication between
the DEV and Elliot.A).

Abusing RBCD:

Having write rights on the target msDS-AllowedToActOnBehalfOfOtherIdentity attribute enables the configuration of Resource-Based Constrained Delegation (RBCD). Exploiting this trust requires an account capable of invoking both S4U2Self and S4U2Proxy, though, fortunately, any account with a configured SPN can invoke these extensions, allowing for the impersonation of any user against the services tied to the affected service account.

The quickest way is to create a machine account, as demonstrated in the following example is by using PowerMad:

Note: James Forshaw discovered a way to abuse RBCD by using a user without SPN, but the account would be unusable. So in the following scenario, we used the traditional method, creating a machine account with MachineAccountQuota set to the default value (10).

Powermand New-MachineAccount

Next, the msDS-AllowedToActOnBehalfOfOtherIdentity attribute in DEV will be edit with the new machine account:

Editing msDS-AllowedToActOnBehalfOfOtherIdentity using Powerview

Rubeus s4u function requests the delegated ST, specifying an SPN pointing to our target and a user to impersonate, using the new machine account credentials:

Rubeus S4u

The resulting ticket will be forwardable:

Rubeus describe function

Use that service ticket as proof of authentication in S4U2Proxy for FS1$:

Rubeus SF4 and PTT

Through pass-the-ticket, access to the target service will be granted:

PTT access granted

From Linux, the process would be the same, easily replicable using impacket, addcomputer, rbcd.py and getST.

Useful Defenses:
  • Enable AccountNotDelegated (Account is sensitive and cannot be delegated) UAC flag on your Tier-0 accounts.
  • Identify all the servers that have delegation configured and change to constrained delegation whenever possible.
  • Add user accounts to the Protected Users Security Group.
  • Sigma rule for unconstrained delegation (PS-Enumeration).
  • Events: 4769 - 5156 - 4624 - 5140 - 5145 - 4675 - 4672 - 4673- 4611- 5136.
  • Techniques - T1589.002. , T1018, T1550.003, T1078.002, T1078.002, T1589.002.

We hope you enjoyed this Kerberos series, and that the series will continue to serve as a reference for your Kerberos queries.
Finally, thank you for reading it!

END.

Resources