The NTLM Authentication Protocol

转载 2006年06月01日 09:24:00

Copyright © 2003 Eric Glass

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.

The most current version of this document may be obtained from http://davenport.sourceforge.net/ntlm.html.

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.

 

Contents


What is NTLM?

NTLM is an authentication protocol used in various Microsoft network protocol implementations and supported by the NTLM Security Support Provider ("NTLMSSP"). Originally used for authentication and negotiation of secure DCE/RPC, NTLM is also used throughout Microsoft's systems as an integrated single sign-on mechanism.

NTLM employs a challenge-response mechanism for authentication, in which clients are able to prove their identities without sending a password to the server. It consists of three messages, commonly referred to as Type 1 (negotiation), Type 2 (challenge) and Type 3 (authentication). It basically works like this:

 

  1. The client sends a Type 1 message to the server. This primarily contains a list of features supported by the client and requested of the server.
  2. The server responds with a Type 2 message. This contains a list of features supported and agreed upon by the server. Most importantly, however, it contains a challenge generated by the server.
  3. The client replies to the challenge with a Type 3 message. This contains several pieces of information about the client, including the domain and username of the client user. It also contains one or more responses to the Type 2 challenge.

The responses in the Type 3 message are the most critical piece, as they prove to the server that the client user has knowledge of the account password.

Local Authentication" section.

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:

 

    4e544c4d53535000020000000000000000000000020200000123456789abcdef

This message contains the NTLMSSP signature, the NTLM message type, an empty target name, minimal flags (Negotiate NTLM and Negotiate OEM), and the challenge.

datagram-style authentication, however.

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.

Implementing CIFS, especially the section on authentication.

The LM Response

The LM response is sent by most clients. This scheme is older than the NTLM response, and less secure. While newer clients support the NTLM response, they typically send both responses for compatibility with legacy servers; hence, the security flaws present in the LM response are still exhibited in many clients supporting the NTLM response.

The LM response is calculated as follows (see

Appendix A for a sample implementation in Java):

  1. The user's password (as an OEM string) is converted to uppercase.
  2. This password is either null-padded or truncated to 14 bytes.
  3. This "fixed" password is split into two 7-byte halves.
  4. These values are used to create two DES keys (one from each 7-byte half).
  5. Each of these keys is used to DES-encrypt the constant ASCII string "KGS!@#$%" (resulting in two 8-byte ciphertext values).
  6. These two ciphertext values are concatenated to form a 16-byte value - the LM hash.
  7. The 16-byte LM hash is null-padded to 21 bytes.
  8. This value is split into three 7-byte thirds.
  9. These values are used to create three DES keys (one from each 7-byte third).
  10. Each of these keys is used to DES-encrypt the challenge from the Type 2 message (resulting in three 8-byte ciphertext values).
  11. 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".

  1. The password (as an OEM string) is converted to uppercase, giving "SECRET01" (or "0x5345435245543031" in hexadecimal).
  2. This password is null-padded to 14 bytes, giving "0x5345435245543031000000000000".
  3. This value is split into two 7-byte halves, "0x53454352455430" and "0x31000000000000".
  4. 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:

    01010011 01000101 01000011 01010010 01000101 01010100 00110000

    A non-parity-adjusted DES key for this value would be:

    01010010 10100010 01010000 01101010 00100100 00101010 01010000 01100000

    (the parity bits are shown in red above). This is "0x52a2506a242a5060" in hexadecimal. Applying odd-parity to ensure that the total number of set bits in each octet is odd gives:

    01010010 10100010 01010001 01101011 00100101 00101010 01010001 01100001

    This is the first DES key ("0x52a2516b252a5161" in hex). We then apply the same process to our second 7-byte value, "0x31000000000000", represented in binary as:

    00110001 00000000 00000000 00000000 00000000 00000000 00000000

    Creating a non-parity-adjusted DES key gives:

    00110000 10000000 00000000 00000000 00000000 00000000 00000000 00000000

    ("0x3080000000000000" in hexadecimal). Adjusting the parity bits gives:

    00110001 10000000 00000001 00000001 00000001 00000001 00000001 00000001

    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.

  5. 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).
  6. These ciphertext values are concatenated to form our 16-byte LM hash - "0xff3750bcc2b22412c2265b23734e0dac".
  7. This is null-padded to 21 bytes, giving "0xff3750bcc2b22412c2265b23734e0dac0000000000".
  8. This value is split into three 7-byte thirds, "0xff3750bcc2b224", "0x12c2265b23734e" and "0x0dac0000000000".
  9. These three values are used to create three DES keys. Using the process outlined previously, our first value:

    11111111 00110111 01010000 10111100 11000010 10110010 00100100

    Gives us the parity-adjusted DES key:

    11111110 10011011 11010101 00010110 11001101 00010101 11001000 01001001

    ("0xfe9bd516cd15c849" in hexadecimal). The second value:

    00010010 11000010 00100110 01011011 00100011 01110011 01001110

    Results in the key:

    00010011 01100001 10001001 11001011 10110011 00011010 11001101 10011101

    ("0x136189cbb31acd9d"). Finally, the third value:

    00001101 10101100 00000000 00000000 00000000 00000000 00000000

    Gives us:

    00001101 11010110 00000001 00000001 00000001 00000001 00000001 00000001

    This is the third DES key ("0x0dd6010101010101").

  10. 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).
  11. 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 NTLM Response

The NTLM response is sent by newer clients. This scheme addresses some of the flaws in the LM response; however, it is still considered fairly weak. Additionally, the NTLM response is nearly always sent in conjunction with the LM response. The weaknesses in that algorithm can be exploited to obtain the case-insensitive password, and trial-and-error used to find the case-sensitive password employed by the NTLM response.

The NTLM response is calculated as follows (see

Appendix A for a sample Java implementation):

  1. 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.
  2. The 16-byte NTLM hash is null-padded to 21 bytes.
  3. This value is split into three 7-byte thirds.
  4. These values are used to create three DES keys (one from each 7-byte third).
  5. Each of these keys is used to DES-encrypt the challenge from the Type 2 message (resulting in three 8-byte ciphertext values).
  6. 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").

  1. The Unicode mixed-case password is "0x53006500630052004500740030003100" in hexadecimal; the MD4 hash of this value is calculated, giving "0xcd06ca7c7e10c99b1d33b7485a2ed808". This is the NTLM hash.
  2. This is null-padded to 21 bytes, giving "0xcd06ca7c7e10c99b1d33b7485a2ed8080000000000".
  3. This value is split into three 7-byte thirds, "0xcd06ca7c7e10c9", "0x9b1d33b7485a2e" and "0xd8080000000000".
  4. These three values are used to create three DES keys. Our first value:

    11001101 00000110 11001010 01111100 01111110 00010000 11001001

    Results in the parity-adjusted key:

    11001101 10000011 10110011 01001111 11000111 11110001 01000011 10010010

    ("0xcd83b34fc7f14392" in hexadecimal). The second value:

    10011011 00011101 00110011 10110111 01001000 01011010 00101110

    Gives the key:

    10011011 10001111 01001100 01110110 01110101 01000011 01101000 01011101

    ("0x9b8f4c767543685d"). Our third value:

    11011000 00001000 00000000 00000000 00000000 00000000 00000000

    Yields our third key:

    11011001 00000100 00000001 00000001 00000001 00000001 00000001 00000001

    ("0xd904010101010101" in hexadecimal).

  5. 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).
  6. These three ciphertext values are concatenated to form the 24-byte NTLM response:

    0x25a98c1c31e81847466b29b2df4680f39958fb8c213a9cc6

The NTLMv2 Response

NTLM version 2 ("NTLMv2") was concocted to address the security issues present in NTLM. While its effectiveness in this regard is questionable, it does at least provide a more secure replacement for the LM response. When NTLMv2 is enabled, the NTLM response is replaced with the NTLMv2 response, and the LM response is replaced with the LMv2 response (which we will discuss next).

The NTLMv2 response is calculated as follows (see

Appendix A for a sample implementation in Java):

  1. The NTLM password hash is obtained (as discussed previously, this is the MD4 digest of the Unicode mixed-case password).
  2. 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.
  3. 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
  4. 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.
  5. 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:

Domain: DOMAIN
Username: user
Password: SecREt01
Challenge: 0x0123456789abcdef
Target Information:
0x02000c0044004f00
  4d00410049004e00
  01000c0053004500
  5200560045005200
  0400140064006f00
  6d00610069006e00
  2e0063006f006d00
  0300220073006500
  7200760065007200
  2e0064006f006d00
  610069006e002e00
  63006f006d000000
  0000

  1. The Unicode mixed-case password is "0x53006500630052004500740030003100" in hexadecimal; the MD4 hash of this value is calculated, giving "0xcd06ca7c7e10c99b1d33b7485a2ed808". This is the NTLM hash.
  2. 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.
  3. 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:

    0x01010000 (the blob signature)
    0x00000000 (reserved value)
    0x0090d336b734c301 (our timestamp)
    0xffffff0011223344 (a random client challenge)
    0x00000000 (unknown, but zero will work)
    0x02000c0044004f00
      4d00410049004e00
      01000c0053004500
      5200560045005200
      0400140064006f00
      6d00610069006e00
      2e0063006f006d00
      0300220073006500
      7200760065007200
      2e0064006f006d00
      610069006e002e00
      63006f006d000000
      0000
    (our target information block)
    0x00000000 (unknown, but zero will work)

  4. We then concatenate the Type 2 challenge with our blob:
    0x0123456789abcdef0101000000000000
      0090d336b734c301ffffff0011223344
      0000000002000c0044004f004d004100
      49004e0001000c005300450052005600
      450052000400140064006f006d006100
      69006e002e0063006f006d0003002200
      7300650072007600650072002e006400
      6f006d00610069006e002e0063006f00
      6d000000000000000000
    

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

  5. This value is concatenated with the blob to obtain the NTLMv2 response:
    0xcbabbca713eb795d04c97abc01ee4983
      01010000000000000090d336b734c301
      ffffff00112233440000000002000c00
      44004f004d00410049004e0001000c00
      53004500520056004500520004001400
      64006f006d00610069006e002e006300
      6f006d00030022007300650072007600
      650072002e0064006f006d0061006900
      6e002e0063006f006d00000000000000
      0000
    

The LMv2 Response

The LMv2 response is used to provide pass-through authentication compatibility with older servers. It is quite possible that the server with which the client is communicating will not actually perform the authentication; rather, it will pass the responses through to a domain controller for verification. Older servers pass only the LM response, and expect it to be exactly 24 bytes. The LMv2 response was designed to allow such servers to operate properly; it is effectively a "miniature" NTLMv2 response, obtained as follows (see

Appendix A for a sample Java implementation):

  1. The NTLM password hash is calculated (the MD4 digest of the Unicode mixed-case password).
  2. 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.
  3. A random 8-byte client challenge is created (this is the same client challenge used in the NTLMv2 blob).
  4. 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.
  5. 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

  1. The Unicode mixed-case password is "0x53006500630052004500740030003100" in hexadecimal; the MD4 hash of this value is calculated, giving "0xcd06ca7c7e10c99b1d33b7485a2ed808". This is the NTLM hash.
  2. 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.
  3. A random 8-byte client challenge is created. From our NTLMv2 example, we will use "0xffffff0011223344".
  4. 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".

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

    0xd6e6152ea25d03b7c6ba6629c2d6aaf0ffffff0011223344

The NTLM2 Session Response

The NTLM2 session response can be employed in conjunction with NTLM2 session security (it is made available with the "Negotiate NTLM2 Key" flag). This is used to provide enhanced protection against precomputed dictionary attacks in environments which do not support NTLMv2 authentication.

The calculation of the NTLM2 session response is similar to the NTLM response; it effectively replaces both the LM and NTLM response fields as follows (see

Appendix A for a sample implementation in Java):

  1. A random 8-byte client challenge is created.
  2. The client challenge is null-padded to 24 bytes. This value is placed in the LM response field of the Type 3 message.
  3. The challenge from the Type 2 message is concatenated with the 8-byte client challenge to form a session nonce.
  4. The MD5 message-digest algorithm (described in RFC 1321) is applied to the session nonce, resulting in a 16-byte value.
  5. This value is truncated to 8 bytes to form the NTLM2 session hash.
  6. The NTLM password hash is obtained (as discussed, this is the MD4 digest of the Unicode mixed-case password).
  7. The 16-byte NTLM hash is null-padded to 21 bytes.
  8. This value is split into three 7-byte thirds.
  9. These values are used to create three DES keys (one from each 7-byte third).
  10. Each of these keys is used to DES-encrypt the NTLM2 session hash (resulting in three 8-byte ciphertext values).
  11. 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"):

  1. A random 8-byte client challenge is created; we will use "0xffffff0011223344", as in the previous examples.
  2. The challenge is null-padded to 24 bytes:

    0xffffff001122334400000000000000000000000000000000

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

  3. The challenge from the Type 2 message is concatenated with the client challenge, forming a session nonce ("0x0123456789abcdefffffff0011223344").
  4. Applying the MD5 digest to this nonce yields the 16-byte value "0xbeac9a1bc5a9867c15192b3105d5beb1".
  5. This is truncated to 8 bytes to obtain the NTLM2 session hash ("0xbeac9a1bc5a9867c").
  6. The Unicode mixed-case password is "0x53006500630052004500740030003100"; applying the MD4 digest to this value gives us the NTLM hash ("0xcd06ca7c7e10c99b1d33b7485a2ed808").
  7. This is null-padded to 21 bytes, giving "0xcd06ca7c7e10c99b1d33b7485a2ed8080000000000".
  8. This value is split into three 7-byte thirds, "0xcd06ca7c7e10c9", "0x9b1d33b7485a2e" and "0xd8080000000000".
  9. These values are used to create three DES keys (as calculated in our previous NTLM response example, "0xcd83b34fc7f14392", "0x9b8f4c767543685d", and "0xd904010101010101").
  10. 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).
  11. These three ciphertext values are concatenated to form the 24-byte NTLM2 session response:

    0x10d550832d12b2ccb79d5ad1f4eed3df82aca4c3681dd455

    which is placed in the NTLM response field of the Type 3 message.

Type 3 Message Example

Now that we're familiar with the Type 3 responses, we are ready to examine a Type 3 Message:

 

    4e544c4d5353500003000000180018006a00000018001800
    820000000c000c0040000000080008004c00000016001600
    54000000000000009a0000000102000044004f004d004100
    49004e00750073006500720057004f0052004b0053005400
    4100540049004f004e00c337cd5cbd44fc9782a667af6d42
    7c6de67c20c2d3e77c5625a98c1c31e81847466b29b2df46
    80f39958fb8c213a9cc6

This message is decomposed as:

0 0x4e544c4d53535000 NTLMSSP Signature
8 0x03000000 Type 3 Indicator
12 0x180018006a000000 LM Response Security Buffer:

Length: 24 bytes (0x1800)
Allocated Space: 24 bytes (0x1800)
Offset: 106 bytes (0x6a000000)

20 0x1800180082000000 NTLM Response Security Buffer:

Length: 24 bytes (0x1800)
Allocated Space: 24 bytes (0x1800)
Offset: 130 bytes (0x82000000)

28 0x0c000c0040000000 Domain Name Security Buffer:

Length: 12 bytes (0x0c00)
Allocated Space: 12 bytes (0x0c00)
Offset: 64 bytes (0x40000000)

36 0x080008004c000000 User Name Security Buffer:

Length: 8 bytes (0x0800)
Allocated Space: 8 bytes (0x0800)
Offset: 76 bytes (0x4c000000)

44 0x1600160054000000 Workstation Name Security Buffer:

Length: 22 bytes (0x1600)
Allocated Space: 22 bytes (0x1600)
Offset: 84 bytes (0x54000000)

52 0x000000009a000000 Session Key Security Buffer:

Length: 0 bytes (0x0000)
Allocated Space: 0 bytes (0x0000)
Offset: 154 bytes (0x9a000000)

60 0x01020000 Flags:

Negotiate Unicode (0x00000001)
Negotiate NTLM (0x00000200)

64
0x44004f004d004100
  49004e00
Domain Name Data ("DOMAIN")
76 0x7500730065007200 User Name Data ("user")
84
0x57004f0052004b00
  5300540041005400
  49004f004e00
Workstation Name Data ("WORKSTATION")
106
0xc337cd5cbd44fc97
  82a667af6d427c6d
  e67c20c2d3e77c56
LM Response Data
130
0x25a98c1c31e81847
  466b29b2df4680f3
  9958fb8c213a9cc6
NTLM Response Data

Analysis of this reveals:

  • This is an NTLM Type 3 message (from the NTLMSSP Signature and Type 3 Indicator).
  • The client has indicated that strings are encoded using Unicode (the Negotiate Unicode flag is set).
  • The client supports NTLM authentication (Negotiate NTLM).
  • The client's domain is "DOMAIN".
  • The client's username is "user".
  • The client's workstation is "WORKSTATION".
  • The client's LM response is "0xc337cd5cbd44fc9782a667af6d427c6de67c20c2d3e77c56".
  • The client's NTLM response is "0x25a98c1c31e81847466b29b2df4680f39958fb8c213a9cc6".
  • An empty session key has been sent.

Upon receipt of the Type 3 message, the server calculates the LM and NTLM responses and compares them to the values provided by the client; if they match, the user is successfully authenticated.

Microsoft Knowledge Base Article 239869; briefly, a modification is made to the registy value:

    HKEY_LOCAL_MACHINE/System/CurrentControlSet/Control/LSA/LMCompatibilityLevel

(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:

 

  1. The client obtains a representation of the credential set for the user via the SSPI AcquireCredentialsHandle function.
  2. 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.
  3. 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.
  4. 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.
  5. 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.

Local Authentication

We have alluded to the local authentication sequence at various points in our discussion; having a basic understanding of SSPI, we can look at this scenario in more detail.

Local authentication is negotiated through a series of decisions made by the client and server, based on the information in the NTLM messages. It works as follows:

 

  1. The client calls the AcquireCredentialsHandle function, specifying the default credentials by passing in null to the "pAuthData" parameter. This obtains a handle to the credentials of the logged in user for single sign-on.
  2. The client calls the SSPI InitializeSecurityContext function to create the Type 1 message. When the default credential handle is supplied, the Type 1 message contains the workstation and domain name of the client. This is indicated by the presence of the "Negotiate Domain Supplied" and "Negotiate Workstation Supplied" flags, and the inclusion of populated Supplied Domain and Supplied Workstation security buffers in the message.
  3. The server receives the Type 1 message from the client, and calls AcceptSecurityContext. This creates a local security context on the server to represent the client. The server examines the domain and workstation information sent by the client to determine if the client and server are the same machine. If so, the server initiates local authentication by setting the "Negotiate Local Call" flag in the resultant Type 2 message. The first long in the Context field of the Type 2 message is populated with the "upper" portion of the newly obtained SSPI context handle (specifically, the "dwUpper" field of the SSPI CtxtHandle structure). The second long in the Context field appears to be empty in all cases. (although logically one would assume it should contain the "lower" portion of the context handle).
  4. The client receives the Type 2 message from the server and passes it to InitializeSecurityContext. Having noted the presence of the "Negotiate Local Call" flag, the client examines the server context handle to determine if it represents a valid local security context. If the context cannot be validated, authentication proceeds as usual - the appropriate responses are calculated, and included with the domain, workstation, and username in the Type 3 message. If the security context handle from the Type 2 message can be validated, however, no responses are prepared whatsoever. Instead, the default credentials are internally associated with the server context. The resulting Type 3 message is completely empty, containing zero-length security buffers for the responses as well as the username, domain, and workstation.
  5. The server receives the Type 3 message and uses it as input to the AcceptSecurityContext function. The server verifies that the security context has been associated with a user; if so, authentication has successfully completed. If the context has not been bound to a user, authentication fails.

Datagram Authentication

Datagram-style authentication is used to negotiate NTLM over a connectionless transport. While much of the semantics around the messages remain unchanged, there are a few significant differences:

 

  • SSPI does not create a Type 1 message during the first call to InitializeSecurityContext.
  • Authentication options are offered by the server, rather than requested by the client.
  • Rather than being superfluous (as in connection-oriented authentication), the flags in the Type 3 message carry their usual meanings.

During "normal" (connection-oriented) authentication, all options are negotiated in the first transaction between the client and the server, during the exchange of the Type 1 and Type 2 messages. The negotiated settings are "remembered" by the server and applied to the client's Type 3 message. Although most clients send the agreed-upon flags with the Type 3 message, they are not used in connection authentication.

In datagram authentication, however, the game changes a bit; to alleviate the server's need to track the negotiated options (which becomes more difficult without a persistent connection), the Type 1 message is removed completely. The server generates a Type 2 message containing all supported flags (as well as the challenge, of course). The client then decides which options it will support, and replies with a Type 3 message containing the responses to the challenge and the set of selected flags. The SSPI handshake sequence for datagram authentication is as follows:

 

  1. The client calls AcquireCredentialsHandle to obtain a representation of the credential set for the user.
  2. The client calls InitializeSecurityContext, passing the ISC_REQ_DATAGRAM flag as a context requirement via the fContextReq parameter. This starts the construction of the client's security context, but does not produce a request token (Type 1 message).
  3. The server calls the AcceptSecurityContext function, specifying the ASC_REQ_DATAGRAM context requirement flag and passing in a null input token. This creates the local security context and yields an authentication response token (the Type 2 message). This Type 2 message will contain the "Negotiate Datagram Style" flag, as well as all flags supported by the server. This is sent to the client as usual.
  4. The client receives the Type 2 message and passes it to InitializeSecurityContext. The client selects appropriate options from those presented by the server (including "Negotiate Datagram Style", which must be set), creates the responses to the challenge, and populates the Type 3 message. The message is then relayed to the server.
  5. The server passes the Type 3 message into the AcceptSecurityContext function. The message is processed according to the flags selected by the client, and the context is successfully accepted.

When used with SSPI, there is apparently no means of producing a datagram-style Type 1 message. It is interesting to note, however, that we can "induce" datagram semantics at a lower level by subtly manipulating the NTLMSSP tokens to produce our own datagram Type 1 token.

This can be achieved by setting the "Negotiate Datagram Style" flag on the Type 1 message produced by the first InitializeSecurityContext call in a connection-oriented SSPI handshake before passing the token to the server. When the modified Type 1 message is passed into the AcceptSecurityContext function, the server will adopt datagram semantics (even though ASC_REQ_DATAGRAM was not specified). This will produce a Type 2 message with the "Negotiate Datagram Style" flag set, but otherwise identical to the connection-oriented message that would normally have been generated; that is, the Type 1 flags sent by the client are considered during the construction of the Type 2 message, rather than simply offering all supported options.

The client can then call InitializeSecurityContext with this Type 2 token. Note that the client is still in connection-oriented mode; the Type 3 message produced will ignore the "Negotiate Datagram Style" flag applied to the Type 2 message. The server, however, is enforcing datagram semantics, and will now require the Type 3 flags to be set appropriately. Adding the "Negotiate Datagram Style" flag to the Type 3 message manually before sending it to the server allows the server to successfully call AcceptSecurityContext with the modified token.

This results in successful authentication; the "doctored" Type 1 message effectively switches the server into datagram-style authentication, in which the Type 3 flags are observed and enforced. There is no known practical use for this, but it does demonstrate some of the interesting and unexpected behavior that can be observed by strategically manipulating the NTLM messages.

At this point, we have established a fairly decent fundamental understanding of NTLM. We will now examine its use within some of Microsoft's network protocol implementations.

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:

  1. The client may request a list of supported authentication mechanisms by sending the AUTH command with no arguments:
        AUTH
    
  2. 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
        .
    
  3. The client initiates NTLM authentication by sending an AUTH command specifying NTLM as the authentication mechanism:
        AUTH NTLM
    
  4. 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
    
  5. The client then sends the Type 1 message, Base-64 encoded for transmission:
        TlRMTVNTUAABAAAABzIAAAYABgArAAAACwALACAAAABXT1JLU1RBVElPTkRPTUFJTg==
    
  6. 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:
        + TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAA
        RABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZA
        BvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAu
        AGMAbwBtAAAAAAA=
    
  7. The client calculates and sends the Base-64 encoded Type 3 response (the line breaks below are for display purposes only):
        TlRMTVNTUAADAAAAGAAYAGoAAAAYABgAggAAAAwADABAAAAACAAIAEwAAAAWABYAVA
        AAAAAAAACaAAAAAQIAAEQATwBNAEEASQBOAHUAcwBlAHIAVwBPAFIASwBTAFQAQQBU
        AEkATwBOAMM3zVy9RPyXgqZnr21CfG3mfCDC0+d8ViWpjBwx6BhHRmspst9GgPOZWP
        uMITqcxg==
    
  8. The server validates the response and indicates the result of authentication:
        +OK User successfully logged on
    

After successful authentication has occurred, the POP3 session enters the "transaction" state, allowing messages to be retrieved by the client.

RFC 1730; the NTLM mechanism is a proprietary extension provided by Exchange and supported by the Outlook client family.

The handshake sequence is similar to the POP3 mechanism:

  1. The server may indicate support for the NTLM authentication mechanism in the capability response. Upon connecting to the IMAP server, the client would request the list of server capabilities:
        0000 CAPABILITY
    
  2. The server responds with the list of supported capabilities; the NTLM authentication extension is indicated by the presence of the string "AUTH=NTLM" in the server's reply:
        * CAPABILITY IMAP4 IMAP4rev1 IDLE LITERAL+ AUTH=NTLM
        0000 OK CAPABILITY completed.
    
  3. The client initiates NTLM authentication by sending an AUTHENTICATE command specifying NTLM as the authentication mechanism:
        0001 AUTHENTICATE NTLM
    
  4. The server responds with an empty challenge, consisting simply of a "+":
        +
    
  5. The client then sends the Type 1 message, Base-64 encoded for transmission:
        TlRMTVNTUAABAAAABzIAAAYABgArAAAACwALACAAAABXT1JLU1RBVElPTkRPTUFJTg==
    
  6. 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:
        + TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAA
        RABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZA
        BvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAu
        AGMAbwBtAAAAAAA=
    
  7. The client calculates and sends the Base-64 encoded Type 3 response (the line breaks below are for display purposes only):
        TlRMTVNTUAADAAAAGAAYAGoAAAAYABgAggAAAAwADABAAAAACAAIAEwAAAAWABYAVA
        AAAAAAAACaAAAAAQIAAEQATwBNAEEASQBOAHUAcwBlAHIAVwBPAFIASwBTAFQAQQBU
        AEkATwBOAMM3zVy9RPyXgqZnr21CfG3mfCDC0+d8ViWpjBwx6BhHRmspst9GgPOZWP
        uMITqcxg==
    
  8. The server validates the response and indicates the result of authentication:
        0001 OK AUTHENTICATE NTLM completed.
    
After authentication has completed, the IMAP session enters the authenticated state.

NTLM SMTP Authentication

In addition to the NTLM authentication mechanisms provided for POP3 and IMAP, Exchange provides similar functionality for the SMTP protocol. This allows NTLM authentication of users sending outgoing mail messages. This is a proprietary extension used with the SMTP AUTH command (documented in

RFC 2554).

The SMTP NTLM authentication handshake operates as follows:

  1. 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
    
  2. 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.
        250-server.example.com Hello [10.10.2.20]
        250-HELP
        250-AUTH LOGIN NTLM
        250-AUTH=LOGIN NTLM
        250 SIZE 10240000
    
  3. The client initiates NTLM authentication by sending an AUTH command specifying NTLM as the authentication mechanism and providing the Base-64 encoded Type 1 message as a parameter:
        AUTH NTLM TlRMTVNTUAABAAAABzIAAAYABgArAAAACwALACAAAABXT1JLU1RBVElPTkRPTUFJTg==
    

    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.

  4. 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:
        334 TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAA
        RABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZA
        BvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAu
        AGMAbwBtAAAAAAA=
    
  5. The client calculates and sends the Base-64 encoded Type 3 response (the line breaks below are for display purposes only):
        TlRMTVNTUAADAAAAGAAYAGoAAAAYABgAggAAAAwADABAAAAACAAIAEwAAAAWABYAVA
        AAAAAAAACaAAAAAQIAAEQATwBNAEEASQBOAHUAcwBlAHIAVwBPAFIASwBTAFQAQQBU
        AEkATwBOAMM3zVy9RPyXgqZnr21CfG3mfCDC0+d8ViWpjBwx6BhHRmspst9GgPOZWP
        uMITqcxg==
    
  6. The server validates the response and indicates the result of authentication:
        235 NTLM authentication successful.
    

After authenticating, the client is able to send messages normally.

The jCIFS Project Home Page
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.
Implementing CIFS: The Common Internet FileSystem
http://ubiqx.org/cifs/
A highly informative online book by Christopher R. Hertel. Especially relevant to this discussion is the section on authentication.
The Open Group ActiveX Core Technology Reference (Chapter 11, "NTLM")
http://www.opengroup.org/comsource/techref2/NCH1222X.HTM
Closest thing to an "official" reference on NTLM. Unfortunately, also rather old and not terribly accurate.
The Security Support Provider Interface
http://www.microsoft.com/windows2000/techinfo/howitworks/security/sspi2000.asp
A whitepaper discussing application development using the SSPI.
NTLM Authentication Scheme for HTTP
http://www.innovation.ch/java/ntlm.html
Informative discussion on the NTLM HTTP authentication mechanism. Inaccurate in some regards, but still quite useful.
Squid NTLM Authentication Project
http://squid.sourceforge.net/ntlm/
Project to provide NTLM HTTP authentication to the Squid proxy server.
Jakarta Commons HttpClient
http://jakarta.apache.org/commons/httpclient/
An open-source Java HTTP client which provides support for the NTLM HTTP authentication scheme.
The GNU Crypto Project
http://www.gnu.org/software/gnu-crypto/
An open-source Java Cryptography Extension provider supplying an implementation of the MD4 message-digest algorithm.
RFC 1320 - The MD4 Message-Digest Algorithm
http://www.ietf.org/rfc/rfc1320.txt
Specification and reference implementation for the MD4 digest (used to calculate the NTLM password hash).
RFC 1321 - The MD5 Message-Digest Algorithm
http://www.ietf.org/rfc/rfc1321.txt
Specification and reference implementation for the MD5 digest (used to calculate the NTLM2 session response).
RFC 2104 - HMAC: Keyed-Hashing for Message Authentication
http://www.ietf.org/rfc/rfc2104.txt
Specification and reference implementation for the HMAC-MD5 algorithm (used in the calculation of the NTLMv2/LMv2 responses).
How to Enable NTLM 2 Authentication
http://support.microsoft.com/default.aspx?scid=KB;en-us;239869
Describes how to enable negotiation of NTLMv2 authentication and enforce NTLM security flags.

Appendix A: Java Implementation of the Type 3 Response Calculations

Listed below is an annotated sample implementation of the various Type 3 response calculations in Java. This example requires a JCE provider implementing the MD4 message-digest algorithm; the author recommends GNU Crypto, available at

http://www.gnu.org/software/gnu-crypto/.

 

import java.security.Key;
import java.security.MessageDigest;

import javax.crypto.Cipher;

import javax.crypto.spec.SecretKeySpec;

/**
 * Calculates the various Type 3 responses.
 */
public class Responses {

    /**
     * Calculates the LM Response for the given challenge, using the specified
     * password.
     *
     * @param password The user's password.
     * @param challenge The Type 2 challenge from the server.
     *
     * @return The LM Response.
     */
    public static byte[] getLMResponse(String password, byte[] challenge)
            throws Exception {
        byte[] lmHash = lmHash(password);
        return lmResponse(lmHash, challenge);
    }

    /**
     * Calculates the NTLM Response for the given challenge, using the
     * specified password.
     *
     * @param password The user's password.
     * @param challenge The Type 2 challenge from the server.
     *
     * @return The NTLM Response.
     */
    public static byte[] getNTLMResponse(String password, byte[] challenge)
            throws Exception {
        byte[] ntlmHash = ntlmHash(password);
        return lmResponse(ntlmHash, challenge);
    }

    /**
     * Calculates the NTLMv2 Response for the given challenge, using the
     * specified authentication target, username, password, target information
     * block, and client challenge.
     *
     * @param target The authentication target (i.e., domain).
     * @param user The username. 
     * @param password The user's password.
     * @param targetInformation The target information block from the Type 2
     * message.
     * @param challenge The Type 2 challenge from the server.
     * @param clientChallenge The random 8-byte client challenge. 
     *
     * @return The NTLMv2 Response.
     */
    public static byte[] getNTLMv2Response(String target, String user,
            String password, byte[] targetInformation, byte[] challenge,
                    byte[] clientChallenge) throws Exception {
        byte[] ntlmv2Hash = ntlmv2Hash(target, user, password);
        byte[] blob = createBlob(targetInformation, clientChallenge);
        return lmv2Response(ntlmv2Hash, blob, challenge);
    }

    /**
     * Calculates the LMv2 Response for the given challenge, using the
     * specified authentication target, username, password, and client
     * challenge.
     *
     * @param target The authentication target (i.e., domain).
     * @param user The username.
     * @param password The user's password.
     * @param challenge The Type 2 challenge from the server.
     * @param clientChallenge The random 8-byte client challenge.
     *
     * @return The LMv2 Response. 
     */
    public static byte[] getLMv2Response(String target, String user,
            String password, byte[] challenge, byte[] clientChallenge)
                    throws Exception {
        byte[] ntlmv2Hash = ntlmv2Hash(target, user, password);
        return lmv2Response(ntlmv2Hash, clientChallenge, challenge);
    }

    /**
     * Calculates the NTLM2 Session Response for the given challenge, using the
     * specified password and client challenge.
     *
     * @param password The user's password.
     * @param challenge The Type 2 challenge from the server.
     * @param clientChallenge The random 8-byte client challenge.
     *
     * @return The NTLM2 Session Response.  This is placed in the NTLM
     * response field of the Type 3 message; the LM response field contains
     * the client challenge, null-padded to 24 bytes.
     */
    public static byte[] getNTLM2SessionResponse(String password,
            byte[] challenge, byte[] clientChallenge) throws Exception {
        byte[] ntlmHash = ntlmHash(password);
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        md5.update(challenge);
        md5.update(clientChallenge);
        byte[] sessionHash = new byte[8];
        System.arraycopy(md5.digest(), 0, sessionHash, 0, 8);
        return lmResponse(ntlmHash, sessionHash);
    }

    /**
     * Creates the LM Hash of the user's password.
     *
     * @param password The password.
     *
     * @return The LM Hash of the given password, used in the calculation
     * of the LM Response.
     */
    private static byte[] lmHash(String password) throws Exception {
        byte[] oemPassword = password.toUpperCase().getBytes("US-ASCII");
        int length = Math.min(oemPassword.length, 14);
        byte[] keyBytes = new byte[14];
        System.arraycopy(oemPassword, 0, keyBytes, 0, length);
        Key lowKey = createDESKey(keyBytes, 0);
        Key highKey = createDESKey(keyBytes, 7);
        byte[] magicConstant = "KGS!@#$%".getBytes("US-ASCII");
        Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
        des.init(Cipher.ENCRYPT_MODE, lowKey);
        byte[] lowHash = des.doFinal(magicConstant);
        des.init(Cipher.ENCRYPT_MODE, highKey);
        byte[] highHash = des.doFinal(magicConstant);
        byte[] lmHash = new byte[16];
        System.arraycopy(lowHash, 0, lmHash, 0, 8);
        System.arraycopy(highHash, 0, lmHash, 8, 8);
        return lmHash;
    }

    /**
     * Creates the NTLM Hash of the user's password.
     *
     * @param password The password.
     *
     * @return The NTLM Hash of the given password, used in the calculation
     * of the NTLM Response and the NTLMv2 and LMv2 Hashes.
     */
    private static byte[] ntlmHash(String password) throws Exception {
        byte[] unicodePassword = password.getBytes("UnicodeLittleUnmarked");
        MessageDigest md4 = MessageDigest.getInstance("MD4");
        return md4.digest(unicodePassword);
    }

    /**
     * Creates the NTLMv2 Hash of the user's password.
     *
     * @param target The authentication target (i.e., domain).
     * @param user The username.
     * @param password The password.
     *
     * @return The NTLMv2 Hash, used in the calculation of the NTLMv2
     * and LMv2 Responses. 
     */
    private static byte[] ntlmv2Hash(String target, String user,
            String password) throws Exception {
        byte[] ntlmHash = ntlmHash(password);
        String identity = user.toUpperCase() + target.toUpperCase();
        return hmacMD5(identity.getBytes("UnicodeLittleUnmarked"), ntlmHash);
    }

    /**
     * Creates the LM Response from the given hash and Type 2 challenge.
     *
     * @param hash The LM or NTLM Hash.
     * @param challenge The server challenge from the Type 2 message.
     *
     * @return The response (either LM or NTLM, depending on the provided
     * hash).
     */
    private static byte[] lmResponse(byte[] hash, byte[] challenge)
            throws Exception {
        byte[] keyBytes = new byte[21];
        System.arraycopy(hash, 0, keyBytes, 0, 16);
        Key lowKey = createDESKey(keyBytes, 0);
        Key middleKey = createDESKey(keyBytes, 7);
        Key highKey = createDESKey(keyBytes, 14);
        Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
        des.init(Cipher.ENCRYPT_MODE, lowKey);
        byte[] lowResponse = des.doFinal(challenge);
        des.init(Cipher.ENCRYPT_MODE, middleKey);
        byte[] middleResponse = des.doFinal(challenge);
        des.init(Cipher.ENCRYPT_MODE, highKey);
        byte[] highResponse = des.doFinal(challenge);
        byte[] lmResponse = new byte[24];
        System.arraycopy(lowResponse, 0, lmResponse, 0, 8);
        System.arraycopy(middleResponse, 0, lmResponse, 8, 8);
        System.arraycopy(highResponse, 0, lmResponse, 16, 8);
        return lmResponse;
    }

    /**
     * Creates the LMv2 Response from the given hash, client data, and
     * Type 2 challenge.
     *
     * @param hash The NTLMv2 Hash.
     * @param clientData The client data (blob or client challenge).
     * @param challenge The server challenge from the Type 2 message.
     *
     * @return The response (either NTLMv2 or LMv2, depending on the
     * client data).
     */
    private static byte[] lmv2Response(byte[] hash, byte[] clientData,
            byte[] challenge) throws Exception {
        byte[] data = new byte[challenge.length + clientData.length];
        System.arraycopy(challenge, 0, data, 0, challenge.length);
        System.arraycopy(clientData, 0, data, challenge.length,
                         clientData.length);
        byte[] mac = hmacMD5(data, hash);
        byte[] lmv2Response = new byte[mac.length + clientData.length];
        System.arraycopy(mac, 0, lmv2Response, 0, mac.length);
        System.arraycopy(clientData, 0, lmv2Response, mac.length,
                         clientData.length);
        return lmv2Response;
    }

    /**
     * Creates the NTLMv2 blob from the given target information block and
     * client challenge.
     *
     * @param targetInformation The target information block from the Type 2
     * message.
     * @param clientChallenge The random 8-byte client challenge.
     *
     * @return The blob, used in the calculation of the NTLMv2 Response.
     */
    private static byte[] createBlob(byte[] targetInformation,
            byte[] clientChallenge) {
        byte[] blobSignature = new byte[] {
            (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00
        };
        byte[] reserved = new byte[] {
            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
        };
        byte[] unknown1 = new byte[] {
            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
        };
        byte[] unknown2 = new byte[] {
            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
        };
        long time = System.currentTimeMillis();
        time += 11644473600000l; // milliseconds from January 1, 1601 -> epoch.
        time *= 10000; // tenths of a microsecond.
        // convert to little-endian byte array.
        byte[] timestamp = new byte[8];
        for (int i = 0; i < 8; i++) {
            timestamp[i] = (byte) time;
            time >>>= 8;
        }
        byte[] blob = new byte[blobSignature.length + reserved.length +
                               timestamp.length + clientChallenge.length +
                               unknown1.length + targetInformation.length +
                               unknown2.length];
        int offset = 0;
        System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length);
        offset += blobSignature.length;
        System.arraycopy(reserved, 0, blob, offset, reserved.length);
        offset += reserved.length;
        System.arraycopy(timestamp, 0, blob, offset, timestamp.length);
        offset += timestamp.length;
        System.arraycopy(clientChallenge, 0, blob, offset,
                         clientChallenge.length);
        offset += clientChallenge.length;
        System.arraycopy(unknown1, 0, blob, offset, unknown1.length);
        offset += unknown1.length;
        System.arraycopy(targetInformation, 0, blob, offset,
                         targetInformation.length);
        offset += targetInformation.length;
        System.arraycopy(unknown2, 0, blob, offset, unknown2.length);
        return blob;
    }

    /**
     * Calculates the HMAC-MD5 hash of the given data using the specified
     * hashing key.
     *
     * @param data The data for which the hash will be calculated. 
     * @param key The hashing key.
     *
     * @return The HMAC-MD5 hash of the given data.
     */
    private static byte[] hmacMD5(byte[] data, byte[] key) throws Exception {
        byte[] ipad = new byte[64];
        byte[] opad = new byte[64];
        for (int i = 0; i < 64; i++) {
            ipad[i] = (byte) 0x36;
            opad[i] = (byte) 0x5c;
        }
        for (int i = key.length - 1; i >= 0; i--) {
            ipad[i] ^= key[i];
            opad[i] ^= key[i];
        }
        byte[] content = new byte[data.length + 64];
        System.arraycopy(ipad, 0, content, 0, 64);
        System.arraycopy(data, 0, content, 64, data.length);
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        data = md5.digest(content);
        content = new byte[data.length + 64];
        System.arraycopy(opad, 0, content, 0, 64);
        System.arraycopy(data, 0, content, 64, data.length);
        return md5.digest(content);
    }

    /**
     * Creates a DES encryption key from the given key material.
     *
     * @param bytes A byte array containing the DES key material.
     * @param offset The offset in the given byte array at which
     * the 7-byte key material starts.
     *
     * @return A DES encryption key created from the key material
     * starting at the specified offset in the given byte array.
     */
    private static Key createDESKey(byte[] bytes, int offset) {
        byte[] keyBytes = new byte[7];
        System.arraycopy(bytes, offset, keyBytes, 0, 7);
        byte[] material = new byte[8];
        material[0] = keyBytes[0];
        material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1);
        material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2);
        material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3);
        material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4);
        material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5);
        material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6);
        material[7] = (byte) (keyBytes[6] << 1);
        oddParity(material);
        return new SecretKeySpec(material, "DES");
    }

SourceForge.net Logo

All trademarks mentioned in this document are the property of their respective owners.

相关文章推荐

The NTLM Authentication Protocol and Security Support Provider (中文翻译)

正在学习Windows身份验证方面的东西,觉得这篇文章写的比较深入。没有找到中文版的,就花些时间把它翻译一下,希望对大家有所帮助。仅仅出于个人兴趣进行翻译,限于个人水平,难免有错误纰漏,希望大家指正。...
  • oxzhao
  • oxzhao
  • 2011年07月05日 04:59
  • 540

NTLM 协议

早期SMB协议在网络上传输明文口令。后来出现 LAN Manager Challenge/Response 验证机制,简称LM,它是如此简单以至很容易就被破解。微软提出了WindowsNT挑战/响应验...

如何破解Windows密码[LM/NTLMv2介绍]

如何破解Windows密码[LM/NTLMv2介绍]原创文章如转载,请注明:转载自冠威博客 [ http://www.guanwei.org/ ] 本文链接地址:http://www.guanwei....
  • xcntime
  • xcntime
  • 2010年12月02日 20:30
  • 2861

小心Windows旧版认证暴露你的系统帐户密码 ntlmv2破解

当你正用浏览器访问网站时,Windows的一个古老漏洞就可能泄露了你电脑的用户名密码、微软登录信息,甚至还有你VPN的账号密码。 多年以前当Windows还在使用单核处理器和256M的RAM的时...

JavaMail出现535 5.7.3 Authentication unsuccessful错误的解决方法

最近实现使用公司内部邮箱发送邮件功能,发送给本公司邮箱时没有问题,但是给QQ、163或sina等外部邮箱时一直出现错误: com.sun.mail.smtp.SMTPAddressFailedExce...

java Mail发邮件 smtp被TLS加密认证不了的解决方案

开始测试前,要确保发邮件的服务器的smtp服务可用。 不然会抛出异常: Sending the email to the following server failed : m.xxx.com:2...

smtp交互过程

事先声明,整个过程以LOGIN认证方式为例,其他认证方式大同小异。按照时间顺序,主要分为22个步骤。 1、客户端TCP连接服务器25端口; 2、三次握手以后,连接建立成功,服务器主动推送服...
  • woshinia
  • woshinia
  • 2013年05月30日 15:07
  • 15573

SMTP验证方式种类(LOGIN、PLAIN、CRAM-MD5)

最初的SMTP协议不包含安全认证的,所谓的ESMTP在安全性方面扩展了SMTP,通过增加命令EHLO和AUTH。如今的SMTP服务器,无论是公网的还是内网的,大都要求安全认证,如果你使用的是Outlo...
  • mhfh611
  • mhfh611
  • 2013年07月25日 16:25
  • 17306

python-ntlm认证

转自:http://www.cnblogs.com/yd1227/archive/2009/09/29/1576380.html python-ntlm Python library that...
  • ace_fei
  • ace_fei
  • 2014年08月18日 17:10
  • 2805

The NTLM Authentication Protocol

Copyright © 2003 Eric Glass Permission to use, copy, modify, and distribute this document for any pu...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:The NTLM Authentication Protocol
举报原因:
原因补充:

(最多只允许输入30个字)