NTLM / SPNEGO / NEGOEX
NTLM is an authentication method in which the client calculates a response
to a server challenge. In Impacket, SMB, LDAP, DCE/RPC, TDS, HTTP, and other
protocols share the same NTLM message implementation. For the authentication flow
and how it differs from an NT hash, see NTLM Authentication.
Message bodies are implemented in ntlm.py, encapsulation in
spnego.py, and NEGOEX in
negoex.py.
Related: DumpNTLMInfo, ntlmrelayx, rdp_check
NTLMv1 and NTLMv2 Are Not Negotiated
Section titled “NTLMv1 and NTLMv2 Are Not Negotiated”USE_NTLMv2 = True # ntlm.pyAs the comment indicates, client/server flag exchange does not select NTLMv2.
It is used when both local policies use v2.
NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY (0x00080000) refers to NTLMv1
Extended Session Security (ESS / NTLM2), not NTLMv2.
computeResponse(..., use_ntlmv2=USE_NTLMv2) is the branch point.
Three Messages
Section titled “Three Messages”The signature is "NTLMSSP\x00".
| Class | Type | Contents |
|---|---|---|
NTLMAuthNegotiate | 1 | Flags, OEM domain/host, optional VERSION |
NTLMAuthChallenge | 2 | 8-byte challenge, TargetInfoFields (AV_PAIRS), optional Version |
NTLMAuthChallengeResponse | 3 | LM/NT responses, UTF-16 domain/user/host, encrypted session key, Version, MIC area |
Entry functions:
getNTLMSSPType1(workstation, domain, signingRequired, use_ntlmv2, version)getNTLMSSPType3(type1, type2, user, password, domain, lmhash, nthash, use_ntlmv2, channel_binding_value, service='cifs')
Flags always set by Type1: NTLM | EXTENDED_SESSIONSECURITY | UNICODE | REQUEST_TARGET | 128 | 56.
When signing is required: KEY_EXCH | SIGN | ALWAYS_SIGN | SEAL. With v2: TARGET_INFO.
The workstation name is retained for Type3, but is not included in Type1 on the wire.
AV pair
Section titled “AV pair”AV_PAIRS is a sequence of <H type><H len><bytes> terminated by NTLMSSP_AV_EOL=0.
| ID | Name | Meaning |
|---|---|---|
| 0x01 | HOSTNAME | NetBIOS host |
| 0x02 | DOMAINNAME | NetBIOS domain |
| 0x03 | DNS_HOSTNAME | DNS host |
| 0x04 | DNS_DOMAINNAME | DNS domain |
| 0x05 | DNS_TREENAME | Forest |
| 0x06 | FLAGS | Bits for MIC, restrictions, etc. |
| 0x07 | TIME | FILETIME |
| 0x08 | RESTRICTIONS | Restrictions |
| 0x09 | TARGET_NAME | SPN written back by the client |
| 0x0a | CHANNEL_BINDINGS | CBT (MD5 of channel binding struct) |
A v2 client may rewrite TargetInfo when constructing Type3
(computeResponseNTLMv2):
AV_TARGET_NAME = service/ + DNS_HOSTNAME(defaultservice='cifs')AV_TIMEif absentAV_CHANNEL_BINDINGSifchannel_binding_valueis present
DumpNTLMInfo examines the Type2 AV pairs. MIC, channel binding, and target name, which affect relay success, also appear here.
Response Calculation
Section titled “Response Calculation”NTLMv1
Section titled “NTLMv1”LMOWFv1: DES(KGS!@#$%) on the uppercased OEM password; for non-Latin-1 input, useDEFAULT_LM_HASH = AAD3B435B51404EEAAD3B435B51404EENTOWFv1:MD4(UTF-16LE(password))= NT hash- Response: encrypt the challenge in three DES blocks
- With ESS: NT = DES(nthash, MD5(serverChal||clientChal)[:8]); LM = clientChal || 0×16
sessionBaseKey=MD4(NT hash)
NTLMv2 ([MS-NLMP] 3.3.2)
Section titled “NTLMv2 ([MS-NLMP] 3.3.2)”responseKeyNT = HMAC_MD5(nthash, UPPER(user) || domain)temp = 0x0101 || 0 || Time || clientChal || 0 || AvPairsntProofStr = HMAC_MD5(responseKeyNT, serverChal || temp)NT response = ntProofStr || tempLM response = HMAC_MD5(responseKeyNT, serverChal || clientChal) || clientChalsessionBaseKey= HMAC_MD5(responseKeyNT, ntProofStr)Anonymous authentication uses empty NT/LM responses and a session key of 16 zero bytes.
Session Key and MIC
Section titled “Session Key and MIC”keyExchangeKey = KXKEY(...) # Same as sessionBaseKey for v2if KEY_EXCH: exportedSessionKey = 16 random bytes Type3.session_key = RC4(keyExchangeKey, exportedSessionKey)else: exportedSessionKey = keyExchangeKeySMB / RPC / LDAP signing and encryption derive from this exportedSessionKey.
MIC:
MIC = HMAC_MD5(exportedSessionKey, Type1 || Type2 || Type3_with_MIC_zeroed)getNTLMSSPType3() does not calculate the MIC. Callers that need it, such as
LDAP SASL bind, TDS, and ntlmrelayx, fill it in afterward.
NTLMAuthChallengeResponse.checkMIC only checks the VERSION flag;
it does not actually detect whether a MIC is present.
GSS SIGN/SEAL (SIGNKEY / SEALKEY / MAC / SEAL) is used by DCERPC
and LDAP signing. It is calculated separately from SMB message signing.
SPNEGO
Section titled “SPNEGO”spnego.py implements BER by hand rather than using pyasn1.
| Symbol | Role |
|---|---|
GSSAPI | 0x60 + OID 1.3.6.1.5.5.2 + payload |
SPNEGO_NegTokenInit | mechTypes plus an optional mechToken |
SPNEGO_NegTokenResp | NegState / SupportedMech / ResponseToken / optional mechListMIC |
SPNEGOCipher | Uses NTLM SIGN/SEAL over SPNEGO (DCERPC / LDAP) |
OID:
| Name | OID |
|---|---|
| NTLMSSP | 1.3.6.1.4.1.311.2.2.10 |
| MS KRB5 | 1.2.840.48018.1.2.2 |
| KRB5 | 1.2.840.113554.1.2.2 |
| KRB5 U2U | 1.2.840.113554.1.2.2.3 |
| NEGOEX | 1.3.6.1.4.1.311.2.2.30 |
An SMB client includes only one mech (NTLM or MS KRB5). The Kerberos token is not a raw AP-REQ; it is
AID + OID(KRB5) + KRB5_AP_REQ + DER(AP_REQ)The NegState values are 0 completed, 1 incomplete, 2 reject, and 3 request-mic.
SPNEGO_NegTokenResp can emit mechListMIC, but fromString
does not parse it.
NEGOEX
Section titled “NEGOEX”Equivalent to [MS-NEGOEX]. Signature: NEGOEXTS. Message types: INITIATOR/ACCEPTOR_NEGO,
META_DATA, CHALLENGE, AP_REQUEST, VERIFY, and ALERT.
NegoExContext is the state machine. The VERIFY checksum uses RFC 3961,
all preceding messages, and key usage 23 (initiator) / 25 (acceptor).
It is not connected to the SMB / LDAP / RPC login paths. It remains an OID definition and standalone module. The PKU2U GUID is also unused.
Defenses That Affect Relay
Section titled “Defenses That Affect Relay”ntlmrelayx operates at this layer.
| Element | Effect |
|---|---|
| MIC | Detects Type3 modification; --remove-mic targets CVE-2019-1040 |
| SIGN/SEAL flag | --remove-sign-seal clears signing and sealing flags |
| Channel binding / EPA | Impedes relay to LDAPS / HTTPS / TDS |
| SMB signing | Impedes SMB relay |
| Target name (SPN) AV | Server-side target validation |
| Extended Protection | Fails without channel_binding_value when CBT is required |
When adding an inbound server or outbound client, specify four points: ownership of Type1/2/3, MIC recalculation, signing requirements, and CBT.
Checks When Making Changes
Section titled “Checks When Making Changes”- Do not confuse
USE_NTLMv2with the ESS flag - Do not assume that the library always adds a MIC to Type3
- Preserve AV pair order and unknown types
- Related tests:
tests/SMB_RPC/test_ntlm.py

