Overview

During the summer, my colleague Derya Yavuz and I published an article on some of the different methods we’ve leveraged to elevate privileges within Active Directory environments. We discussed authentication coercion techniques such as PrinterBug, PetitPotam, and DFSCoerce. One of the techniques we mentioned in that article was performing an NTLM downgrade attack to obtain an NTLMv1 hash from a victim client computer.

However, we encountered some hurdles to exploiting this issue, as it required leveraging a service such as crack.sh to obtain the original NTLM hash for the victim computer account. Obviously, this is not possible during a penetration test because submitting customer data to a third-party service would open the client to unacceptable risk. Additionally, we weren’t sure if we could configure a system to support NTLMv1 and NTLMv2 via the LmCompatibilityLevel setting. Finally, we did not know if there was any logic which we could use to downgrade an NTLMv2 authentication to NTLMv1.

This article attempts to answer these questions by digging a bit more into the NTLM specification, reviewing the source code of open source NTLM implementations, and analyzing network traffic captures. We also look at the Responder source code, and dig into the NTLM messages using the NTLMParse utility Praetorian released previously.

Digging into the NTLM Specification

Some Confusion Surrounding Version Negotiation

To begin my research, I attempted to answer the question “Does the NTLM authentication protocol support negotiation of a version?”. I assumed that the version negotiation would be part of the protocol itself when I began my research into ADFS relaying attacks. Therefore, I was quite confused when I read in the specifications that “the NTLM authentication version is not negotiated by the protocol. It has to be configured on both the client and the server prior to authentication.” Figure 1 shows the relevant section of the NTLM specification.

Figure 1: A section of the NTLM specification which states that the protocol itself does not negotiate the NTLM authentication version.

However, another portion of the specification confused me a bit more. When it describes the NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY flag it mentions that when set, this flag “requests usage of the NTLM v2 session security. NTLM v2 session security is a misnomer because it is not NTLM v2. It is NTLMv1 using the extended session security that is also in NTLM v2.” (See Figure 2) [1].

Figure 2: A section of the NTLM specification which describes the extended session security flag.

Finding Some Clarity

Next, I spent some time reviewing the various options which could be configured for the LmCompatibilityLevel setting to further my understanding. The table in Figure 3 outlines the valid options for this setting. This also confirmed my understanding from the NTLM specification that they only supported configuring one option or the other without any sort of negotiation mechanism. A server can be configured to support NTLMv1 or NTLMv2, but there isn’t an option for clients to support both.

Figure 3: A table from MSDN outlining the available options for the LmCompatibilityLevel setting.

Later, I discovered an MSDN article which mentions that “Unlike plain NTLMv1 or NTLMv2, NTLMv1 w/ ESS is actually negotiated between a client and a server (NTLMv1 and NTLMv2 are configured using security key LmCompatibilityLevel). It is negotiated by setting a bit in NegotiateFlags, called P (MS-NLMP, Section 2.2.2.5).” [2]. This statement somewhat confused me since it discusses negotiating support for additional security features (See Figure 4).

Figure 4: An excerpt from an MSDN article which provides additional details on NTLMv1 extended session security (ESS).

After digging through the specification a bit more I discovered the pseudocode shown in Figure 5 which shows some sample code for computing the challenge response in NTLMv1. I was surprised to note that this version passed a ClientChallenge value to the function, since NTLMv1 doesn’t actually support a client challenge field like NTLMv2.

Figure 5: Pseudocode from the NTLM specification indicating how NTLMv1 computes the NtChallengeResponse and LmChallengeResponse.

Where the Client Challenge Comes In

After reading the specification at that time, I was unsure where the client challenge originated. However, in Figure 6 we can see in the go-ntlm library source code that the client’s LmChallengeResponse field prompts this challenge. We can also see that in the case of NTLMv2 this challenge is read from the NTLMv2_CLIENT_CHALLENGE structure stored within the NTLMv2_RESPONSE field, which is stored within the NtChallengeResponse field.

Figure 6: Source code from the go-ntlm library that reads the client challenge value from the LmChallengeResponse field with NTLMv1.

Imagine a scenario where we are trying to capture an NTLMv1 hash from a client configured to support NTLMv1 authentication with extended session security. In this case, we need the client to encrypt an attacker-controlled challenge of 1122334455667788 in order to leverage the rainbow table service provided by the crack.sh site. Unfortunately, the client-generated challenge would break this if the server we are using to capture the hash indicates it supports extended session security within the NegotiateFlags sent by the server to the client. In this case, we would want to, for example, use the –disable-ess flag [3] within Responder to prevent the client from including a client challenge which would break compatibility with the rainbow table.

Examining the Responder Source Code

My next question was, if the NTLM protocol does not negotiate a version, how does Responder differentiate between NTLMv1 and NTLMv2 hashes? I thought the best way to answer this question would be to review the source code of the Responder utility.

Figures 7 and 8 show the relevant sections from the Responder source code, in which we observe that the NthashLen is the length of the NtChallengeResponse field in the payload section of the message. We can see here that Responder is checking the length of this field to determine the version of the NTLM authentication protocol. We can see that the length of the field is twenty-four bytes for NTLMv1 in Figure 7 and greater than twenty-four bytes for NTLMv2 in Figure 8.

Figure 7: The code Responder uses to determine if a message is of type NTLMv1.

Figure 8: The code Responder uses to determine if a message is of type NTLMv2.

If we then look at Section 2.2.2.6 of the NTLM specification, we can see an outline of NTLM_RESPONSE format for when NTLMv1 is leveraged. Figure 9 shows that the NTLM_RESPONSE structure contains a single twenty-four byte field named Response. In the subsequent sections, we dig more into the format of the NTLMv1 response and some of the vulnerabilities arising from leveraging the DES encryption algorithm to compute it.

Figure 9: A table from the NTLM specification detailing the format of the NTLM_RESPONSE message. We observe that it contains a single required fixed-length field of twenty-four bytes.

Identifying a Reliable Mechanism to Determine Version

Next, let’s take a look at the NTLMv2 response to determine why Responder is checking if the length is greater than twenty-four bytes in this scenario. Examining Section 2.2.2.8 of the specification (shown in Figure 10) we observe that the NTLMv2_RESPONSE structure contains a fixed-length field of sixteen bytes and a variable length field called NTLMv2_CLIENT_CHALLENGE. We examine the required fields of the NTLMv2_CLIENT_CHALLENGE structure (as shown in Figure 11) and determine that this structure has at least twenty-eight bytes of required data. Since the computed NTLMv2 response will always contain at least twenty-four bytes of required data we can accurately leverage this as a mechanism to determine when NTLMv2 is in use.

Figure 10: The NTLMv2_RESPONSE field contains a required field of 16 bytes and an embedded variable-length NTLMv2_CLIENT_CHALLENGE structure.

Figure 11: The NTLMv2_CLIENT_CHALLENGE structure must be at least twenty-eight bytes long (not including the variable length AvPairs structure which also includes required fields).

Understanding the NTLMv1 Hash Format

After gaining a bit more of  an understanding of how Responder differentiates between NTLMv1 and NTLMv2 hashes I was curious on what format Responder uses to store NTLMv1 hashes. In this case, the format was quite simple. In Figure 12 we see that the NTLMv1 hash contains the username, computer name, LmChallengeResponse, NtChallengeResponse, and challenge. Essentially, it contains the output of the computed challenges and the input challenge required to compute the challenge response. An attacker that wishes to recover the user’s original plaintext password would simply leverage the same algorithm and compare the computed challenge to perform an offline attack in an attempt to recover the user’s password. In Figure 13, we show how the LmChallengeResponse and NtChallengeResponse fields in the NTLMv1 hash match up with the corresponding fields in the captured NTLM_AUTHENTICATE message.

Figure 12: The NtChallengeResponse and LmChallengeResponse along with the server challenge are the primary components of the generated NTLMv1 hash.

Figure 13: The NtChallengeResponse and LmChallengeResponse values Responder uses to compute the NTLMv1 hash output.

NTLMv1 DES Encryption Mechanism

NTLMv1 computes the client challenge by leveraging the DESL function to encrypt the server challenge. It accomplishes this by breaking the user’s NT hash into three, seven byte DES keys. Unfortunately, since the algorithm it uses also encrypts each ciphertext value with three separate DES keys an attacker can crack each DES key individually to recover the entire NTLM hash for the victim user. See Figure 14 for some of the go-ntlm library code that computes the NTLMv1 challenge response.

Figure 14: Example code for generating the NTLMv1 NtChallengeResponse and LmChallengeResponse values from the go-ntlm library.

Figure 15 shows the definition of the DESL function that the code uses in Figure 14. The DESL function works by separating the user’s NTLM hash into three separate keys with the third key padded with zeros. The three separate DES keys then encrypt and concatenate the server challenge (or an MD5 hash of the server and client challenges, under extended session security). Unfortunately, since the DES algorithm uses 56-bit keys an attacker can leverage a service such as crack.sh to recover the original NTLM hash.

Figure 15: An excerpt from the NTLM specification detailing how the user’s NT or LM hash breaks into multiple DES keys which then subsequently encrypt the computed challenge value.

Understanding the NTLMv2 Challenge Response Mechanism

The method for computing the NTLMv2 challenge response value is very similar to that for NTLMv1 with a few key differences. First, instead of using the previously mentioned DES algorithm, it leverages the HMAC MD5 algorithm to compute the challenge response. Secondly, the data used to compute the challenge includes additional information such as the AV_PAIR values within the NTLMv2 client challenge structure. Figure 16 shows the go-ntlm library code that computes the NTLMv2 response value.

Figure 16: An excerpt from the go-ntlm library showing how the NTLMv2 challenge response message is computed.

Unsurprisingly, the NTLMv2 hash format is very similar to the format of the NTLMv1 hash format. In this case, it contains the username, domain, server challenge, computer response (NTProofStr), and the NTLMv2_CLIENT_CHALLENGE structure which contains the timestamp, client challenge, and the AV_PAIR bytes. Figure 17 shows a breakdown of the various fields of the NTLMv2 structure.

Figure 17: An NTLMv2 hash captured by Responder with labels identifying relevant components of the hash data .

SMB to LDAP Relaying Attacks Using NTLMv1

Relaying from SMB to the LDAP service is also something that is theoretically possible with NTLMv1. This would provide another alternative exploitation vector that doesn’t require the attacker to leverage a site such as crack.sh, which may not be permitted for security reasons. NTLMv1 doesn’t support computing a message integrity code (see Figure 18), which allows an attacker to modify the attributes of the computed NTLM_AUTHENTICATE message. In Figure 19 we see that the LDAP service leverages the NTLMSSP_NEGOTIATE_SIGN field to determine if signing is required.

Figure 18: Inspecting an NTLMv1 AUTHENTICATE_MESSAGE using the NTLMParse utility we observe that a message integrity code (MIC) does not exist, allowing for modification of the message.

Figure 19: The NTLMSSP_NEGOTIATE_SIGN flag with the LDAP service interpret as a requirement for signing.

In Figure 20 we see the LmCompatibilityLevel setting that configures the version of NTLM authentication for both the client and the server.

Figure 20: An example where we have configured the LmCompatibilityLevel setting to use the “Send NTLM Response Only” option.

Relaying from SMB to the LDAP service is quite straightforward and simply requires an attacker to specify the –remove-mic flag when performing a relaying attack. Originally, this flag was added to support exploitation of the “Drop the MIC” vulnerability. However, it also has the benefit of setting the NTLMSSP_NEGOTIATE_SIGN flag to false. This allows relaying from SMB to the LDAP service to work since NTLMv1 doesn’t include a message integrity code (MIC). Figure 21 shows an example scenario where we invoked NTLMRelayX with the –remove-mic flag and then triggered an SMB authentication to the attacker-controlled SMB server from a victim account. We observed that relaying SMB to the LDAP service was successful in this scenario.

Figure 21: We invoke NTLMRelayX with the –remove-mic flag and successfully relay NTLMv1 authentication over SMB to the LDAP service.

Bypassing SMB Signing with NTLMv1

Theoretically, bypassing SMB signing requirements is always possible when NTLMv1 is enabled, because the NTLMv1 authentication mechanism does not contain information on the target to which the client is authenticating. To understand why this is an issue, we must first understand a bit about how NTLM authentication works within a domain environment. At a high-level, when a client authenticates to a server leveraging NTLM authentication in an Active Directory domain environment, the server typically doesn’t have possession of the user’s NTLM hash.

Instead, the server must send the client’s NTLM_AUTHENTICATE response to a domain controller that will then finalize processing the authentication request. The domain controller then returns a session signing key that is leveraged for SMB signing. Essentially, with NTLMv1 the client doesn’t embed any information regarding the target to which it was attempting to authenticate. This ultimately means an attacker could themselves submit the NTLM_AUTHENTICATE message to a domain controller to validate the authentication and retrieve the session key for SMB signing. Keren Pollack has written an excellent article titled Why NTLMv1 Will Always Be Vulnerable to NTLM Relay Attacks that discusses this concept in more depth [3].

Relaying NTLMv1 to ADFS

In yet another scenario, we may receive an NTLMv1 authentication from a victim user account. Since this is a user account as opposed to a computer account, we may wish to relay this authentication attempt to the ADFS service in order to authenticate to cloud services as the victim user account.

The NTLMv1 protocol does not support the channel binding token. Fundamentally, this means that an attacker could relay an NTLMv1 authentication to the ADFS service under the default EPA setting of Allow. We discuss ADFS relay attacks in a previously published article titled Relaying to ADFS Attacks [5].

Digging into SMB Relaying to LDAP with NTLMv2

While reviewing the NTLM specification a specific section surrounding the TargetInfo field provided by the server in the NTLM_CHALLENGE message stood out to me. This section stated that if the MsvAvTimestamp field is present in the TargetInfo fields then the client should include a message integrity code. Figure 22 shows the relevant section of the NTLM specification which mentions the expected behavior of the client when the MsvAvTimestamp field is present. As an attacker I can modify the NTLM_CHALLENGE message returned by the server when performing this attack to remove the timestamp.

Figure 22: The section of the NTLM specification which outlines the behavior when the MsvAvTimestamp field is present.

My hypothesis at this point in the research was that an attacker could potentially obtain a valid NTLM message from a client without a MIC field included and then pass this to the server. The follow-on assumption here is that the target server might instead then leverage the client-generated timestamp from the response.

To test my hypothesis I made some modifications to Impacket which would remove the MsvAvTimestamp field returned by the server in the NTLM_CHALLENGE message and provide the modified response to the client. From my testing, I observed that, while the client-generated NTLM_AUTHENTICATE message did not include the message integrity code, the targeted server rejected the authentication attempt. In this case, the Windows NTLM implementation ignores the timestamp provided by the client.

Investigating By Looking at the Samba Source Code

Next, I took some time to examine the source code to Sambda. While this implementation will not be exactly the same as the corresponding Windows version, I figured it might provide some additional insights into what might be happening in the Windows implementation. In the case of Samba, I quickly discovered a comment indicating that the system doesn’t leverage the timestamp provided by the client when performing authentication. Instead, the system leverages the server-generated timestamp. Figure 23 shows this section of the Samba source code.

Figure 23: Examining the Samba source code we see a comment indicating that the server does not use “AUTHENTICATE_MESSAGE.NtChallengeResponse.TimeStamp value to perform validation of the NtChallengeResponse value.

Reviewing the code further, I later observed that when the server processes the client’s NTLM_AUTHENTICATE message it compares the provided timestamp with the one in the timestamp field in the NTLM_CHALLENGE message. When those fields don’t match the server returns an error (see Figure 24).

Figure 24: An excerpt from the Samba source code comparing the client-provided and server-provided MsvAvTimestamp values. The server returns an error if it finds the two don’t match upon comparison. 

While in this case it is likely no vulnerability exists, it was still an interesting learning experience. One of my favorite things about security research, even when it is unsuccessful, is that you almost always end up learning something interesting (and having lots of fun along the way).

Future Work

I would be very interested in seeing someone release a version of the NTLMv1 rainbow table that crack.sh leverages. This would significantly decrease the amount of effort required to exploit NTLMv1 authentication without requiring the end-user to expose the NTLM hash of the target account to an external service.

Having said that, in the case of crack.sh, while using it would expose the NTLM hash of the account, the data would not include any additional information about the username, domain name, domain sid, etc. associated with the account or domain environment. Additionally, in the case of computer accounts, the exposed NTLM hash would rotate automatically after thirty-days under the default setting. In some scenarios, this could make leveraging the crack.sh site an acceptable risk for some use-cases (although one could argue from a purely theoretical perspective this is security through obscurity).

Conclusion

In this article we spent some additional time delving into the NTLM specification to gain a better understanding of how various aspects of the NTLM protocol function. We covered answers to common questions such as,

  • “Does NTLM support negotiation of an authentication protocol version?”
  • “How does NTLMv1 and NTLMv2 session security function?”
  • “How does Responder identify NTLMv1 and NTLMv2 hashes?”
  • “What is the format used for NTLMv1 versus NTLMv2 hashes?”

Then we leveraged some of our additional knowledge of the NTLM specification to identify alternative methods of exploiting NTLMv1 authentication. We specifically focused on identifying an environment that doesn’t require submitting potentially sensitive data (a valid NTLM hash for an account) to a potentially untrusted third-party service such as crack.sh. These techniques included bypassing SMB signing, relaying SMB to LDAP, and relaying NTLMv1 authentication attempts to the ADFS service.

References

[1]https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/c50a85f0-5940-42d8-9e82-ed206902e919

[2]https://docs.microsoft.com/en-us/archive/blogs/openspecification/ntlm-keys-and-sundry-stuff

[3]https://github.com/lgandx/Responder/pull/163

[4]https://www.calcomsoftware.com/why-ntlmv1-will-always-be-vulnerable-to-ntlm-relay-attacks/

[5]https://www.praetorian.com/blog/relaying-to-adfs-attacks/