The NTLM Authentication Protocol

Permission to use, copy, modify, and distribute this document for any purpose and without any fee is hereby granted, provided that the above copyright notice and this list of conditions appear in all copies.

Abstract

This article seeks to describe NTLM at an intermediate to advanced level of detail, suitable as a reference for implementors. It is hoped that this document will evolve into a comprehensive description of NTLM; at this time there are omissions, both in the author's knowledge and in his documentation, and almost certainly inaccuracies. However, this document should at least be able to provide a solid foundation for further research. The information presented herein was used as the basis for the implementation of NTLM in the open-source jCIFS library, available at http://jcifs.samba.org.

The target information is a security buffer containing a Target Information block, which is used in calculating the NTLMv2 response (discussed later). This is composed of a sequence of subblocks, each consisting of:

Field

Content

Description

Type

short

Indicates the type of data in this subblock:

1 (0x0100):

Server name

2 (0x0200):

Domain name

3 (0x0300):

Fully-qualified DNS host name (i.e., server.domain.com)

4 (0x0400):

DNS domain name (i.e., domain.com)

Length

short

Length in bytes of this subblock's content field

Content

Unicode string

Content as indicated by the type field. Always sent in Unicode, even when OEM is indicated by the message flags.

The sequence is terminated by a terminator subblock; this is a subblock of type "0", of zero length. Subblocks of type "5" have also been encountered, apparently containing the "parent" DNS domain for servers in subdomains; it may be that there are other as-yet-unidentified subblock types as well.

The context and target information may be omitted, in which case the data block begins at offset 32 (immediately following the challenge). A minimal Type 2 message would look something like this:

The LM/LMv2 and NTLM/NTLMv2 responses are security buffers containing replies created from the user's password in response to the Type 2 challenge; the process for generating these responses is outlined in the next section.

The domain name is a security buffer containing the authentication realm in which the authenticating account has membership. This is either Unicode or OEM, depending on the negotiated encoding.

The user name is a security buffer containing the authenticating account name. This is either Unicode or OEM, depending on the negotiated encoding.

The workstation name is a security buffer containing the client workstation's name. This is either Unicode or OEM, depending on the negotiated encoding.

The session key value is largely unknown, and is often empty when included; it is apparently relevant in newer signing and sealing mechanisms. The Open Group documentation states that it additionally plays a role in datagram-style authentication.

When "Negotiate Local Call" has been established in the Type 2 message, the security buffers in the Type 3 message are typically all empty (zero length). The client "adopts" the SSPI context sent in the Type 2 message, effectively circumventing the need to calculate an appropriate response.

These values are used to create two DES keys (one from each 7-byte half).

Each of these keys is used to DES-encrypt the constant ASCII string "KGS!@#$%" (resulting in two 8-byte ciphertext values).

These two ciphertext values are concatenated to form a 16-byte value - the LM hash.

The 16-byte LM hash is null-padded to 21 bytes.

This value is split into three 7-byte thirds.

These values are used to create three DES keys (one from each 7-byte third).

Each of these keys is used to DES-encrypt the challenge from the Type 2 message (resulting in three 8-byte ciphertext values).

These three ciphertext values are concatenated to form a 24-byte value. This is the LM response.

This process is best illustrated with a detailed example. Consider a user with the password "SecREt01", responding to the Type 2 challenge "0x0123456789abcdef".

The password (as an OEM string) is converted to uppercase, giving "SECRET01" (or "0x5345435245543031" in hexadecimal).

This password is null-padded to 14 bytes, giving "0x5345435245543031000000000000".

This value is split into two 7-byte halves, "0x53454352455430" and "0x31000000000000".

These two values are used to create two DES keys. A DES key is 8 bytes long; each byte contains seven bits of key material and one odd-parity bit (the parity bit may or may not be checked, depending on the underlying DES implementation). Our first 7-byte value, "0x53454352455430", would be represented in binary as:

This is our second DES key, "0x3180010101010101" in hexadecimal. Note that if our particular DES implementation does not enforce parity (many do not), the parity-adjustment steps can be skipped; the non-parity-adjusted values would then be used as the DES keys. In any case, the parity bits will not affect the encryption process.

Each of our keys is used to DES-encrypt the constant ASCII string "KGS!@#$%" ("0x4b47532140232425" in hex). This gives us "0xff3750bcc2b22412" (using the first key) and "0xc2265b23734e0dac" (using the second).

These ciphertext values are concatenated to form our 16-byte LM hash - "0xff3750bcc2b22412c2265b23734e0dac".

This is null-padded to 21 bytes, giving "0xff3750bcc2b22412c2265b23734e0dac0000000000".

This value is split into three 7-byte thirds, "0xff3750bcc2b224", "0x12c2265b23734e" and "0x0dac0000000000".

These three values are used to create three DES keys. Using the process outlined previously, our first value:

Each of the three keys is used to DES-encrypt the challenge from the Type 2 message (in our example, "0x0123456789abcdef"). This gives the results "0xc337cd5cbd44fc97" (using the first key), "0x82a667af6d427c6d" (using the second) and "0xe67c20c2d3e77c56" (using the third).

These three ciphertext values are concatenated to form the 24-byte LM response:

0xc337cd5cbd44fc9782a667af6d427c6de67c20c2d3e77c56

There are several weaknesses in this algorithm which make it susceptible to attack. While these are covered in detail in the Hertel text, the most prominent problems are:

Passwords are converted to upper case before calculating the response. This significantly reduces the set of possible passwords that must be tested in a brute-force attack.

If the password is seven or fewer characters, the second value from step 3 above will be 7 null bytes. This effectively compromises half of the LM hash (as it will always be the ciphertext of "KGS!@#$%" encrypted with the DES key "0x0101010101010101" - the constant "0xaad3b435b51404ee"). This in turn compromises the three DES keys used to produce the response; the entire third key and all but one byte of the second will be known constant values.

The MD4 message-digest algorithm (described in RFC 1320) is applied to the Unicode mixed-case password. This results in a 16-byte value - the NTLM hash.

The 16-byte NTLM hash is null-padded to 21 bytes.

This value is split into three 7-byte thirds.

These values are used to create three DES keys (one from each 7-byte third).

Each of these keys is used to DES-encrypt the challenge from the Type 2 message (resulting in three 8-byte ciphertext values).

These three ciphertext values are concatenated to form a 24-byte value. This is the NTLM response.

Note that only the calculation of the hash value differs from the LM scheme; the response calculation is the same. To illustrate this process, we will apply it to our previous example (a user with the password "SecREt01", responding to the Type 2 challenge "0x0123456789abcdef").

The Unicode mixed-case password is "0x53006500630052004500740030003100" in hexadecimal; the MD4 hash of this value is calculated, giving "0xcd06ca7c7e10c99b1d33b7485a2ed808". This is the NTLM hash.

This is null-padded to 21 bytes, giving "0xcd06ca7c7e10c99b1d33b7485a2ed8080000000000".

This value is split into three 7-byte thirds, "0xcd06ca7c7e10c9", "0x9b1d33b7485a2e" and "0xd8080000000000".

These three values are used to create three DES keys. Our first value:

Each of the three keys is used to DES-encrypt the challenge from the Type 2 message ("0x0123456789abcdef"). This yields the results "0x25a98c1c31e81847" (using our first key), "0x466b29b2df4680f3" (using the second) and "0x9958fb8c213a9cc6" (using the third key).

These three ciphertext values are concatenated to form the 24-byte NTLM response:

The NTLM password hash is obtained (as discussed previously, this is the MD4 digest of the Unicode mixed-case password).

The Unicode uppercase username is concatenated with the Unicode uppercase authentication target (domain or server name). The HMAC-MD5 message authentication code algorithm (described in RFC 2104) is applied to this value using the 16-byte NTLM hash as the key. This results in a 16-byte value - the NTLMv2 hash.

A block of data known as the "blob" is constructed. The Hertel text discusses the format of this structure in greater detail; briefly:

Description

Content

0

Blob Signature

0x01010000

4

Reserved

long (0x00000000)

8

Timestamp

Little-endian, 64-bit signed value representing the number of tenths of a microsecond since January 1, 1601.

16

Client Challenge

8 bytes

24

Unknown

4 bytes

28

Target Information

Target Information block (from the Type 2 message).

(variable)

Unknown

4 bytes

The challenge from the Type 2 message is concatenated with the blob. The HMAC-MD5 message authentication code algorithm is applied to this value using the 16-byte NTLMv2 hash (calculated in step 2) as the key. This results in a 16-byte output value.

This value is concatenated with the blob to form the NTLMv2 response.

Let's look at an example. Since we need a bit more information to calculate the NTLMv2 response, we will use the following values from the examples presented previously:

The Unicode mixed-case password is "0x53006500630052004500740030003100" in hexadecimal; the MD4 hash of this value is calculated, giving "0xcd06ca7c7e10c99b1d33b7485a2ed808". This is the NTLM hash.

The Unicode uppercase username is concatenated with the Unicode uppercase authentication target, giving "USERDOMAIN" (or "0x550053004500520044004f004d00410049004e00" in hexadecimal). HMAC-MD5 is applied to this value using the 16-byte NTLM hash from the previous step as the key, which yields "0x04b8e0ba74289cc540826bab1dee63ae". This is the NTLMv2 hash.

Next, the blob is constructed. The timestamp is the most tedious part of this; looking at the clock on my desk, it's about 6:00 AM EDT on June 17th, 2003. In Unix time, that would be 1055844000 seconds after the Epoch. Adding 11644473600 will give us seconds after January 1, 1601 (12700317600). Multiplying by 107 (10000000) will give us tenths of a microsecond (127003176000000000). As a little-endian 64-bit value, this is "0x0090d336b734c301" (in hexadecimal).

We also need to generate an 8-byte random "client challenge"; we will use the not-so-random "0xffffff0011223344". Constructing the rest of the blob is easy; we just concatenate:

The NTLM password hash is calculated (the MD4 digest of the Unicode mixed-case password).

The Unicode uppercase username is concatenated with the Unicode uppercase authentication target (domain or server name). The HMAC-MD5 message authentication code algorithm is applied to this value using the 16-byte NTLM hash as the key. This results in a 16-byte value - the NTLMv2 hash.

A random 8-byte client challenge is created (this is the same client challenge used in the NTLMv2 blob).

The challenge from the Type 2 message is concatenated with the client challenge. The HMAC-MD5 message authentication code algorithm is applied to this value using the 16-byte NTLMv2 hash (calculated in step 2) as the key. This results in a 16-byte output value.

This value is concatenated with the 8-byte client challenge to form the 24-byte LMv2 response.

We will illustrate this process with a brief example using our tried-and-true sample values:

Domain:

DOMAIN

Username:

user

Password:

SecREt01

Challenge:

0x0123456789abcdef

The Unicode mixed-case password is "0x53006500630052004500740030003100" in hexadecimal; the MD4 hash of this value is calculated, giving "0xcd06ca7c7e10c99b1d33b7485a2ed808". This is the NTLM hash.

The Unicode uppercase username is concatenated with the Unicode uppercase authentication target, giving "USERDOMAIN" (or "0x550053004500520044004f004d00410049004e00" in hexadecimal). HMAC-MD5 is applied to this value using the 16-byte NTLM hash from the previous step as the key, which yields "0x04b8e0ba74289cc540826bab1dee63ae". This is the NTLMv2 hash.

A random 8-byte client challenge is created. From our NTLMv2 example, we will use "0xffffff0011223344".

We then concatenate the Type 2 challenge with our client challenge:

0x0123456789abcdefffffff0011223344

Applying HMAC-MD5 to this value using the NTLMv2 hash from step 2 as the key gives us the 16-byte value "0xd6e6152ea25d03b7c6ba6629c2d6aaf0".

This value is concatenated with the client challenge to obtain the 24-byte LMv2 response:

The client challenge is null-padded to 24 bytes. This value is placed in the LM response field of the Type 3 message.

The challenge from the Type 2 message is concatenated with the 8-byte client challenge to form a session nonce.

The MD5 message-digest algorithm (described in RFC 1321) is applied to the session nonce, resulting in a 16-byte value.

This value is truncated to 8 bytes to form the NTLM2 session hash.

The NTLM password hash is obtained (as discussed, this is the MD4 digest of the Unicode mixed-case password).

The 16-byte NTLM hash is null-padded to 21 bytes.

This value is split into three 7-byte thirds.

These values are used to create three DES keys (one from each 7-byte third).

Each of these keys is used to DES-encrypt the NTLM2 session hash (resulting in three 8-byte ciphertext values).

These three ciphertext values are concatenated to form a 24-byte value. This is the NTLM2 session response, which is placed in the NTLM response field of the Type 3 message.

To demonstrate this with our previous example values (a user with the password "SecREt01", responding to the Type 2 challenge "0x0123456789abcdef"):

A random 8-byte client challenge is created; we will use "0xffffff0011223344", as in the previous examples.

The challenge is null-padded to 24 bytes:

0xffffff001122334400000000000000000000000000000000

This value is placed in the LM response field of the Type 3 message.

The challenge from the Type 2 message is concatenated with the client challenge, forming a session nonce ("0x0123456789abcdefffffff0011223344").

Applying the MD5 digest to this nonce yields the 16-byte value "0xbeac9a1bc5a9867c15192b3105d5beb1".

This is truncated to 8 bytes to obtain the NTLM2 session hash ("0xbeac9a1bc5a9867c").

The Unicode mixed-case password is "0x53006500630052004500740030003100"; applying the MD4 digest to this value gives us the NTLM hash ("0xcd06ca7c7e10c99b1d33b7485a2ed808").

This is null-padded to 21 bytes, giving "0xcd06ca7c7e10c99b1d33b7485a2ed8080000000000".

This value is split into three 7-byte thirds, "0xcd06ca7c7e10c9", "0x9b1d33b7485a2e" and "0xd8080000000000".

These values are used to create three DES keys (as calculated in our previous NTLM response example, "0xcd83b34fc7f14392", "0x9b8f4c767543685d", and "0xd904010101010101").

Each of these three keys is used to DES-encrypt the NTLM2 session hash ("0xbeac9a1bc5a9867c"). This yields the results "0x10d550832d12b2cc" (using our first key), "0xb79d5ad1f4eed3df" (using the second), and "0x82aca4c3681dd455" (using the third key).

These three ciphertext values are concatenated to form the 24-byte NTLM2 session response:

(LMCompatibility on Win9x-based systems). This is a REG_DWORD entry, and can be set to one of the following values:

Level

Sent by Client

Accepted by Server

0

LM NTLM

LM NTLM LMv2 NTLMv2

1

LM NTLM

LM NTLM LMv2 NTLMv2

2

NTLM

LM NTLM LMv2 NTLMv2

3

LMv2 NTLMv2

LM NTLM LMv2 NTLMv2

4

LMv2 NTLMv2

NTLM LMv2 NTLMv2

5

LMv2 NTLMv2

LMv2 NTLMv2

In Levels 1 and higher, NTLM2 session security is supported. Only Levels 0 and 3 are available on Win9x-based systems (Windows 95, Windows 98, and Windows ME); these platforms do not support the NTLM response, and instead send only the LM response in the Type 3 message. In Level 2, clients send the NTLM response twice (in both the LM and NTLM response fields). At Level 3 and higher, the LMv2 and NTLMv2 responses replace the LM and NTLM responses, respectively.

When NTLM2 session security has been negotiated (indicated by the "Negotiate NTLM2 Key" flag), the NTLM2 session response can be used in Levels 0, 1, and 2 as a replacement for the weaker LM and NTLM responses. This offers heightened protection against server-based precomputed dictionary attacks; the client's response to a given challenge is made variable by adding a random client nonce to the calculation.

RFC 2743), and allows for a very high-level, mechanism-independent means of authentication. SSPI supports several underlying providers. One of these is the NTLMSSP (NTLM Security Support Provider), which provides the NTLM authentication mechanism we have been discussing thus far. SSPI supplies a flexible API for handling opaque, provider-specific authentication tokens; the NTLM Type 1, Type 2, and Type 3 messages are such tokens, specific to and processed by the NTLMSSP. The API provided by SSPI abstracts away all the details of NTLM; the application developer doesn't even have to be aware that NTLM is being used, and another authentication mechanism (such as Kerberos) can be swapped in with little or no changes at the application level.

We aren't going to go into the details of SSPI, but we will briefly outline the SSPI authentication handshake as applied to NTLM:

The client obtains a representation of the credential set for the user via the SSPI AcquireCredentialsHandle function.

The client calls the SSPI InitializeSecurityContext function to obtain an authentication request token (in our case, a Type 1 message). The client sends this token to the server. The return value from the function indicates that authentication will require multiple steps.

The server receives the token from the client, and uses it as input to the AcceptSecurityContext SSPI function. This creates a local security context on the server to represent the client, and yields an authentication response token (the Type 2 message), which is sent to the client. The return value from the function indicates that further information is needed from the client.

The client receives the response token from the server and calls InitializeSecurityContext again, passing the server's token as input. This provides us with another authentication request token (the Type 3 message). The return value indicates that the security context was successfully initialized; the token is sent to the server.

The server receives the token from the client and calls AcceptSecurityContext again, using the Type 3 message as input. The return value indicates the context was successfully accepted; no token is produced, and authentication is complete.

RFC 1734. On the client side, this mechanism is supported by Outlook and Outlook Express, and is called "Secure Password Authentication".

The POP3 NTLM authentication handshake occurs during the POP3 "authorization" state, and works as follows:

The client may request a list of supported authentication mechanisms by sending the AUTH command with no arguments:

AUTH

The server responds with a success message, followed by the list of supported mechanisms; this list should include "NTLM", and is terminated by a line containing a single period (".").

+OK The operation completed successfully.
NTLM
.

The client initiates NTLM authentication by sending an AUTH command specifying NTLM as the authentication mechanism:

AUTH NTLM

The server responds with a success message as shown below. Note that there is a space between the "+" and the "OK"; RFC 1734 states that the server should reply with a challenge, but NTLM requires the Type 1 message from the client. So the server sends a "non-challenge", which is basically the message "OK".

+ OK

The client then sends the Type 1 message, Base-64 encoded for transmission:

TlRMTVNTUAABAAAABzIAAAYABgArAAAACwALACAAAABXT1JLU1RBVElPTkRPTUFJTg==

The server replies with the Type 2 challenge message (again, Base-64 encoded). This is send in the challenge format specified by RFC 1734 ("+", followed by a space, followed by the challenge message). This is shown below; the line breaks are for editorial clarity and are not present in the server's reply:

The client initiates NTLM authentication by sending an AUTHENTICATE command specifying NTLM as the authentication mechanism:

0001 AUTHENTICATE NTLM

The server responds with an empty challenge, consisting simply of a "+":

+

The client then sends the Type 1 message, Base-64 encoded for transmission:

TlRMTVNTUAABAAAABzIAAAYABgArAAAACwALACAAAABXT1JLU1RBVElPTkRPTUFJTg==

The server replies with the Type 2 challenge message (again, Base-64 encoded). This is send in the challenge format specified by RFC 1730 ("+", followed by a space, followed by the challenge message). This is shown below; the line breaks are for editorial clarity and are not present in the server's reply:

The server may indicate support for NTLM as an authentication mechanism in the EHLO reply. Upon connecting to the SMTP server, the client would send the initial EHLO message:

EHLO client.example.com

The server responds with the list of supported extensions; the NTLM authentication extension is indicated by its presence in the list of AUTH mechanisms as shown below. Note that the AUTH list is sent twice (once with an "=" and once without). The "AUTH=" form was apparently specified in a draft of the RFC; sending both forms ensures that clients implemented against this draft are supported.

According to RFC 2554, the client may opt not to send the initial response parameter (instead merely sending "AUTH NTLM" and waiting for an empty server challenge before replying with the Type 1 message). However, this did not appear to work properly when tested against Exchange.

The server replies with a 334 response containing the Type 2 challenge message (again, Base-64 encoded). This is shown below; the line breaks are for editorial clarity and are not present in the server's reply:

http://jcifs.samba.org/jCIFS is an open-source Java implementation of CIFS/SMB. The information presented in this article was used as the basis for the jCIFS NTLM implementation. jCIFS provides support for both the client and server sides of the NTLM HTTP authentication scheme, as well as non-protocol-specific NTLM utility classes.