NTLM Challenge/Response

The LM and NTLM (v1 and v2) challenge/response processes are nearly identical, which is to be expected since the NTLM Security Support Provider (SSP) is responsible for implementing the LAN Manager, NTLMv1, NTLMv2, and NTLMv2 Session protocols.

NTLM Challenge/Response

Here's what the process looks like:

  1. Client sends a login request to the server. This is also known as a Type 1 message and contains a list of features supported by the client and requested of the server.
  2. Server respondes with a Type 2 message, which contains the agreed-upon features and a random string (the challenge)1.
  3. Client responds with a Type 3 message, containing its response to the challenge and, in the case of NTLMv2, a little something extra.
  4. Server processes the response (in a domain environment, the server will send the username, challenge, and response to a DC to do this work), using its copy of the user's hash as the key, and compares the result with the challenge. If they match, the client is authenticated.

So why do we go through all this trouble with the challenge and response? Why can't the client just send its hashed credentials for the server to compare with its own local copy and be done with it?

The reason for all of this challenge/response business is that Windows hashes are password equivalent. In both NTLM and Kerberos, it is the user's hash that acts as the input into the process. The only thing an attacker needs to authenticate as a user is access to their NT hash. This is known as a pass-the-hash attack.

NTLM uses challenge/response as a way to prevent the user's hash from being sent over the network where it can get stolen. Rather, the hash is used to encrypt a challenge, which is then sent as proof that the client has access to the user's credentials (the hash). The server will then encrypt the challenge it sent with its own copy of the user's hash and compare the two results. If they match, the client is authenticated.

So how does the response actually get made?

LM & NTLMv1 Response

In a welcome bit of simplicity, LM and NTLM both follow nearly the same process for creating a response. The only difference is the input hash format used as a key for the DES encryption algorithm.

Here's a closer look at the encryption process:

1
2
3
Response = DES(<HASH>[0-6], challenge) +
           DES(<HASH>[7-13], challenge) +
           DES((<HASH>[14-15] + 5 * NULL), challenge)

If LM is enabled, the response will contain both the LM response (24 bytes) and the NTLMv1 response (24 bytes). If LM is not enabled, you'll just get the NTLMv1 part. Fair enough.

LMv2 & NTLMv2 Response

You knew v2 wasn't going to get simpler, right?

LMv2 Response

The LM response is created for systems that don't understand NTLMv2 and are expecting to see a 24-byte response value in both the LM and NT positions. The HMAC-MD5 hash is 128 bits (16 bytes) long which, when combined with the 8-byte client challenge, results in a 24 byte value. Coincidentally (or not?) the size of the old school LM response.

1
2
3
NTLMv2 Hash   = HMAC-MD5(NT Hash, uppercase(username) + target)
LMv2 Goodies  = HMAC-MD5(NTLMv2 Hash, challenge + 8-byte client nonce)
LMv2 Response = LMv2 Goodies + 8-byte client nonce

NTLMv2 Response

The NTLMv2 response is the default version of NTLM for pretty much every computer running Vista or greater. If you're dealing with NTLM today, it's probably NTLMv2.

Here's how an NTLMv2 response is created:

  1. The username is converted to uppercase and concatenated with either the (case-sensitive) target domain name or server name2.
  2. The username/target string is smashed into an HMAC-MD5 function, using the NT hash as the key. This gives us the 16-byte NTLMv2 hash.
  3. The blob is created3.
  4. The server's challenge is concatenated with the blob, and then smashed into another HMAC-MD5 function, this time using the NTLMv2 hash as the key, resulting in a 16-byte output, which I'll be calling the Goodies.
  5. The Goodies are concatenated with the blob to form the NTLMv2 response.
1
2
3
NTLMv2 Hash     = HMAC-MD5(NT Hash, uppercase(username) + target)
NTLMv2 Goodies  = HMAC-MD5(NTLMv2 Hash, challenge + blob)
NTLMv2 Response = NTLMv2 Goodies + blob

The data contained in the blob is of variable length, which means that the response will also be of variable length.

NTLM2 Session Response (No V!)

Do you have serious security concerns about precomputed dictionary attacks, but can't implement NTLMv2? Read on, dear reader, read on.

We're going to start this one with the LM response:

  1. Client creates an 8-byte nonce and null pads it to 24 bytes to form the LM response.
1
LM Response = 8-byte client nonce + 16 * NULL

Now on to the NTLM2 bit:

  1. Combine the 8-byte client nonce with the server challenge to create a session key.
  2. Squish the session key through MD5.
  3. Truncate the 16 bytes of hashed session key down to 8. This is the NTLM2 session hash.
  4. Null pad the 16-byte NT hash to 21 bytes, then split that into three 7-byte blocks.
  5. Using each of the 7-byte blocks as the key, DES-encrypt the NTLM2 session hash.
  6. Concatenate the three resulting ciphertext bits into a 24-byte response.
1
2
3
4
5
NTLM2 Session Key = 8-byte client nonce + 16-byte server challenge
NTLM2 Session Hash = (MD5(NTLM2 Session Key))[0-7]
NTLM2 Response = DES(NTLM2 Session Hash[0-6], NTLM2 Session Hash) +
                 DES(NTLM2 Session Hash[7-13], NTLM2 Session Hash) +
                 DES((NTLM2 Session Hash[14-15] + 5 * NULL), NTLM2 Session Hash)

ELI5

The deeper down you go with NTLM the more complicated it gets, but from a 5-year-old's perspective it's quite simple. Both the client and the server have access to the hash of the user account you're using to authenticate. If they didn't, you wouldn't be able to authenticate at all which makes for as short blog post.

So you want to connect to the server but we're going to do this in a such a way that we're not sending your password-equivalente NT hash across the network where it could be burgled.

You contact the server and say "hi" and in return the server provides you with a random string. You encrypt this random string with either DES (LM & NTLMv1) or HMAC-MD5 (NTLMv2), using your NT password hash as the key, and then send it back to the server.

Since the server (or DC) also has access to your hash, it encrypts the challenge with that hash and compares its result to the one sent by the client. If it's a match, the client is authenticated.

Resources


  1. The LM/NTLMv1 challenge is 8 bytes, the NTLMv2 challenge is 16. ↩︎

  2. If the target server is not in the client's domain, the server name is used in place of the logon domain name. ↩︎

  3. I did not make this name up. This contains, among other times, a timestamp, a client nonce, some target-specific data, and a few unknowns. During my research, I kept running into articles referring to the 8-byte client nonce as a challenge. This made me think that somehow the client was also authenticating the server, but I don't believe this is the case. After a decent amount of digging, it appears that this is truly a nonce, and is implemented to prevent replay attacks. ↩︎

<<
>>