In part 1, we covered the prerequisite Windows internals knowledge to understand how the Mimikatz pass-the-hash (PtH) command is implemented. In this post, we begin reverse engineering the Mimikatz tool’s implementation of pass-the-hash.

What do we mean by “pass-the-hash”?

In the context of this post, pass-the-hash involves leveraging legitimate authentication mechanisms built into Windows. As we previously discussed, an access token has authentication material mapped to it in LSASS to implement the single sign-on functionality for network authentication in Windows.

In this context, the idea behind exploiting PtH is to modify this cached authentication material in LSASS to use an attacker-controlled credential instead of the current user’s NTLM hash for authentication to network resources. This technique allows an attacker to leverage a compromised user’s NTLM hash to perform authentication to remote hosts without knowing the account’s password. Overwriting stored authentication material enables an attacker to use builtin Windows commands (e.g., dir to list files on a remote host) as Windows will automatically use the NTLM hash specified by the attacker.

There exist alternative methods of leveraging the PtH technique, such as leveraging a custom implementation of the NT LAN Manager authentication protocol alongside custom implementations of SMB. One example of this is the Impacket utility [6]. Our discussion of pass-the-hash omits network and protocol-level implementation details.

What do we need to accomplish?

In part one, we mentioned that each process and thread has an access token associated with it. This access token specifies the process’s privileges on the local system, group information, and default DACL. We also discussed how an access token includes an authentication identifier that maps credentials cached in LSASS to an access token used when a process tries to interact with network resources such as file shares.

Mimikatz is capable of multiple modes of operation. If the end-user specifies the LUIDof the logon session, then Mimikatz overwrites the stored credential material for that session. If the operator specifies the username (using the /user option), then the Mimikatz tool will spawn a new process using the CreateProcessWithLogon function and overwrite the credential material associated with that logon. The Mimikatz process’s main thread will then use impersonation to impersonate that logon session using SetThreadToken.

Implementations of pass-the-hash

After reviewing previous research on this subject, there appear to be three primary methods for overwriting stored credentials in the LSASS process. Hernan Ochoa discusses these design tradeoffs extensively in two separate presentations, “Pass-The-Hash Toolkit for Windows Implementation & Use” [4] and “WCE Internals” [1].

DLL injection into LSASS

Ochoa outlines a pass-the-hash technique by injecting a DLL into LSASS and resolving and subsequently calling the relevant functions from within the injected DLL [1, Slide 23]. In this instance a signature is still used to identify the relevant functions (such as msv1_0.dll!NlpAddPrimaryCredential) [4, Slide 42].

Resolving structure addresses using the Microsoft symbol server

Ochoa outlines a second method he has used to implement pass-the-hash by using the Microsoft Symbol Server to resolve the address of LogonSessionList[1, Slide 45].  This technique makes it much easier for the attacker to locate the appropriate addresses. However, it requires both an outbound HTTP connection to the Microsoft symbol server and large non-default DLL files that are not ordinarily present within a default installation of Windows (such as symsrv.dll and dbghelp.dll) [1, Slide 47].

Scanning for structures using heuristic signatures

The method used by Mimikatz involves using signatures to identify the location of symbols within LsaSrv.dll, such as the LogonSessionList global variable. This method works by scanning for a signature to identify instructions used to load the address of variables such as LsaLogonSessionList into memory. This technique is described in detail in later sections.

Analyzing the three potential implementation methods

There are tradeoffs in each of these methods. The first method is likely the easiest to implement. However, it requires injecting a DLL into LSASS, which can be noticeable to endpoint security tools. The second method requires being able to query the Microsoft Symbol Server, which could introduce difficulties on systems without access to the internet. The third method is probably the best in terms of stealth and operational usage. However, it does appear to be the trickiest to implement, since the signatures will depend on the exact build of Windows running on the target system.

The developers of Mimikatz have used the signature-scanning method outlined by Ochoa. This method requires a significant amount of work to develop and maintain. When new versions of Windows are released, potential updates need to be made to adjust structure offset changes and changes to the source, which could otherwise break certain heuristics.

Understanding the signature scanning method

Mimikatz contains signatures specific to each major Windows release. These signatures can be leveraged as a heuristic to determine the location of two important structures; specifically, by scanning for a unique sequence of bytes within LsaSrv.dll. The first structure is the LogonSessionList and the second item is the LogonSessionListCount pointer. These structures are used to store a list of currently active Windows logon sessions.

The method employed by Mimikatz is quite ingenious. The idea behind this technique is that since both LogonSessionList and LogonSessionListCount are global variables, they can leverage a heuristic to identify instructions that reference these global variables. This is shown in the image given below. Mimikatz scans for the sequence of bytes outlined in black below in order to identify the mov r9d, cs:?LogonSessionListCount and lea rcx, ?LogonSessionList instructions. On the x86_64 architecture, these instructions use rip relative addressing to access variables relative to the value of the current instruction pointer using a given offset. These offsets are outlined in blue and green in little-endian notation:

The diagram given above is explained in more depth below:

  • Signature: The orange marker is the beginning of the pattern that Mimikatz searches for in LsaSrv.dll. The sequence of bytes that Mimikatz searches for on Windows 10, version 1803 is outlined in black in the image above. The first byte of this pattern is marked with a yellow box.
  • LogonSessionList: The gray bar marks the separator between the lea rsi instruction and the offset that is specified. On the x86_64 architecture, it is possible to reference addresses relative to the current value of the instruction pointer. In this instance, a 32-bit offset is specified in little endian notation. This offset is denoted by the green box. The red bar at address 0x80065941 is the address that rip points to when the offset is added to the current rip value.
  • LogonSessionListCount: The pink line denotes the separation between the bytes in the mov instruction. The bytes to the left of the pink bar specify that the destination register is the lower 32 bits of the r9 register. The blue box represents the offset relative to the instruction pointer that should be used to access the LogonSessionListCount variable in little endian notation.

In the following example, we calculate the address of LogonSessionList and LogonSessionListCount using the information obtained using this signature. The offset from rip is 0x107E3A and the address of rip is marked using an orange line (0x80065926). Adding these, we deduce that the address of LogonSessionListCount is 0x8016D760. We can verify this manually using IDA Pro as seen below:

In this instance, the Mimikatz developers used the WLsaEnumerateLogonSession function for their signature. However, if we examine LsaSrv.dll in IDA Pro, we can see several other candidate functions:

Pivoting From LogonSessionList

In this section, we will describe how Mimikatz pivots from LogonSessionList and LogonSessionListCount to overwrite the authentication information associated with the targeted logon session. This is done in the kuhl_m_sekurlsa_enum function, which accepts a callback function that is invoked when traversing the list of cached credential items.

This can be seen in the code given below from kuhl_m_sekurlsa_pth_luid. This uses the kuhl_m_sekurlsa_enum function to specify two callbacks (one for each authentication provider: MSV1_0 and Kerberos). Specifically, these are kuhl_m_sekurlsa_enum_callback_msv_pth and kuhl_m_sekurlsa_enum_callback_kerberos_pth.

When the kuhl_m_sekurlsa_enum function is invoked, it will loop through all of the entries in LogonSessionList using LogonSessionCount to determine the number of entries. Each of the entries themselves is a pointer to a LIST_ENTRY linked list containing one or more structures of type _KIWI_BASIC_SECURITY_LOGON_SESSION_DATA. The MSV1_0 callback is invoked and receives a copy of the structure along with a PSEKURLSA_PTH_DATA structure containing relevant information such as the target LogonId and the NTLM hash that is being used for the attack.

The purpose of this check is to verify that the correct LogonId has been found. A second function is invoked which handles the primary logic for implementing the pass-the-hash technique. This function (kuhl_m_sekurlsa_msv_enum_cred) invokes kuhl_m_sekurlsa_msv_enum_cred_callback_pth. Next, this function receives a structure of type KIWI_MSV1_0_PRIMARY_CREDENTIAL. This structure appears to be undocumented and was probably obtained through reverse engineering.

The following modifications are made to the credential structure:

  1. The LM, SHA, and DPAPI protected attributes are set to false with the length zeroed to indicate the credential structure does not contain these hash values.
  2. The field isNtOwfPassword is set to true to specify that an NTLM hash is present within the structure and the NtOwfPassword password is populated with the NTLM hash provided by the user.
  3. The credential data is then encrypted using LsaProtectMemory.

The primary work modifying the Kerberos structures in-memory is the kuhl_m_sekurlsa_enum_generic_callback_kerberos function, which is responsible for parsing either a linked list (prior to Windows Vista) or AVL tree, to identify the Kerberos structures related to the targeted logon session. The callback function kuhl_m_sekurlsa_enum_kerberos_callback_pth is then executed, which is responsible for patching in the appropriate credential information.

Mimikatz technique summary

The technique leveraged by Mimikatz is conceptually simple, but the implementation is subtle and complex. The tool simply identifies credential structures in-memory and overwrites them with the appropriate credential material specified by the attacker. That being said, extensive effort has been put into reverse engineering these internal structures along with building out a set of signatures and other heuristics for each major operating system version.

Overwriting these structures does not change the security information or user information for the local user account. The credentials stored in LSASS are associated with the logon session used for network authentication and not for identifying the local user account associated with a process.

What are the alternative techniques to Mimikatz?

Widespread attacker usage of Windows credential dumping means that suspicious access to the LSASS process is heavily monitored in most environments with a mature security program. In this section, we cover alternative techniques that can be leveraged within an environment that have a lower probability of detection:

  • Passing-the-ticket with Rubeus: The Rubeus tool allows operators to request a Kerberos Ticket Granting Ticket (TGT) using the user’s NTLM account hash with the RC4 cipher. This TGT can then be imported into the LSASS process by connecting to the Local Procedure Call (LPC) interface exposed by the LSA (LsaAuthenticationPort) [5] and invoking LsaCallAuthenticationPackage[3].
  • Proxying Traffic with Impacket: Proxying traffic using Impacket is another relevant technique that can be leveraged to make use of compromised NTLM account hashes. Impacket, combined with SOCKS functionality, allows an operator to proxy a custom implementation of Windows protocols such as SMB, RPC, Kerberos, and NetNTLMv2 authentication through a compromised host for lateral movement purposes. The advantage of this technique is that it doesn’t involve accessing LSASS memory or invoking Windows APIs that could be monitored by an endpoint security tool (such as LsaCallAuthenticationPackage).
  • Obtaining the Cleartext Password through Password Cracking: Cracking the associated password hash in many ways doesn’t count as passing-the-Hash, but operationally speaking, it has a similar outcome. Particularly in environments where lateral movement using standard techniques such as WMI or SMB is heavily monitored, it can be desirable to crack the account password and leverage built-in Windows protocols such as RDP for lateral movement using the plaintext credential. Using RDP for lateral movement with legitimate system administrator credentials is typically hard to detect.

Another technique would be for the operator to develop a custom device driver used to read to and write to LSASS process memory. Depending on how this driver is implemented, it would likely evade the standard methods used to detect LSASS process memory accesses.

In some ways, effective red teaming often involves “reinventing the wheel” or finding creative ways to accomplish the same basic task. Different techniques often have a different probability of detection, and understanding the implementation of a given technique allows you to create custom variations with a lower likelihood of detection. The likelihood of detection is typically a product of:

  • Are the defenders knowledgeable about the technique?
  • Does the endpoint security tool being used include built-in detections for a particular technique?
  • Is the detection technique likely to identify false positives? (Certain techniques are inherently more difficult to detect due to their similarity to legitimate behavior.)

In this post, we covered the method used by Mimikatz to implement pass-the-hash and compared it with potential alternative implementations. This will allow the reader to implement their own customized versions of Mimikatz (or smaller utilities with their own implementations).

Modifying LSASS memory to leverage compromised NTLM credentials is only one method among many. It is important to remember that there are a variety of other methods that can be used to leverage compromised NTLM credentials if one wishes to avoid potentially suspicious interactions with LSASS. For instance, the Rubeus tool provides a mechanism that can be used to request a TGT using a user’s NTLM hash. The TGT can then be imported into LSASS by connecting to the LSA over ALPC and interacting with the Kerberos authentication package [3].

In addition to this, if the implant being used (such as Cobalt Strike’s beacon) supports SOCKS proxying, it is possible to use Impacket to communicate directly with internal systems to request a TGT through Kerberos, or use the NTLM hash to authenticate [2]. Depending on the controls present in the target environment, these techniques may be stealthier than writing to LSASS process memory.