Kerberos "Double Hop" Problem
This page is only informational. Bottom two sections are directly copied from HTB Academy.
The Kerberos Double Hop problem is a common issue related to authentication in multi-tiered applications that use the Kerberos protocol. It's called the "Double Hop" problem because it involves two "hops" of authentication, and Kerberos, by design, doesn't easily handle the second hop. Let’s break it down step by step:
What is Kerberos?
Kerberos is a network authentication protocol that uses tickets to allow nodes to prove their identity in a secure manner over an insecure network. It's widely used in Active Directory environments. In simple terms, a client (like a user) requests access to a service (like a web server), and Kerberos issues a ticket that proves the client is authenticated and authorized to access the service.
Understanding the "Hops"
In multi-tiered environments, there are often multiple servers that a user may interact with. For example:
First hop: A user connects to Server A (e.g., a web server).
Second hop: Server A needs to connect to Server B (e.g., a database server) on behalf of the user to fetch data or perform another action.
This is where the "double hop" problem arises.
How Kerberos Authentication Works (First Hop)
User Authenticates to Server A: When the user tries to access Server A, they request a Kerberos ticket from the Key Distribution Center (KDC). The ticket allows them to prove their identity to Server A.
Server A Validates the Ticket: Server A accepts the Kerberos ticket and authenticates the user.
At this point, Kerberos works perfectly. The user is authenticated to Server A.
The Double Hop Problem (Second Hop)
Now, Server A needs to act on behalf of the user and access Server B (for example, a database). But Server A doesn’t have the user’s credentials to present to Server B, because Kerberos tickets are tied to the user and not meant to be reused by intermediate servers. This creates the Double Hop problem.
Server A Tries to Access Server B: Server A now needs to authenticate to Server B (the second hop). But the issue is that Server A doesn't have the necessary credentials (i.e., the user’s Kerberos ticket) to forward to Server B.
Server B Denies Access: Since Server A cannot provide valid credentials for the user, Server B denies access, and the chain of requests breaks down. The result is the second hop failing.
Why Does This Happen?
Kerberos uses a feature called Delegation, but by default, it does not automatically delegate or forward user credentials from one server to another for security reasons. This limitation prevents Server A from impersonating the user when accessing Server B, thus causing the "Double Hop" problem.
Solutions to the Kerberos Double Hop Problem
There are a few methods to solve this issue, and they depend on the level of trust and security required in the environment:
Kerberos Delegation: This allows a server (in this case, Server A) to obtain a ticket to act on behalf of the user when connecting to other services (like Server B).
Constrained Delegation: This limits what services a server can access on behalf of the user. It’s more secure because you specify exactly which services are allowed to be accessed by Server A.
Unconstrained Delegation: This allows Server A to access any service on behalf of the user. This is less secure because it gives Server A broad access.
Credential Delegation (CredSSP): This allows the user’s credentials to be passed securely from Server A to Server B during authentication, so the second hop is successful.
Use NTLM instead of Kerberos: In some environments, using NTLM (a less secure protocol) can bypass the Kerberos Double Hop issue, but this is not recommended as it weakens security.
If unconstrained delegation is enabled on a server, it is likely we won't face the "Double Hop" problem. In this scenario, when a user sends their TGS ticket to access the target server, their TGT ticket will be sent along with the request. The target server now has the user's TGT ticket in memory and can use it to request a TGS ticket on their behalf on the next host they are attempting to access. In other words, the account's TGT ticket is cached, which has the ability to sign TGS tickets and grant remote access. Generally speaking, if you land on a box with unconstrained delegation, you already won and aren't worrying about this anyways.
Lets look at 2 workarounds:
PSCredential Object
We can also connect to the remote host via host A and set up a PSCredential object to pass our credentials again. Lets see first how it fails:
After connecting to a remote host with domain credentials, we import PowerView and then try to run a command. As seen below, we get an error because we cannot pass our authentication on to the Domain Controller to query for the SPN accounts.
*Evil-WinRM* PS C:\Users\backupadm\Documents> import-module .\PowerView.ps1
|S-chain|-<>-127.0.0.1:9051-<><>-172.16.8.50:5985-<><>-OK
|S-chain|-<>-127.0.0.1:9051-<><>-172.16.8.50:5985-<><>-OK
*Evil-WinRM* PS C:\Users\backupadm\Documents> get-domainuser -spn
Exception calling "FindAll" with "0" argument(s): "An operations error occurred.
"
At C:\Users\backupadm\Documents\PowerView.ps1:5253 char:20
+ else { $Results = $UserSearcher.FindAll() }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DirectoryServicesCOMExceptionIf we check with klist, we see that we only have a cached Kerberos ticket for our current server.
*Evil-WinRM* PS C:\Users\backupadm\Documents> klist
Current LogonId is 0:0x57f8a
Cached Tickets: (1)
#0> Client: backupadm @ INLANEFREIGHT.LOCAL
Server: academy-aen-ms0$ @
KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
Ticket Flags 0xa10000 -> renewable pre_authent name_canonicalize
Start Time: 6/28/2022 7:31:53 (local)
End Time: 6/28/2022 7:46:53 (local)
Renew Time: 7/5/2022 7:31:18 (local)
Session Key Type: AES-256-CTS-HMAC-SHA1-96
Cache Flags: 0x4 -> S4U
Kdc Called: DC01.INLANEFREIGHT.LOCALSo now, let's set up a PSCredential object and try again. First, we set up our authentication.
*Evil-WinRM* PS C:\Users\backupadm\Documents> $SecPassword = ConvertTo-SecureString '!qazXSW@' -AsPlainText -Force
|S-chain|-<>-127.0.0.1:9051-<><>-172.16.8.50:5985-<><>-OK
|S-chain|-<>-127.0.0.1:9051-<><>-172.16.8.50:5985-<><>-OK
*Evil-WinRM* PS C:\Users\backupadm\Documents> $Cred = New-Object System.Management.Automation.PSCredential('INLANEFREIGHT\backupadm', $SecPassword)Now we can try to query the SPN accounts using PowerView and are successful because we passed our credentials along with the command.
*Evil-WinRM* PS C:\Users\backupadm\Documents> get-domainuser -spn -credential $Cred | select samaccountname
|S-chain|-<>-127.0.0.1:9051-<><>-172.16.8.50:5985-<><>-OK
|S-chain|-<>-127.0.0.1:9051-<><>-172.16.8.50:5985-<><>-OK
samaccountname
--------------
azureconnect
backupjob
krbtgt
mssqlsvc
sqltest
sqlqa
sqldev
mssqladm
svc_sql
sqlprod
sapsso
sapvc
vmwarescvcIf we try again without specifying the -credential flag, we once again get an error message.
get-domainuser -spn | select
*Evil-WinRM* PS C:\Users\backupadm\Documents> get-domainuser -spn | select samaccountname
|S-chain|-<>-127.0.0.1:9051-<><>-172.16.8.50:5985-<><>-OK
|S-chain|-<>-127.0.0.1:9051-<><>-172.16.8.50:5985-<><>-OK
Exception calling "FindAll" with "0" argument(s): "An operations error occurred.
"
At C:\Users\backupadm\Documents\PowerView.ps1:5253 char:20
+ else { $Results = $UserSearcher.FindAll() }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DirectoryServicesCOMExceptionIf we RDP to the same host, open a CMD prompt, and type klist, we'll see that we have the necessary tickets cached to interact directly with the Domain Controller, and we don't need to worry about the double hop problem. This is because our password is stored in memory, so it can be sent along with every request we make.
Note: klist command will show all cached creds in PowerShell.
Register PSSession Configuration
We've seen what we can do to overcome this problem when using a tool such as evil-winrm to connect to a host via WinRM. What if we're on a domain-joined host and can connect remotely to another using WinRM? Or we are working from a Windows attack host and connect to our target via WinRM using the Enter-PSSession cmdlet? Here we have another option to change our setup to be able to interact directly with the DC or other hosts/resources without having to set up a PSCredential object and include credentials along with every command (which may not be an option with some tools). Let's start by first establishing a WinRM session on the remote host.
Enter-PSSession -ComputerName ACADEMY-AEN-DEV01.INLANEFREIGHT.LOCAL -Credential inlanefreight\backupadmIf we check for cached tickets using klist, we'll see that the same problem exists. Due to the double hop problem, we can only interact with resources in our current session but cannot access the DC directly using PowerView. We can see that our current TGS is good for accessing the HTTP service on the target since we connected over WinRM, which uses SOAP (Simple Object Access Protocol) requests in XML format to communicate over HTTP, so it makes sense.
[ACADEMY-AEN-DEV01.INLANEFREIGHT.LOCAL]: PS C:\Users\backupadm\Documents> klist
Current LogonId is 0:0x11e387
Cached Tickets: (1)
#0> Client: backupadm @ INLANEFREIGHT.LOCAL
Server: HTTP/ACADEMY-AEN-DEV01.INLANEFREIGHT.LOCAL @ INLANEFREIGHT.LOCAL
KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
Ticket Flags 0x40a10000 -> forwardable renewable pre_authent name_canonicalize
Start Time: 6/28/2022 9:09:19 (local)
End Time: 6/28/2022 19:09:19 (local)
Renew Time: 0
Session Key Type: AES-256-CTS-HMAC-SHA1-96
Cache Flags: 0x8 -> ASC
Kdc Called:We also cannot interact directly with the DC using PowerView
[ACADEMY-AEN-DEV01.INLANEFREIGHT.LOCAL]: PS C:\Users\backupadm\Documents> Import-Module .\PowerView.ps1
[ACADEMY-AEN-DEV01.INLANEFREIGHT.LOCAL]: PS C:\Users\backupadm\Documents> get-domainuser -spn | select samaccountname
Exception calling "FindAll" with "0" argument(s): "An operations error occurred.
"
At C:\Users\backupadm\Documents\PowerView.ps1:5253 char:20
+ else { $Results = $UserSearcher.FindAll() }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DirectoryServicesCOMExceptionOne trick we can use here is registering a new session configuration using the Register-PSSessionConfiguration cmdlet.
PS C:\htb> Register-PSSessionConfiguration -Name backupadmsess -RunAsCredential inlanefreight\backupadm
WARNING: When RunAs is enabled in a Windows PowerShell session configuration, the Windows security model cannot enforce
a security boundary between different user sessions that are created by using this endpoint. Verify that the Windows
PowerShell runspace configuration is restricted to only the necessary set of cmdlets and capabilities.
WARNING: Register-PSSessionConfiguration may need to restart the WinRM service if a configuration using this name has
recently been unregistered, certain system data structures may still be cached. In that case, a restart of WinRM may be
required.
All WinRM sessions connected to Windows PowerShell session configurations, such as Microsoft.PowerShell and session
configurations that are created with the Register-PSSessionConfiguration cmdlet, are disconnected.
WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Plugin
Type Keys Name
---- ---- ----
Container {Name=backupadmsess} backupadmsessOnce this is done, we need to restart the WinRM service by typing Restart-Service WinRM in our current PSSession. This will kick us out, so we'll start a new PSSession using the named registered session we set up previously.
After we start the session, we can see that the double hop problem has been eliminated, and if we type klist, we'll have the cached tickets necessary to reach the Domain Controller. This works because our local machine will now impersonate the remote machine in the context of the backupadm user and all requests from our local machine will be sent directly to the Domain Controller.
PS C:\htb> Enter-PSSession -ComputerName DEV01 -Credential INLANEFREIGHT\backupadm -ConfigurationName backupadmsess
[DEV01]: PS C:\Users\backupadm\Documents> klist
Current LogonId is 0:0x2239ba
Cached Tickets: (1)
#0> Client: backupadm @ INLANEFREIGHT.LOCAL
Server: krbtgt/INLANEFREIGHT.LOCAL @ INLANEFREIGHT.LOCAL
KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
Ticket Flags 0x40e10000 -> forwardable renewable initial pre_authent name_canonicalize
Start Time: 6/28/2022 13:24:37 (local)
End Time: 6/28/2022 23:24:37 (local)
Renew Time: 7/5/2022 13:24:37 (local)
Session Key Type: AES-256-CTS-HMAC-SHA1-96
Cache Flags: 0x1 -> PRIMARY
Kdc Called: DC01We can now run tools such as PowerView without having to create a new PSCredential object.
[DEV01]: PS C:\Users\Public> get-domainuser -spn | select samaccountname
samaccountname
--------------
azureconnect
backupjob
krbtgt
mssqlsvc
sqltest
sqlqa
sqldev
mssqladm
svc_sql
sqlprod
sapsso
sapvc
vmwarescvcNote: We cannot use Register-PSSessionConfiguration from an evil-winrm shell because we won't be able to get the credentials popup. Furthermore, if we try to run this by first setting up a PSCredential object and then attempting to run the command by passing credentials like -RunAsCredential $Cred, we will get an error because we can only use RunAs from an elevated PowerShell terminal. Therefore, this method will not work via an evil-winrm session as it requires GUI access and a proper PowerShell console. Furthermore, in our testing, we could not get this method to work from PowerShell on a Parrot or Ubuntu attack host due to certain limitations on how PowerShell on Linux works with Kerberos credentials. This method is still highly effective if we are testing from a Windows attack host and have a set of credentials or compromise a host and can connect via RDP to use it as a "jump host" to mount further attacks against hosts in the environment. .
Last updated
Was this helpful?