LSA Whisperer

Evan McBroom
Posts By SpecterOps Team Members
27 min readApr 17, 2024

--

Thank you to SpecterOps for supporting this research, to Elad for helping draft this blog, and to Sarah, Daniel, and Adam for proofreading and editing! Crossposted on GitHub.

What follows is the culmination of two years of research with funding by SpecterOps and contributions from many of my coworkers.

Special thanks are needed to Elad, Lee, Will, Daniel, and Kai. Elad, Lee, and Will have contributed several ideas to the project, which are documented here, and have each spent multiple days testing the tool. Daniel has answered all of my inevitable questions about AzureAD (whoops, now Entra ID! It was AzureAD when I started 🙂) and OAuth 2.0. Kai helped both research LSA’s cloudap authentication package (AP) and add kerberos package support to the tool. To each, thank you!

The LSA Whisperer project focuses on interacting with “Authentication Packages’’ using their individual message protocols. APs are Security Support Provider (SSP) DLLs that LSA loads to implement a specific authentication logic design (e.g., local logons). An SSP may also be called a Security Package (SP) if it implements a security protocol (e.g., NTLM), but any use of the word “package” in this blog will only be referring to the AP functionality of an SSP.

Diary of a Developer

The project that is about to be described has been a journey to completion, so please allow me to explain how it all began.

Summer, 2022

SpecterOps does a series of internal technical talks presented by coworkers and hosted by Cody Thomas. During one of these talks, Lee Chagolla-Christensen showed some work he did to trace how browsers requested SSO cookies from an LSA package named cloudap. He was interested in making the same request programmatically, but he had not had an opportunity yet to investigate how.

The idea of requesting an SSO cookie directly was enticing, so I offered to help. During the break times while teaching SpecterOps’s Tradecraft Analysis course, we managed to get some interaction occurring with cloudap via the LsaCallAuthenticationPackage Win32 API. We had figured out how to request cloudap to refresh a primary refresh token (PRT) for a user, but we had not yet figured out how to recover a user’s SSO cookie. The successful interaction was exciting nonetheless. Auditing cloudap in Ghidra also allowed us to stumble on a wealth of functionality, known as “calls”, that we could use to interact with the package in other ways.

After that, I started working on a command-line interface (CLI) utility to interact with the cloudap calls we found interesting. There was no consideration of the potential offensive use case for a tool at that time. I was simply interested in what calls cloudap offered and being able to call the useful ones myself. Daniel Heinsen was extremely helpful during this process, answering my many questions regarding all things Entra ID and OAuth 2.0.

Fall, 2022

Soon after Elad Shamir rejoined SpecterOps. He had heard I was doing some work with LSA, so we talked about an authentication flow he was investigating with the kerberos package. Specifically, when the kerberos package recovers an NT OWF hash from a decrypted PAC, kerberos will store the hash in a logon session managed by the msv1_0 package using msv1_0’s internal CacheLogon call. He was interested to see if we could make the same CacheLogon call ourselves to Pass-the-Hash (PtH) with on-host tooling. The only alternative with on-host tooling is to use Mimikatz or a similar tool to modify LSASS’s memory.

The idea was fantastic and I began auditing CacheLogon to see how to make the call. We found that the approach is indeed possible, but we ultimately encountered the roadblock in that msv1_0 restricts the CacheLogon call to only clients that are the LSASS process itself. We noticed several other msv1_0 calls that seemed intriguing, though, and I started working on an equivalent msv1_0 CLI utility for interacting with the calls we were interested in.

During this time we discovered the fact that the GetCredentialKey call for msv1_0 would return a subset of the primary credentials for an msv1_0 managed logon session. These are derivations of a user’s plaintext password (such as an NT hash) that msv1_0 stores to allow it to later perform various actions for the user. We did not fully understand the implication of these credentials at the time, but we knew they were used in some way with the Data Protection API (DPAPI). We also could not find anyone documenting this same call which was surprising and encouraged us to keep going and investigate other packages as well.

NOTE: “Credential Keys” is Microsoft’s term for a one-way function (OWF) hash or a secure credential hash of a user’s password. The appropriate key may be used to decrypt a DPAPI master key. Will Schroeder has added cred key support to SharpDPAPI to weaponize this output. Thank you, Will!

Around this time Lee joked:

“Why don’t you make a CLI utility for kerberos? Actually, why not make one for all the packages?”

The idea did not seem too far-fetched to implement, so the utilities got merged and additional work began on making kerberos calls. Elad came up with a name that everyone liked, and thus LSA Whisperer was born.

Spring, 2023

The whole company met for our yearly retreat in early 2023. By this time, I had reversed the names of all the calls for all packages that currently ship with Windows. I had identified that cloudap loaded plugins, that these plugins supported calls as well, and had reversed the names of each of those calls. I had also reversed the Security Package Manager (SPM), which manages LSA packages overall, identified that it too supported calls, and identified their names.

The full list of available calls was overwhelming. I sat down with Elad, Lee, and Will during the retreat to prioritize it into a smaller list of calls we wanted to learn more about. Kai Huang also joined the project around this time to help research calls in the larger packages, namely cloudap and kerberos. The prioritized list and extra help made the project much more manageable.

Fall, 2023

In Q4 of 2023, SpecterOps funded me to work the entire quarter on developing LSA Whisperer. Although it’s common for SpecterOps to give employees work time and other resources for their personal projects, I understand this is uncommon for the industry and I am grateful to SpecterOps for your support.

By the start of Q4, the project was well organized. The tool could already make many calls to several packages, all research was documented in a project wiki, and the work plan and milestones for the quarter were outlined in a formal research plan. Progress accelerated during this time, and the tool gained partial or full support for 12 cloudap calls, 10 cloudap AzureAD plugin calls (Microsoft uses Entra ID’s previous name for the plugin), 21 kerberos calls, 1 livessp call, 1 msv1_0 call (many were previously implemented), 4 negoexts calls, 2 pku2u calls, 2 schannel calls, and 2 spm calls. Lee and Will graciously spent many days at the end of the quarter testing each of these new calls to confirm or disprove our ideas for their potential use cases.

Three bugs were also identified during this time, which were all responsibly disclosed to the Microsoft Security Response Center (MSRC). Two were arbitrary pointer dereference issues with the LSASS process for NT 6.4 and higher. These require the SeTcbPrivilege privilege, which reduces the potential for abuse, and MSRC ultimately considered both to be by design. The third was a memory leak in LSASS triggerable by any logged-on user for at least NT 5.1 and higher. The third bug was assigned CVE-2024–26209, was fixed on April 9th, 2024, and is now available via security updates to all end users. MSRC was quick to respond and pleasant to work with for all of these issues which I appreciate. For those interested, a description and proof of concept (POC) for each of these issues may be found on a GitHub project where I share various POCs that I author.

Spring, 2024

I had the opportunity to present LSA Whisperer at the annual SpecterOps Conference (SOCON) in Arlington, VA. There was a large attendance and the feedback afterwards made us feel that everything went well! Thank you to everyone who was able to attend the talk. Your presence and interest made presenting a joy!

I would like to thank my colleagues for all of the support leading up to and during the presentation. The talk would not have gone as smooth without it. Lastly, the presentation was a combination of my own ideas and those of many of my colleagues including Elad, Lee, and Will. It was an honor to be able to present the culmination of everyone’s work, thank you!

Now that brings us to the present: April, 2024. The security update has been released and with that, we may release our tooling.

Project Overview

If you would like, you are welcome to skip this guide and go directly to the main page for the project. The main page describes how to build our tooling — LSA Whisperer, while the project wiki describes how to use the tool, how it works, and what authentication package functionality it supports. The wiki is large so here we provide a more gentle introduction to these same topics.

Security Support Providers

As described in the blog’s introduction, SSPs are DLLs loaded by LSA that implement a specific authentication logic design (e.g., local logons) or security protocol (e.g., NTLM). The below table provides a summary of the SSPs that have been released by Microsoft over the years. LSA Whisperer supports interacting with a subset of shown SSPs that are marked as being an authentication package (AP). Many of these SSPs implement a security protocol and, as such, may also be referred to as a security package (SP) but for the purposes of this blog we will only be focusing on the AP functionality of these SSPs.

Package Calls

Each AP has the option to support receiving, processing, and responding to arbitrary requests from other software. These requests are referred to as “package calls.” Making a package call is officially supported for both user mode and device driver software through the use of the LsaCallAuthenticationPackage API.

Aside from wdigest, all APs that currently ship with Windows support handling package calls. APs use this feature to support custom functionality that is not otherwise available through the standard SSP function table that an AP must implement. For years, red teams and threat actors have abused some of this custom functionality (e.g., to recover Kerberos tickets).

Despite a few of these package calls being well known, we noticed that the overwhelming majority of them have not been documented. Our interest is in identifying all of the custom functionality that each AP supports through making package calls because we theorized that more would be abusable during an offensive operation. If that has piqued your interest, read on!

Techniques

During our research, we documented the package calls that are available for all APs that currently ship with Windows. We narrowed these down to a subset which we believe to be useful and implemented support for the majority of this subset into LSA Whisperer.

Here are the highlights of the package calls that may be made on a given host. All package calls not mentioned in these highlights may be found on the project wiki. If you are interested in a package call that is not implemented or you see an area of the wiki that can be improved, please submit an issue on GitHub or consider making a pull request (PR) for the project.

Kerberos

We will begin with the kerberos package as its package calls are more well-known in the industry. The first thing of note, as with many APs, is the ability to use package calls to gather host enumeration data. With the kerberos package you can show currently cached Kerberos tickets, domain extended policies (e.g., “Is FAST Armouring enabled?”), and how Kerberos is configured on that host.

Of most interest to people is likely the package’s ability to recover Kerberos tickets for active logon sessions or possibly its ability to cache a given Kerberos ticket to use on the host: a technique known as “Passing the Ticket’’ (PTT). Of less importance, but still of interest to offensive operations, is the package’s ability to purge tickets, bind a domain name, and a pin domain name.

Ticket purging is helpful if you have done the PTT technique, the ticket you used is no longer operationally useful, and you need to remove it from your cache. Although most red team tools only support purging all tickets in your cache, we identified that the package allows you to selectively purge tickets, which provides the potential for a significant quality of life improvement for these red team tools. Domain name binding can help with performing the Golden Ticket technique as identified by Martial Puygrenier while domain name pinning has been shown by James Forshaw and Nick Landers to allow for logical abuses of Kerberos. In their case, they showed a local privilege escalation (LPE) to SYSTEM but people should look for other potential abuses of domain pinning as well 🙂

All kerberos package calls are documented on the kerberos page of the wiki. The relevant calls for the techniques presented in this section include:

  • AddBindingCacheEntry
  • PinKdc / UnpinAllKdc
  • PrintCloudKerberosDebug
  • Query/Purge BindingCache
  • Query/Purge KdcProxyCache
  • Query/Purge TicketCache[Ex|Ex2|Ex3]
  • QueryDomainExtendedPolicies
  • QueryS4U2ProxyCache
  • Retrieve[Encoded]Ticket
  • SubmitTicket

Cloud AP

Next up is cloudap which manages Entra ID, AD FS, and Microsoft account logons. As with kerberos, cloudap offers functionality for gathering basic information about the host and logon sessions managed by cloudap, such as if a partial TGT, also known as the “cloud to on-premises TGT”, is present in a local cache.

While this information is helpful for troubleshooting logon issues, what is more interesting are the individual plugins that cloudap loads. Cloudap currently has two plugins. The first, known as AzureAD or AAD, manages Entra ID and AD FS logons. The second, known as Windows Live ID or MSA, manages Microsoft Account logins.

The main thing of interest with these plugins is that they can be used to request a single sign-on (SSO) cookie to authenticate to an identity provider (IDP) as a target user. The AAD plugin allows you to request an SSO cookie for Entra, AD FS, and an SSO cookie for your current device itself. Although we have not made a successful request yet to MSA, we are confident we will be able to request an SSO cookie for Microsoft Accounts from it as well because we have successfully done so with the legacy livessp AP that it is based on.

Of less immediate impact but still of importance are the additional host enumeration options that the AAD plugin specifically offers. Red team tools typically gather cloud information using the NetGetAadJoinInformation Win32 API but this function notably lacks SSO information that can be gathered from other CLI tools such as dsregcmd. The missing SSO information from this API may be gathered using the AAD plugin.

All calls for cloudap and the AAD plugin are documented on the cloudap page of the wiki. The relevant calls for the techniques presented in this section include:

  • GetAuthenticatingProvider
  • GetDpApiCredKeyDecryptStatus
  • GetPwdExpiryInfo
  • GetUnlockKeyType
  • IsCloudToOnPremTgtPresentInCache
  • AAD -> CreateSSOCookie
  • AAD -> CreateDeviceSSOCookie
  • AAD -> CreateEnterpriseSSOCookie (Enterprise refers to AD FS)
  • AAD -> DeviceValidityCheck

Microsoft Authentication Package V1.0

The last package that we will cover in this post is the msv1_0 but more are covered on the wiki! Msv1_0’s main purpose as an AP is to facilitate local machine logons. Being the oldest of the APs, it has gathered other responsibilities such as managing DPAPI credentials, sending Netlogon requests, and generating data for NTLM messages to support its 2nd role as the security package for NTLM.

Due to these additional responsibilities, msv1_0 was as fruitful in our findings as cloudap was in recovering credential material. The first thing we identified is that msv1_0 allows you to recover the DPAPI “credential key” of a logged-on user. The term credential key is described on the wiki and we plan to cover it further in a future blog, but if you recover a user’s credential key you can decrypt their DPAPI master key file. This process works even when Credential Guard is enabled.

The next thing we identified is that msv1_0 allows you to generate an NTLMv1 response with a chosen server challenge value. That works regardless of NTLMv1 being disabled using the LmCompatibilityLevel key in the registry (as needed by similar attacks such as Internal Monologue) but we believe it to not work if Credential Guard is enabled. The importance of being able to acquire an NTLMv1 response, especially with a chosen server challenge, is that it is trivial to crack to a usable NT hash for pass the hash (PtH) techniques and other tradecraft. Some online resources such as crack.sh will even crack these responses for free if the response has a specific server challenge which we can guarantee. The cracking process will also be instant for crack.sh due to its use of a rainbow table for these free requests.

All calls for msv1_0 plugin are documented on the msv1_0 page of the wiki. The relevant calls for the techniques presented in this section include:

  • GetCredentialKey
  • GetStrongCredentialKey
  • Lm20GetChallengeResponse

Dead Ends

Security researchers often celebrate their successes and hide the setbacks. I thought that, for a change, it would be good to share what didn’t work out as we had hoped. Most research efforts are unsuccessful and if we start sharing those as well, maybe it will encourage more people to try, and maybe others will be able to build on our unfinished work and ideas to overcome the obstacles that hindered our progress.

Transferring Credentials as an Alternative to Token Theft

Several packages have a TransferCreds call, which, as the name suggests, either transfers or duplicates the credential material from one logon session managed by the package into another one. Our idea for these calls was to use them as a new, alternative technique for Access Token Theft.

Token theft was introduced back in 2008 by Luke Jennings with the release of Incognito. It was a game-changing technique that allowed attackers to use the credentials of other logged-on users without targeting LSASS. Nowadays, many EDRs can detect this technique and its variants, but more importantly Windows will purge any credential material that is no longer being used by a process or thread after a user logs off, limiting the window of opportunity for this attack. In 2022, Will Schroeder released Koh, a tool that can force Windows in some circumstances to maintain credentials past when a user logs off to extend this window of opportunity.

We believed that the TransferCreds call had a similar potential for abuse. Our idea was for an attacker to either create a new logon session or use an existing “sacrificial” logon session they controlled, then transfer the credentials of a logon session for a target user into the attacker controlled session. That would allow an attacker to use a user’s credentials well after they logged off and without generating token duplication and impersonation activity which may be viewed as suspicious.

This call is available in the msv1_0, kerberos, cloudap, and in negotiate APs. The msv1_0 package prevents any process other than LSASS itself from making the call. Supporting this is outside the scope of the project because if you can execute code inside LSASS to make this call, then you already have the ability to directly recover credentials from LSASS’s memory and makes this call unnecessary.

The kerberos package allows other processes to make this call but it will remove all Kerberos tickets and Kerberos keys from the source logon session. That should not disrupt the user’s experience when accessing services that use Negotiate to authenticate because their logon session will fallback to using NTLM, but it would disrupt their ability to use services that only use Kerberos to authenticate. Once you transfer tickets to another session, you can transfer them back. So it may still offer value to attackers by allowing them to temporarily transfer tickets to a controlled logon session to use, possibly bypassing any token stealing, ticket dumping, or PTT tradecraft telemetry that would normally occur when stealing a user’s Kerberos tickets. If you have other ideas on how this call may be used, we would love to hear it!

We did not have time to investigate the behavior of cloudap’s implementation, but we believe it will have the same effect as the kerberos package. Lastly, the negotiate package’s implementation will simply forward the TransferCreds call to whichever package was used for a negotiated logon session making the previously mentioned issues all issues for the negotiate package as well.

Passing the Hash

NTLM is a built-in challenge-response authentication protocol on Windows. In the protocol, a client will prove their possession of a password by using a derivation of it, the NT hash, to either encrypt or generate an HMAC of a challenge given by the server they are attempting to connect to. The NT hash’s use as a key exposes the authentication flow to Pass-the-Hash (PtH) attacks in which an attacker may craft a valid response to a server challenge by knowing a user’s NT hash without knowing their cleartext password.

With the exception of Mimikatz, all the tools that implement the PtH attack do so by implementing an NTLM client to allow for client responses to be generated using an NT hash instead of a user’s password. Mimikatz offers the only alternative solution. Mimikatz’s approach is to create a new logon session on a Windows host, identify the location of that session’s NT hash in LSASS’s memory, then overwrite it with a specified NT hash. That forces any process that uses that new session to use the specified NT hash when authenticating to another host using NTLM. Mimikatz takes this approach because there are no Windows APIs that allow a logon session to be created using a specified NT hash. The approach may make LSASS unstable because it is modifying its memory but is the only on-target option when a networked based tool cannot be proxied on.

The msv1_0 package offers the CacheLogon call that allows a user to cache a specified NT hash into an active logon session. If a logon session has a cached NT hash, it will be used for NTLM authentication instead of the actual NT hash for the session’s user account. The kerberos package, as an example, uses this call in passwordless authentication scenarios. During these scenarios, such as smartcard logons or Windows Hello for Business, the PKINIT authentication scheme will be used and the domain controller (DC) will send an encrypted copy of the user’s NT hash to the kerberos package. The kerberos package will decrypt the user’s NT hash then use msv1_0’s CacheLogon call to store it for the newly created logon session to use.

We wanted to use the CacheLogon call in the same way the kerberos package does to have a logon session use a known NT hash for NTLM authentication. Doing so should provide a more stable solution than Mimikatz to do a PtH attack with on-host or non-proxied tooling because it does not require overwriting LSASS memory. There are also complementary calls that you can pair with CacheLogon, such as CacheLookup, which can inspect the current msv1_0 cache for a logon session, and ClearCachedCredentials, which you can use to remove a cached NT hash for a logon session when you are done using it. Unfortunately, just like the TransferCreds call, msv1_0 only allows the CacheLogon call to be made from the LSASS process itself. We are still interested in supporting these features for hosts where LSASS does not run as a PPL, such as a user-controlled VM that is proxied into a target network for a red team assessment, but we have not implemented them yet.

Bypassing “LSA Only” Checks with Passthrough Calls

We have mentioned three calls so far that can only be made by the LSASS process itself; namely the CacheLogon, CacheLookup, and TransferCred calls for msv1_0. When any of these calls are made, they will check whether the information for the client request is zero, which will only happen if the client is LSASS itself and, if the information is not zero, return with an error code of “access denied.”

We can implement a solution when LSASS is not running as a PPL by injecting code into LSASS that will make these calls from within the process, but we wanted to know if there was an alternative solution that would not require code injection. Indeed, there almost was!

In our investigation, we looked at an msv1_0 package call named GenericPassthrough. The GenericPassthrough call takes an arbitrary AP request and sends it using Netlogon to a DC for processing. The kerberos package, as an example, supports a VerifyPac call and domain joined hosts will send VerifyPac requests to a DC using a GenericPassthrough call to have the DC verify a Kerberos ticket’s PAC information.

An interesting thing we noticed is that when a host is not domain joined, msv1_0 will reissue any AP request it receives via GenericPassthrough back to the local LSA instance itself. That is done to support certain workgroup activities, but the important thing is that msv1_0 will view the final reissued AP request as coming from the LSASS process. We also believe msv1_0 can be tricked into believing the host is not domain-joined to reissue an AP request for us by temporarily stopping the Netlogon service and unsetting a specific named event that is set when the service is started.

Unfortunately, we found that even if we implemented the code to trick msv1_0 into reissuing an AP request for us to bypass the “LSA only” check that some calls make (e.g., msv1_0’s CacheLogon), we found that the final request would be considered a “passthrough request.” APs typically restrict passthrough requests from clients to only a small subset of the full list of AP requests a client could normally make. As an example, kerberos only allows VerifyPac as a passthrough request and msv1_0 only allows the SubAuth request to facilitate its “sub-authentication package” concept.

The key takeaway is that although it may be possible to trick msv1_0 into reissuing an AP request to bypass an “LSA only” check, we would not be able to use this approach to make any of the calls we are interested in: msv1_0’s CacheLogon, CacheLookup, and TransferCred calls. We still like to support these calls but currently plan on implementing a solution that requires injecting code into LSASS to do so.

Dumping NT Hashes

The GetCredentialKey and GetStrongCredentialKey calls for msv1_0 return a DPAPI “credential key” for a logon session. As mentioned in the technique section on msv1_0, we plan to cover credential keys more in a future blog. What is important to know, though, is that in some not-so-common situations the credential key is actually just the user’s NT hash!

When we first implemented these calls, we actually ran into this situation pretty reliably and kept getting the NT hash for our target user. We had no clue what a credential key was, that it had anything to do with DPAPI, and that it is uncommon for these calls to give a user’s NT hash. Instead, we thought we had found the ideal way to dump credential material from LSASS and we were excited.

Our initial understanding turned out too good to be true, but with more investigation these calls became exciting to us in a different way. We soon found that these calls instead produce the DPAPI credential key, which in those not-so-common edge cases happens to be the NT hash, but in all cases will be key material that you can use to decrypt the user’s DPAPI master key file. When this key is also the NT hash, one of the requirements for this edge case is that the account is local. That makes its recovery less important due to many alternative methods of extracting the same value from the local SAM.

Either way, we decided to take the recovery of a DPAPI credential key as a win and later, almost by happenstance, we identified msv1_0’s ability to generate NTLMv1 responses with a chosen server challenge value which, as described in the msv1_0 tradecraft section, is trivially crackable to an NT hash for the user. So with more work we eventually got to our goal, but it was not as straightforward as we originally thought.

Dumping Kerberos Keytabs

A keytab file stores Kerberos authentication keys for an account and they are typically used to automate user authentication without requiring user interaction. When we were investigating the kerberos package, we noticed it supported the RetrieveKeytab call.

We were hopeful we could use this call to generate a keytab file with Kerberos authentication keys for an account similar to Mimikatz’s sekurlsa::ekeys command. However, this call requires the caller to supply a password. The resultant keytab file will be populated with keys that are derived from that password instead of a password for an existing user account, making the call not too valuable to an attacker from our perspective.

Solving the Double Hop Problem

When a user logs on to a remote host (the first hop) and then from that remote host attempts to log on to yet another remote host (the second hop), authentication to the second hop normally fails. That is the Double Hop Problem.

The Double Hop Problem results from how authentication in Windows environments works, and it affects not only offensive operations but also legitimate system administration. The logon session to the first remote host (the first hop) is a “network logon” established using Kerberos or NTLM rather than with reusable credential material, such as a password or smartcard data. Therefore, the first hop host is not able to authenticate the user to the second hop. The first hop simply does not have reusable credential material to perform that action.

There are several legitimate solutions to this problem, including Kerberos Delegation, Credential Delegation (supported by CredSSP), and Remote Credential Guard. Although not originally designed to solve the Double Hop Problem, the two approaches that are typically used in red teams include the Kerberos Pass the Ticket (PTT) technique and creating a new logon session with explicit credentials. The first approach is already achieved through an AP package call, specifically the SubmitTicket call for kerberos. The second is not. Instead, it is achieved by using one of the LogonUser Win32 APIs which is what the Cobalt Strike’s make_token command and its derivatives use.

When investigating the package calls we could make, we noticed the ChangeCachedPassword call for msv1_0 and the AddExtraCredentials call for kerberos. We thought that either could potentially allow us to apply a known password to an existing logon session. Doing so would solve the Double Hop Problem when using known passwords without requiring the LogonUser APIs to be called. That would allow us to use a known password on a host without generating an event for a new logon session being created. But that, too, turned out to be unviable. From testing, ChangeCachedPassword appeared not to apply to network logons and the AddExtraCredentials package did not appear to affect subsequent authentication attempts.

Eventually we found msv1_0’s CacheLogon call, which may still achieve the same goal for us; namely, solving the Double Hop Problem when using a known password without generating a new logon session. That call is described further in the “Passing the Hash” section, but we believe that on a host without LSASS running as a PPL we can pre-convert a known password to an NT hash and pass it to a CacheLogon call to use in an existing logon session. The downside to this approach is that we would need to execute code in LSASS, which would likely create more telemetry than a single event for a new logon session.

Making Remote Calls

The SSP Interface (SSPI) RPC server that facilitates everything that LSA Whisperer does was designed to be used locally. The server itself only registers an ALPC port to listen on for its endpoint which is only locally accessible. A core design of RPC, though, is that the RPC runtime allows you to send RPC requests for any RPC interface registered by a process to any RPC endpoint the process is currently listening on and the runtime will fulfill that request.

Although the SSPI RPC server only registers a single ALPC port for an RPC endpoint, the LSASS process itself listens to many RPC endpoints, some of which are remotely accessible. We wanted to make these SSPI RPC requests to one of these endpoints on a remote host. Doing so would allow LSA Whisperer to do things such as dump Kerberos tickets, DPAPI credential keys, and SSO cookies on a remote host.

Wouldn’t that be nice? Well, (un)fortunately the SSPI RPC interface is registered with the RPC_IF_ALLOW_LOCAL_ONLY flag which forces the RPC runtime to only allow local RPC clients to interact with the interface. So, if you have ever wondered if you can dump Kerberos tickets on a remote host, as far as we know it is not possible–but we would be happy to be proven wrong! 🙂

Mitigations

There are almost no built-in options for an administrator to prevent the package calls that LSA Whisperer makes. LSA Whisperer only uses intended functionality for Windows, and blocking its package calls from succeeding would prevent normal Windows authentication processes from succeeding. That being said, enabling Credential Guard does block some of the DPAPI credential recovery commands and we believe also prevents NTLMv1 response generation using the primary credentials of an active logon session. One DPAPI credential recovery command is still available with Credential Guard enabled, but blocking the NTLMv1 response generation prevents attackers from easily recovering a usable NT hash for the user. Credential Guard may affect commands that we did not observe during testing. If you observe Credential Guard affecting a command which we missed please consider making a PR to the wiki.

There are also almost no built-in options for administrators to monitor for LSA Whisperer and similar tools. The largest potential is with monitoring via Event Tracing for Windows (ETW) which is used throughout all packages but not in a consistent or documented way. Additional assistance from Microsoft with guidance on which ETW providers to monitor for and with publishing the message schemas for each would go a long way toward enabling Administrators to monitor these calls.

Package calls are now facilitated by the SSPI RPC server, so we also investigated using RPC features as a method to monitor for or prevent specific package calls. There is an ETW provider named “RPC Events” that gives some insight into all RPC activity, including activity facilitating a package call, but in our opinion it does not provide enough insight to effectively monitor this activity. We investigated using Windows’s built-in RPC filters mechanism but it does not provide the granularity needed for effective monitoring or prevention of package calls. Lastly, we investigated using the RPC firewall, a popular FOSS third-party solution maintained by Zero Networks. Although the solution appears to be a promising solution for administrative needs, it does not prevent local RPC traffic and it is not compatible with LSA running as a PPL. The SSPI RPC server only allows local RPC traffic so the RPC firewall is not applicable for this need. Even if it did, allowing LSA to not run as a PPL would offer attackers the ability to use Mimikatz and related tools to directly recover credentials from LSASS’s memory, making the alternative approach that LSA Whisperer takes debatably unneeded.

Ultimately, our recommendations for administrators include:

  1. Enable LSASS to run as a PPL
  2. Enabling Credential Guard
  3. Log off machines when you are through using them
  4. Use Privileged Access Workstations/Devices and follow the Clean Source Principle

Enabling LSASS to run as a PPL does not affect LSA Whisperer but does make it more difficult for attackers to use other tools that directly recover credentials from LSASS’s memory. Credential Guard largely unaffected LSA Whisperer but we believe it will block LSA Whisperer’s ability to generate an NTLMv1 response with a primary credential for an active logon session. That prevents attackers from easily gaining a usable NT hash for a user. Lastly, a large portion of LSA Whisperer’s functionality is only usable when there is an active logon session in the first place for it to request credentials from. By fully logging off machines that you are done with, you will remove the resource that a large part of LSA Whisperer’s functionality uses. The last recommendation is a general architectural best practice for securing access to end user devices in the first place.

This summarizes our recommendations to administrators for what we believe directly affects LSA Whisperer but there are settings you may enable to further strengthen Credential Guard. These settings are documented on a dedicated page on Microsoft’s learning portal. Not all of these settings may be feasible for your organization to enable but we do recommend the ones that are.

EDR Recommendations

Endpoint Detection and Response (EDR) vendors, including Microsoft themselves, are likely in the best position to monitor for and prevent the use of LSA Whisperer and similar tools. These are what we recommend EDRs monitor for:

  1. Calls to the LsaCallAuthenticationPackage Win32 API
  2. Interaction with ALPC port lsasspirpc

Our belief is that most EDR vendors already monitor calls to LsaCallAuthenticationPackage but only for detecting Keberos ticket recovery and PTT techniques. Vendors should instead view this as an API that facilitates all of the functionality LSA Whisperer offers. Some attackers may choose to circumvent this API by interacting with the SSPI RPC server directly. The intended RPC endpoint for the SSPI RPC server is the lsasspirpc ALPC port. So we recommend that EDRs monitor for processes that interact directly with that endpoint instead of using the Win32 API. LSA Whisperer itself may be used to test this as it will interact with the ALPC directly by default.

Lastly, we recommend that EDRs prevent the following activity:

  1. Calls to LSASS RPC endpoints other than lsasspirpc for interface 4f32adc8–6052–4a04–8701–293ccf2096f0 (SSPI)

A well known technique to circumvent the monitoring of a specific RPC endpoint is to send the same RPC requests to any other endpoint hosted by the process running the RPC server. That works because the RPC runtime allows you to send RPC requests for any RPC interface registered by a process to any RPC endpoint the process is currently listening on and the runtime will fulfill that request. Attackers may use this approach to bypass additional monitoring of the lsasspirpc ALPC port but legitimate software will never do this. Instead, legitimate software will only interact with the lsasspirpc ALPC port using the documented Win32 API. So preventing all SSPI RPC requests to any RPC endpoint other than the lsasspirpc ALPC port should be safe to do and provide a true positive detection.

Conclusion

We touched on several topics in this post, but what I want you to take away is that most (and possibly all) of the credentials an attacker needs to accomplish their objectives on a Windows operation can be achieved without accessing LSASS’s memory. You do not even need to open a handle to LSASS. In the right situation, you can get what you want if you ask LSA nicely.

Do you need Kerberos tickets? Wait for the target user to login, impersonate the user or SYSTEM, and then ask LSA to give them to you using normal “ticket dumping” tradecraft. Do you instead need an NT hash for PtH tradecraft? Assuming Credential Guard is disabled, impersonate SYSTEM, request an NTLMv1 response for a logon session of the target user with a chosen server challenge, then crack it to a usable NT hash for the user. Do you instead need access to the plethora of user data encrypted with DPAPI? Impersonate SYSTEM, request the DPAPI credential key for a logon session of the target user, then use it with SharpDPAPI to decrypt their DPAPI master key file. Do your red team assessments now include cloud-based objectives? Impersonate a cloud-based user logon session then request LSA to give you a SSO cookie for that account.

If you like this work, please checkout the LSA Whisperer project on GitHub! The project also includes an extensive wiki for all of the research that was done. You can use the building guide in the project’s readme file and the usage guide on the project’s wiki to get started. For the SOCON presentation, the slides are available on Github and video will be available for free on SpecterOps’s YouTube channel once it is released. If you have a question, you can reach out to me in the BloodHoundGang Slack team or by starting a discussion on GitHub. I will do my best to respond to questions as I am able to. If you see an area where the project or wiki can be improved, please submit an issue on GitHub or consider making a pull request for the project. Your contributions are appreciated!

Lastly, thank you — the reader — for taking time to check out this work. It was a pleasure to do and I hope you enjoyed reading it.

Updates

  • Apr 19, 2024: Our original understanding for the purpose of the CacheLogon call was incorrect which Alex Short reached out to help clarify. Kerberos does not decrypt data before caching it. The data is only stored in the registry under HKLM\Security\Cache for later recovery by using the CacheLookup call. This is the same cache used by LsaApLogonUserEx2 to store user data for future logon attempts when Netlogon is unavailable. Lastly, the cached data is not used for NTLM authentication which removes these calls as a possibility for performing Pass the Hash. Thank you Alex for the help understanding these calls!

--

--