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 Data Types
- The NTLM Message Header Layout
- The Type 1 Message
- The Type 2 Message
- The Type 3 Message
- NTLM Version 2
- NTLMSSP and SSPI
- NTLM HTTP Authentication
- NTLM POP3 Authentication
- NTLM IMAP Authentication
- NTLM SMTP Authentication
- Links and References
- Appendix A: Java Implementation of the Type 3 Response Calculations
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:
- 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.
- 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.
- 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.
Before we start digging in any further, we will need to define a few data types as used in the messages.
For our purposes, a "short" is a little-endian, 16-bit unsigned value. For example, the decimal value "1234" represented as a short would be physically laid out as "0xd204" in hexadecimal.
A "long" is a little-endian, 32-bit unsigned value. The decimal value "1234" represented as a long in hexidecimal would be "0xd2040000".
A Unicode string is a string in which each character is represented as a 16-bit little-endian value (16-bit UCS-2 Transformation Format, little-endian byte order, with no Byte Order Mark and no null-terminator). The string "hello" in Unicode would be represented hexidecimally as "0x680065006c006c006f00".
An OEM string is a string in which each character is represented as an 8-bit value from the local machine's native character set (DOS codepage). There is no null-terminator. In NTLM messages, OEM strings are typically presented in uppercase. The string "HELLO" in OEM would be represented hexidecimally as "0x48454c4c4f".
A "security buffer" is a structure used to point to a buffer of binary data. It consists of:
So the security buffer "0xd204d204e1100000" would be read as:
Length: 0xd204 (1234 bytes) If you started at the first byte in the message, and skipped ahead 4321 bytes, you would be at the start of the data buffer. You would read 1234 bytes (which is the length of the buffer). Since the allocated space for the buffer is also 1234 bytes, you would then be at the end of the buffer.
Now we're ready to look at the physical layout of NTLM message headers.
All messages start with the NTLMSSP signature, which is (aptly enough) the null-terminated ASCII string "NTLMSSP" (hexadecimal "0x4e544c4d53535000").
Next is a long containing the message type (1, 2, or 3). A Type 1 message, for example, has type "0x01000000" in hex.
This is followed by message-specific information, typically consisting of security buffers and the message flags.
The message flags are contained in a bitfield within the header. This is a long, in which each bit represents a specific flag. Most of these will make more sense later, but we'll go ahead and present them here to establish a frame of reference for the rest of the discussion. Many of these flags are not used, or are used infrequently; commonly occurring flags are displayed in bold in the table below. Flags marked as "unidentified" or "unknown" are outside the realm of the author's knowledge (which is not by any means absolute).
NTLM Data Types
Allocated Space: 0xd204 (1234 bytes)
Offset: 0xe1100000 (4321 bytes)
The NTLM Message Header Layout
The NTLM Flags
Flag | Name | Description |
---|---|---|
0x00000001 | Negotiate Unicode | Indicates that Unicode strings are supported for use in security buffer data. |
0x00000002 | Negotiate OEM | Indicates that OEM strings are supported for use in security buffer data. |
0x00000004 | Request Target | Requests that the server's authentication realm be included in the Type 2 message. |
0x00000008 | unknown | This flag's usage has not been identified. |
0x00000010 | Negotiate Sign | Specifies that authenticated communication between the client and server should carry a digital signature (message integrity). |
0x00000020 | Negotiate Seal | Specifies that authenticated communication between the client and server should be encrypted (message confidentiality). |
0x00000040 | Negotiate Datagram Style | Indicates that datagram authentication is being used. |
0x00000080 | Negotiate Lan Manager Key | Indicates that the LAN Manager session key should be used for signing and sealing authenticated communications. |
0x00000100 | Negotiate Netware | This flag's usage has not been identified. |
0x00000200 | Negotiate NTLM | Indicates that NTLM authentication is being used. |
0x00000400 | unknown | This flag's usage has not been identified. |
0x00000800 | unknown | This flag's usage has not been identified. |
0x00001000 | Negotiate Domain Supplied | Sent by the client in the Type 1 message to indicate that the name of the domain in which the client workstation has membership is included in the message. This is used by the server to determine whether the client is eligible for local authentication. |
0x00002000 | Negotiate Workstation Supplied | Sent by the client in the Type 1 message to indicate that the client workstation's name is included in the message. This is used by the server to determine whether the client is eligible for local authentication. |
0x00004000 | Negotiate Local Call | Sent by the server to indicate that the server and client are on the same machine. Implies that the client may use the established local credentials for authentication instead of calculating a response to the challenge. |
0x00008000 | Negotiate Always Sign | Indicates that authenticated communication between the client and server should be signed with a "dummy" signature. |
0x00010000 | Target Type Domain | Sent by the server in the Type 2 message to indicate that the target authentication realm is a domain. |
0x00020000 | Target Type Server | Sent by the server in the Type 2 message to indicate that the target authentication realm is a server. |
0x00040000 | Target Type Share | Sent by the server in the Type 2 message to indicate that the target authentication realm is a share. Presumably, this is for share-level authentication. Usage is unclear. |
0x00080000 | Negotiate NTLM2 Key | Indicates that the NTLM2 signing and sealing scheme should be used for protecting authenticated communications. Note that this refers to a particular session security scheme, and is not related to the use of NTLMv2 authentication. This flag can, however, have an effect on the response calculations (as detailed in the "NTLM2 Session Response" section). |
0x00100000 | Request Init Response | This flag's usage has not been identified. |
0x00200000 | Request Accept Response | This flag's usage has not been identified. |
0x00400000 | Request Non-NT Session Key | This flag's usage has not been identified. |
0x00800000 | Negotiate Target Info | Sent by the server in the Type 2 message to indicate that it is including a Target Information block in the message. The Target Information block is used in the calculation of the NTLMv2 response. |
0x01000000 | unknown | This flag's usage has not been identified. |
0x02000000 | unknown | This flag's usage has not been identified. |
0x04000000 | unknown | This flag's usage has not been identified. |
0x08000000 | unknown | This flag's usage has not been identified. |
0x10000000 | unknown | This flag's usage has not been identified. |
0x20000000 | Negotiate 128 | Indicates that 128-bit encryption is supported. |
0x40000000 | Negotiate Key Exchange | Indicates that the client will provide an encrypted master session key in the "Session Key" field of the Type 3 message. This is used in signing and sealing, and is RC4-encrypted using the previous session key as the encryption key. |
0x80000000 | Negotiate 56 | Indicates that 56-bit encryption is supported. |
As an example, consider a message specifying:
Negotiate Unicode (0x00000001)
Request Target (0x00000004)
Negotiate NTLM (0x00000200)
Negotiate Always Sign (0x00008000)
Combining the above gives "0x00008205". This would be physically laid out as "0x05820000" (since it is represented in little-endian byte order).
Let's jump in and take a look at the Type 1 message:
The Type 1 Message
Description | Content | |
---|---|---|
0 | NTLMSSP Signature | Null-terminated ASCII "NTLMSSP" (0x4e544c4d53535000) |
8 | NTLM Message Type | long (0x01000000) |
12 | Flags | long |
(16) | Supplied Domain (Optional) | security buffer |
(24) | Supplied Workstation (Optional) | security buffer |
(32) | start of data block (if required) |
The Type 1 message is sent from the client to the server to initiate NTLM authentication. Its primary purpose is to establish the "ground rules" for authentication by indicating supported options via the flags. Optionally, it can also provide the server with the client's workstation name and the domain in which the client workstation has membership; this information is used by the server to determine whether the client is eligible for local authentication.
Typically, the Type 1 message contains flags from the following set:
Negotiate Unicode (0x00000001) | The client sets this flag to indicate that it supports Unicode strings. |
Negotiate OEM (0x00000002) | This is set to indicate that the client supports OEM strings. |
Request Target (0x00000004) | This requests that the server send the authentication target with the Type 2 reply. |
Negotiate NTLM (0x00000200) | Indicates that NTLM authentication is supported. |
Negotiate Domain Supplied (0x00001000) | When set, the client will send with the message the name of the domain in which the workstation has membership. |
Negotiate Workstation Supplied (0x00002000) | Indicates that the client is sending its workstation name with the message. |
Negotiate Always Sign (0x00008000) | Indicates that communication between the client and server after authentication should carry a "dummy" signature. |
Negotiate NTLM2 Key (0x00080000) | Indicates that this client supports the NTLM2 signing and sealing scheme; if negotiated, this can also affect the response calculations. |
Negotiate 128 (0x20000000) | Indicates that this client supports strong (128-bit) encryption. |
Negotiate 56 (0x80000000) | Indicates that this client supports medium (56-bit) encryption. |
The supplied domain is a security buffer containing the domain in which the client workstation has membership. This is always in OEM format, even if Unicode is supported by the client.
The supplied workstation is a security buffer containing the client workstation's name. This, too, is in OEM rather than Unicode.
Note that the supplied domain and workstation are optional fields; they may be empty (security buffer indicating a length of zero), or may not be sent at all (security buffer omitted altogether). If the supplied domain and workstation are omitted, the Type 1 message carries no data block (the message ends after the flags field, and is a fixed-length 16-byte structure). The "most-minimal" well-formed Type 1 message, therefore, would be:
4e544c4d535350000100000002020000
This message contains only the NTLMSSP signature, the NTLM message type, and the minimal set of flags (Negotiate NTLM and Negotiate OEM).
Consider the following hexadecimal Type 1 Message:
We break this up as follows:
Type 1 Message Example
4e544c4d535350000100000007320000060006002b0000000b000b0020000000
574f524b53544154494f4e444f4d41494e
0 | 0x4e544c4d53535000 | NTLMSSP Signature |
8 | 0x01000000 | Type 1 Indicator |
12 | 0x07320000 | Flags:
Negotiate Unicode (0x00000001) |
16 | 0x060006002b000000 | Supplied Domain Security Buffer:
Length: 6 bytes (0x0600) |
24 | 0x0b000b0020000000 | Supplied Workstation Security Buffer:
Length: 11 bytes (0x0b00) |
32 | 0x574f524b53544154494f4e | Supplied Workstation Data ("WORKSTATION") |
43 | 0x444f4d41494e | Supplied Domain Data ("DOMAIN") |
Analyzing this information, we can see:
- This is an NTLM Type 1 message (from the NTLMSSP Signature and Type 1 Indicator).
- This client can support either Unicode or OEM strings (the Negotiate Unicode and Negotiate OEM flags are both set).
- This client supports NTLM authentication (Negotiate NTLM).
- The client is requesting that the server send information regarding the authentication target (Request Target is set).
- This client is sending its domain, which is "DOMAIN" (the Negotiate Domain Supplied flag is set, and the domain name is present in the Supplied Domain Security Buffer).
- The client is sending its workstation name, which is "WORKSTATION" (the Negotiate Workstation Supplied flag is set, and the workstation name is present in the Supplied Workstation Security Buffer).
Note that the supplied workstation and domain are in OEM format. Additionally, the order in which the security buffer data blocks are laid out is unimportant; in the example, the workstation data is placed before the domain data.
After creating the Type 1 message, the client sends it to the server. The server analyzes the message, much as we have just done, and creates a reply. This brings us to our next topic, the Type 2 message.
The Type 2 Message
Description | Content | |
---|---|---|
0 | NTLMSSP Signature | Null-terminated ASCII "NTLMSSP" (0x4e544c4d53535000) |
8 | NTLM Message Type | long (0x02000000) |
12 | Target Name | security buffer |
20 | Flags | long |
24 | Challenge | 8 bytes |
(32) | Context (optional) | 8 bytes (two consecutive longs) |
(40) | Target Information (optional) | security buffer |
32 (48) | start of data block |
The Type 2 message is sent by the server to the client in response to the client's Type 1 message. It serves to complete the negotiation of options with the client, and also provides a challenge to the client. It may optionally contain information about the authentication target.
Typical Type 2 message flags include:
Negotiate Unicode (0x00000001) | The server sets this flag to indicate that it will be using Unicode strings. This should only be set if the client indicates (in the Type 1 message) that it supports Unicode. Either this flag or Negotiate OEM should be set, but not both. |
Negotiate OEM (0x00000002) | This flag is set to indicate that the server will be using OEM strings. This should only be set if the client indicates (in the Type 1 message) that it will support OEM strings. Either this flag or Negotiate Unicode should be set, but not both. |
Request Target (0x00000004) | This flag is often set in the Type 2 message; while it has a well-defined meaning within the Type 1 message, its semantics here are unclear. |
Negotiate NTLM (0x00000200) | Indicates that NTLM authentication is supported. |
Negotiate Local Call (0x00004000) | The server sets this flag to inform the client that the server and client are on the same machine. The server provides a local security context handle with the message. |
Negotiate Always Sign (0x00008000) | Indicates that communication between the client and server after authentication should carry a "dummy" signature. |
Target Type Domain (0x00010000) | The server sets this flag to indicate that the authentication target is being sent with the message and represents a domain. |
Target Type Server (0x00020000) | The server sets this flag to indicate that the authentication target is being sent with the message and represents a server. |
Target Type Share (0x00040000) | The server apparently sets this flag to indicate that the authentication target is being sent with the message and represents a network share. This has not been confirmed. |
Negotiate NTLM2 Key (0x00080000) | Indicates that this server supports the NTLM2 signing and sealing scheme; if negotiated, this can also affect the client's response calculations. |
Negotiate Target Info (0x00800000) | The server sets this flag to indicate that a Target Information block is being sent with the message. |
Negotiate 128 (0x20000000) | Indicates that this server supports strong (128-bit) encryption. |
Negotiate 56 (0x80000000) | Indicates that this server supports medium (56-bit) encryption. |
The target name is a security buffer containing the name of the authentication target. This is typically sent in response to a client requesting the target (via the Request Target flag in the Type 1 message). This can contain a domain, server, or (apparently) a network share. The target type is indicated via the Target Type Domain, Target Type Server, and Target Type Share flags. The target name can be either Unicode or OEM, as indicated by the presence of the appropriate flag in the Type 2 message.
The challenge is an 8-byte block of random data. The client will use this to formulate a response.
The context field is typically populated when Negotiate Local Call is set. It contains an SSPI context handle, which allows the client to "short-circuit" authentication and effectively circumvent responding to the challenge. Physically, the context is two long values. This is covered in greater detail later, in the "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:
| ||||||||
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.
Let's look at the following hexadecimal Type 2 Message:
Breaking this into its constituent fields gives:
Type 2 Message Example
4e544c4d53535000020000000c000c003000000001028100
0123456789abcdef0000000000000000620062003c000000
44004f004d00410049004e0002000c0044004f004d004100
49004e0001000c0053004500520056004500520004001400
64006f006d00610069006e002e0063006f006d0003002200
7300650072007600650072002e0064006f006d0061006900
6e002e0063006f006d0000000000
0 | 0x4e544c4d53535000 | NTLMSSP Signature | ||||||||||
8 | 0x02000000 | Type 2 Indicator | ||||||||||
12 | 0x0c000c0030000000 | Target Name Security Buffer:
Length: 12 bytes (0x0c00) | ||||||||||
20 | 0x01028100 | Flags:
Negotiate Unicode (0x00000001) | ||||||||||
24 | 0x0123456789abcdef | Challenge | ||||||||||
32 | 0x0000000000000000 | Context | ||||||||||
40 | 0x620062003c000000 | Target Information Security Buffer:
Length: 98 bytes (0x6200) | ||||||||||
48 | 0x44004f004d004100 49004e00 |
Target Name Data ("DOMAIN") | ||||||||||
60 | 0x02000c0044004f00 4d00410049004e00 01000c0053004500 5200560045005200 0400140064006f00 6d00610069006e00 2e0063006f006d00 0300220073006500 7200760065007200 2e0064006f006d00 610069006e002e00 63006f006d000000 0000 |
Target Information Data:
|
An analysis of this message shows:
- This is an NTLM Type 2 message (from the NTLMSSP Signature and Type 2 Indicator).
- The server has indicated that strings will be encoded using Unicode (the Negotiate Unicode flag is set).
- The server supports NTLM authentication (Negotiate NTLM).
- The Target Name provided by the server is populated and represents a domain (the Target Type Domain flag is set and the domain name is present in the Target Name Security Buffer).
- The server is providing a Target Information structure (Negotiate Target Info is set). This structure is present in the Target Information Security Buffer (domain name "DOMAIN", server name "SERVER", DNS domain name "domain.com", and DNS server name "server.domain.com").
- The challenge generated by the server is "0x0123456789abcdef".
- An empty context has been sent.
Note that the target name is in Unicode format (as specified by the Negotiate Unicode flag).
After the server creates the Type 2 message, it is sent to the client. The response to the server's challenge is provided in the client's Type 3 message.
The Type 3 Message
Description | Content | |
---|---|---|
0 | NTLMSSP Signature | Null-terminated ASCII "NTLMSSP" (0x4e544c4d53535000) |
8 | NTLM Message Type | long (0x03000000) |
12 | LM/LMv2 Response | security buffer |
20 | NTLM/NTLMv2 Response | security buffer |
28 | Domain Name | security buffer |
36 | User Name | security buffer |
44 | Workstation Name | security buffer |
(52) | Session Key (optional) | security buffer |
(60) | Flags (optional) | long |
52 (64) | start of data block |
The Type 3 message is the final step in authentication. This message contains the client's responses to the Type 2 challenge, which demonstrate that the client has knowledge of the account password without sending the password directly. The Type 3 message also indicates the domain and username of the authenticating account, as well as the client workstation name.
Note that the flags in the Type 3 message are optional; older clients include neither the session key nor the flags in the message. In this case, the data block begins at offset 52, immediately following the workstation name security buffer. It has been determined experimentally that the Type 3 flags (when included) do not carry any additional semantics in connection-oriented authentication; they do not appear to have any discernable effect on either authentication or the establishment of session security. Clients sending flags typically mirror the established Type 2 settings fairly closely. It is possible that the flags are sent as a "reminder" of established options, to allow the server to avoid caching the negotiated settings. The Type 3 flags are relevant during 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.
The client creates one or more responses to the Type 2 challenge, and sends these in the Type 3 message. There are five types of responses:
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):
This process is best illustrated with a detailed example. Consider a user with the password "SecREt01", responding to the Type 2 challenge "0x0123456789abcdef".
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. 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"). 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:
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):
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").
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). 0x25a98c1c31e81847466b29b2df4680f39958fb8c213a9cc6 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):
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:
Responding to the Challenge
For more detailed information on these schemes, it is highly recommended that you read Christopher Hertel's Implementing CIFS, especially the section on authentication.
The LM Response
The NTLM Response
The NTLMv2 Response
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
Domain: | DOMAIN |
Username: | user |
Password: | SecREt01 |
Challenge: | 0x0123456789abcdef |
Target Information: | 0x02000c0044004f00 4d00410049004e00 01000c0053004500 5200560045005200 0400140064006f00 6d00610069006e00 2e0063006f006d00 0300220073006500 7200760065007200 2e0064006f006d00 610069006e002e00 63006f006d000000 0000 |
- 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:
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) - 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".
- 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):
- 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:
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):
- A random 8-byte client challenge is created.
- 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:
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) |
20 | 0x1800180082000000 | NTLM Response Security Buffer:
Length: 24 bytes (0x1800) |
28 | 0x0c000c0040000000 | Domain Name Security Buffer:
Length: 12 bytes (0x0c00) |
36 | 0x080008004c000000 | User Name Security Buffer:
Length: 8 bytes (0x0800) |
44 | 0x1600160054000000 | Workstation Name Security Buffer:
Length: 22 bytes (0x1600) |
52 | 0x000000009a000000 | Session Key Security Buffer:
Length: 0 bytes (0x0000) |
60 | 0x01020000 | Flags:
Negotiate Unicode (0x00000001) |
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.
NTLM version 2 consists of three new response algorithms (NTLMv2, LMv2, and the NTLM2 session response, discussed previously) and a new signing and sealing scheme (NTLM2 session security). NTLM2 session security is negotiated via the "Negotiate NTLM2 Key" flag; NTLMv2 authentication, however, is enabled through a modification to the registry. Further, the registry setting on the client and server must be compatible in order for authentication to be successful. The result is that the overwhelming majority of hosts just use the default setting, and NTLMv2 authentication is rarely seen in deployed systems.
Instructions for enabling NTLM version 2 are detailed in Microsoft Knowledge Base Article 239869; briefly, a modification is made to the registy value: (LMCompatibility on Win9x-based systems). This is a REG_DWORD entry, and can be set to one of the following values:
NTLM Version 2
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\LSA\LMCompatibilityLevel
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.
Now that we have a working knowledge of NTLM authentication, it is appropriate to look at how NTLM fits into the "big picture".
Windows provides an authentication framework known as SSPI - the Security Support Provider interface. This is the Microsoft equivalent of the GSS-API (Generic Security Service Application Program Interface, 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:
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:
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:
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:
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.
Microsoft has established the proprietary "NTLM" authentication scheme for HTTP to provide integrated authentication to IIS web servers. This authentication mechanism allows clients to access resources using their Windows credentials, and is typically used within corporate environments to provide single sign-on functionality to intranet sites. Historically, NTLM authentication was only supported by Internet Explorer; recently, however, support has been added to various other user agents.
The NTLM HTTP authentication mechanism works as follows:
Note that Internet Explorer will only select NTLM if it is the first mechanism offered; this is at odds with RFC 2616, which states that the client must select the strongest supported authentication scheme. This scheme differs from most "normal" HTTP authentication mechanisms, in that subsequent requests over the authenticated connection are not themselves authenticated; NTLM is connection-oriented, rather than request-oriented. So a second request for "/index.html" would not carry any authentication information, and the server would request none. If the server detects that the connection to the client has been dropped, a request for "/index.html" would result in the server reinitiating the NTLM handshake.
A notable exception to the above is the client's behavior when submitting a POST request (typically employed when the client is sending form data to the server). If the client determines that the server is not the local host, the client will initiate reauthentication for POST requests over the active connection. The client will first submit an empty POST request with a Type 1 message in the "Authorization" header; the server responds with the Type 2 message (in the "WWW-Authenticate" header as shown above). The client then resubmits the POST with the Type 3 message, sending the form data with the request.
The NTLM HTTP mechanism can also be used for HTTP proxy authentication. The process is similar, except:
With Windows 2000, Microsoft introduced the "Negotiate" HTTP authentication mechanism. While primarily aimed at providing a means of authenticating the user against Active Directory via Kerberos, it is backward-compatible with the NTLM scheme. When the Negotiate mechanism is used in "legacy" mode, the headers passed between the client and server are identical, except "Negotiate" (rather than "NTLM") is indicated as the mechanism name.
Microsoft's Exchange server provides an NTLM authentication mechanism for the POP3 protocol. This is a proprietary extension used with the POP3 AUTH command as documented in 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:
After successful authentication has occurred, the POP3 session enters the "transaction" state, allowing messages to be retrieved by the client.
Exchange provides an IMAP authentication mechanism similar in form to the POP3 mechanism previously discussed. IMAP authentication is documented in 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:
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:
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. After authenticating, the client is able to send messages normally.
Note that due to the highly dynamic and transient nature of the Web, these may or may not be available.
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/.
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. NTLMSSP and SSPI
Local Authentication
Datagram Authentication
NTLM HTTP Authentication
GET /index.html HTTP/1.1
HTTP/1.1 401 Unauthorized
WWW-Authenticate: NTLM
Connection: close
GET /index.html HTTP/1.1
Authorization: NTLM TlRMTVNTUAABAAAABzIAAAYABgArAAAACwALACAAAABXT1
JLU1RBVElPTkRPTUFJTg==
HTTP/1.1 401 Unauthorized
WWW-Authenticate: NTLM TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8
AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTA
EUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHI
ALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA=
GET /index.html HTTP/1.1
Authorization: NTLM TlRMTVNTUAADAAAAGAAYAGoAAAAYABgAggAAAAwADABAAA
AACAAIAEwAAAAWABYAVAAAAAAAAACaAAAAAQIAAEQATwBNAEEASQBOAHUAcwBlAHIA
VwBPAFIASwBTAFQAQQBUAEkATwBOAMM3zVy9RPyXgqZnr21CfG3mfCDC0+d8ViWpjB
wx6BhHRmspst9GgPOZWPuMITqcxg==
HTTP/1.1 200 OK
NTLM POP3 Authentication
AUTH
+OK The operation completed successfully.
NTLM
.
AUTH NTLM
+ OK
TlRMTVNTUAABAAAABzIAAAYABgArAAAACwALACAAAABXT1JLU1RBVElPTkRPTUFJTg==
+ TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAA
RABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZA
BvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAu
AGMAbwBtAAAAAAA=
TlRMTVNTUAADAAAAGAAYAGoAAAAYABgAggAAAAwADABAAAAACAAIAEwAAAAWABYAVA
AAAAAAAACaAAAAAQIAAEQATwBNAEEASQBOAHUAcwBlAHIAVwBPAFIASwBTAFQAQQBU
AEkATwBOAMM3zVy9RPyXgqZnr21CfG3mfCDC0+d8ViWpjBwx6BhHRmspst9GgPOZWP
uMITqcxg==
+OK User successfully logged on
NTLM IMAP Authentication
After authentication has completed, the IMAP session enters the authenticated state.
0000 CAPABILITY
* CAPABILITY IMAP4 IMAP4rev1 IDLE LITERAL+ AUTH=NTLM
0000 OK CAPABILITY completed.
0001 AUTHENTICATE NTLM
+
TlRMTVNTUAABAAAABzIAAAYABgArAAAACwALACAAAABXT1JLU1RBVElPTkRPTUFJTg==
+ TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAA
RABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZA
BvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAu
AGMAbwBtAAAAAAA=
TlRMTVNTUAADAAAAGAAYAGoAAAAYABgAggAAAAwADABAAAAACAAIAEwAAAAWABYAVA
AAAAAAAACaAAAAAQIAAEQATwBNAEEASQBOAHUAcwBlAHIAVwBPAFIASwBTAFQAQQBU
AEkATwBOAMM3zVy9RPyXgqZnr21CfG3mfCDC0+d8ViWpjBwx6BhHRmspst9GgPOZWP
uMITqcxg==
0001 OK AUTHENTICATE NTLM completed.
NTLM SMTP Authentication
EHLO client.example.com
250-server.example.com Hello [10.10.2.20]
250-HELP
250-AUTH LOGIN NTLM
250-AUTH=LOGIN NTLM
250 SIZE 10240000
AUTH NTLM TlRMTVNTUAABAAAABzIAAAYABgArAAAACwALACAAAABXT1JLU1RBVElPTkRPTUFJTg==
334 TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAA
RABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZA
BvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAu
AGMAbwBtAAAAAAA=
TlRMTVNTUAADAAAAGAAYAGoAAAAYABgAggAAAAwADABAAAAACAAIAEwAAAAWABYAVA
AAAAAAAACaAAAAAQIAAEQATwBNAEEASQBOAHUAcwBlAHIAVwBPAFIASwBTAFQAQQBU
AEkATwBOAMM3zVy9RPyXgqZnr21CfG3mfCDC0+d8ViWpjBwx6BhHRmspst9GgPOZWP
uMITqcxg==
235 NTLM authentication successful.
Links and References
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.
A highly informative online book by Christopher R. Hertel. Especially relevant to this discussion is the section on authentication.
Closest thing to an "official" reference on NTLM. Unfortunately, also rather old and not terribly accurate.
A whitepaper discussing application development using the SSPI.
Informative discussion on the NTLM HTTP authentication mechanism. Inaccurate in some regards, but still quite useful.
Project to provide NTLM HTTP authentication to the Squid proxy server.
An open-source Java HTTP client which provides support for the NTLM HTTP authentication scheme.
An open-source Java Cryptography Extension provider supplying an implementation of the MD4 message-digest algorithm.
Specification and reference implementation for the MD4 digest (used to calculate the NTLM password hash).
Specification and reference implementation for the MD5 digest (used to calculate the NTLM2 session response).
Specification and reference implementation for the HMAC-MD5 algorithm (used in the calculation of the NTLMv2/LMv2 responses).
Describes how to enable negotiation of NTLMv2 authentication and enforce NTLM security flags. Appendix A: Java Implementation of the Type 3 Response Calculations
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");
}
/**
* Applies odd parity to the given byte array.
*
* @param bytes The data whose parity bits are to be adjusted for
* odd parity.
*/
private static void oddParity(byte[] bytes) {
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^
(b >>> 4) ^ (b >>> 3) ^ (b >>> 2) ^
(b >>> 1)) & 0x01) == 0;
if (needsParity) {
bytes[i] |= (byte) 0x01;
} else {
bytes[i] &= (byte) 0xfe;
}
}
}
}
All trademarks mentioned in this document are the property of their respective owners.