Skip to main content

Overview

The ntlm module provides a complete implementation of the NT LAN Manager (NTLM) authentication protocol, including NTLMv1, NTLMv2, and NTLM2 Session Security. It handles challenge-response authentication, hash generation, and session key derivation.

Key Functions

getNTLMSSPType1()

Create an NTLM Type 1 (Negotiate) message.
from impacket import ntlm

type1 = ntlm.getNTLMSSPType1(workstation='CLIENT', domain='CORP',
                              signingRequired=False, use_ntlmv2=True)
workstation
str
default:"''"
Workstation name to send in negotiate message
domain
str
default:"''"
Domain name to send
signingRequired
bool
default:"False"
Whether message signing is required
use_ntlmv2
bool
default:"True"
Use NTLMv2 protocol (recommended)
version
VERSION
OS version structure to include
return
NTLMAuthNegotiate
Type 1 message object

getNTLMSSPType3()

Create an NTLM Type 3 (Authenticate) message.
type3, session_key = ntlm.getNTLMSSPType3(
    type1, type2, user, password, domain,
    lmhash='', nthash='', use_ntlmv2=True
)
type1
NTLMAuthNegotiate
required
Type 1 message from getNTLMSSPType1()
type2
bytes
required
Type 2 (Challenge) message from server
user
str
required
Username for authentication
password
str
required
Password (not used if hashes provided)
domain
str
required
Domain name
lmhash
str
default:"''"
LM hash (hex string)
nthash
str
default:"''"
NT hash (hex string)
use_ntlmv2
bool
default:"True"
Use NTLMv2 (recommended)
channel_binding_value
bytes
default:"b''"
Channel binding data for EPA (Extended Protection for Authentication)
service
str
default:"'cifs'"
Service principal name (e.g., ‘cifs’, ‘http’, ‘ldap’)
type3
NTLMAuthChallengeResponse
Type 3 authenticate message
session_key
bytes
Session key for signing/encryption

Hash Computation

compute_lmhash()

Compute LM hash from password.
from impacket.ntlm import compute_lmhash

lmhash = compute_lmhash('Password123')
password
str
required
Password to hash (only Latin-1 characters supported)
return
bytes
16-byte LM hash
If password contains non-Latin-1 characters, returns the default empty LM hash.

compute_nthash()

Compute NT hash from password.
from impacket.ntlm import compute_nthash

nthash = compute_nthash('Password123')
password
str
required
Password to hash (Unicode supported)
return
bytes
16-byte NT hash (MD4 of Unicode password)

Response Computation

computeResponse()

Compute NTLM challenge response.
ntResponse, lmResponse, sessionKey = ntlm.computeResponse(
    flags, serverChallenge, clientChallenge, serverName,
    domain, user, password, lmhash='', nthash='',
    use_ntlmv2=True
)
flags
int
required
NTLM negotiation flags
serverChallenge
bytes
required
8-byte challenge from server
clientChallenge
bytes
required
8-byte client challenge
serverName
bytes
required
Target information from Type 2 message
domain
str
required
Domain name
user
str
required
Username
password
str
required
Password
lmhash
str
default:"''"
Pre-computed LM hash
nthash
str
default:"''"
Pre-computed NT hash
use_ntlmv2
bool
default:"True"
Use NTLMv2 protocol
ntResponse
bytes
NT response
lmResponse
bytes
LM response
sessionKey
bytes
Base session key

Signing and Sealing

SIGN()

Sign a message.
signature = ntlm.SIGN(flags, signingKey, message, seqNum, handle)
flags
int
required
NTLM flags
signingKey
bytes
required
Signing key from SIGNKEY()
message
bytes
required
Message to sign
seqNum
int
required
Sequence number
handle
callable
required
RC4 cipher function
return
NTLMMessageSignature
Message signature

SEAL()

Encrypt and sign a message.
sealedMsg, signature = ntlm.SEAL(
    flags, signingKey, sealingKey,
    messageToSign, messageToEncrypt,
    seqNum, handle
)
flags
int
required
NTLM flags
signingKey
bytes
required
Signing key
sealingKey
bytes
required
Sealing (encryption) key
messageToSign
bytes
required
Message data for signature
messageToEncrypt
bytes
required
Message data to encrypt
seqNum
int
required
Sequence number
handle
callable
required
RC4 cipher function
sealedMsg
bytes
Encrypted message
signature
NTLMMessageSignature
Message signature

SIGNKEY()

Derive signing key from session key.
signKey = ntlm.SIGNKEY(flags, randomSessionKey, mode='Client')
flags
int
required
NTLM flags
randomSessionKey
bytes
required
Random session key
mode
str
default:"'Client'"
Direction: 'Client' or 'Server'
return
bytes
Signing key

SEALKEY()

Derive sealing (encryption) key from session key.
sealKey = ntlm.SEALKEY(flags, randomSessionKey, mode='Client')
flags
int
required
NTLM flags
randomSessionKey
bytes
required
Random session key
mode
str
default:"'Client'"
Direction: 'Client' or 'Server'
return
bytes
Sealing key (encryption key)

Classes

NTLMAuthNegotiate

NTLM Type 1 (Negotiate) message.
auth = ntlm.NTLMAuthNegotiate()
auth['flags'] = ntlm.NTLMSSP_NEGOTIATE_UNICODE | \
                ntlm.NTLMSSP_NEGOTIATE_NTLM
data = auth.getData()
flags
int
Negotiation flags
domain_name
bytes
Domain name
host_name
bytes
Workstation name
os_version
VERSION
OS version structure

NTLMAuthChallenge

NTLM Type 2 (Challenge) message.
challenge = ntlm.NTLMAuthChallenge(data)
server_challenge = challenge['challenge']
target_info = challenge['TargetInfoFields']
challenge
bytes
8-byte server challenge
TargetInfoFields
bytes
AV_PAIRS structure with server information
flags
int
Server’s negotiation flags
domain_name
bytes
Target domain

NTLMAuthChallengeResponse

NTLM Type 3 (Authenticate) message.
response = ntlm.NTLMAuthChallengeResponse(
    username='admin',
    password='password',
    challenge=server_challenge
)

data = response.getData()
ntlm
bytes
NT response
lanman
bytes
LM response
user_name
bytes
Username (UTF-16LE)
domain_name
bytes
Domain name (UTF-16LE)
host_name
bytes
Workstation name (UTF-16LE)
session_key
bytes
Encrypted random session key
flags
int
Negotiation flags

AV_PAIRS

Attribute-Value pairs for target information.
av_pairs = ntlm.AV_PAIRS(targetInfo)

# Access fields
hostname = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME]
dns_name = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME]

# Set fields
av_pairs[ntlm.NTLMSSP_AV_TARGET_NAME] = b'cifs/server'

# Get serialized data
data = av_pairs.getData()
NTLMSSP_AV_HOSTNAME
bytes
Server hostname
NTLMSSP_AV_DOMAINNAME
bytes
Domain name
NTLMSSP_AV_DNS_HOSTNAME
bytes
DNS hostname
NTLMSSP_AV_DNS_DOMAINNAME
bytes
DNS domain name
NTLMSSP_AV_TIME
bytes
Timestamp
NTLMSSP_AV_TARGET_NAME
bytes
Target SPN
NTLMSSP_AV_CHANNEL_BINDINGS
bytes
Channel binding data

Constants

NTLM Flags

# Negotiation flags
NTLMSSP_NEGOTIATE_UNICODE = 0x00000001
NTLMSSP_NEGOTIATE_OEM = 0x00000002
NTLMSSP_REQUEST_TARGET = 0x00000004
NTLMSSP_NEGOTIATE_SIGN = 0x00000010
NTLMSSP_NEGOTIATE_SEAL = 0x00000020
NTLMSSP_NEGOTIATE_DATAGRAM = 0x00000040
NTLMSSP_NEGOTIATE_LM_KEY = 0x00000080
NTLMSSP_NEGOTIATE_NTLM = 0x00000200
NTLMSSP_NEGOTIATE_ANONYMOUS = 0x00000800
NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED = 0x00001000
NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000
NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x00008000
NTLMSSP_TARGET_TYPE_DOMAIN = 0x00010000
NTLMSSP_TARGET_TYPE_SERVER = 0x00020000
NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000
NTLMSSP_NEGOTIATE_IDENTIFY = 0x00100000
NTLMSSP_NEGOTIATE_TARGET_INFO = 0x00800000
NTLMSSP_NEGOTIATE_VERSION = 0x02000000
NTLMSSP_NEGOTIATE_128 = 0x20000000
NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000
NTLMSSP_NEGOTIATE_56 = 0x80000000

AV Pair Types

NTLMSSP_AV_EOL = 0x00              # End of list
NTLMSSP_AV_HOSTNAME = 0x01         # Server hostname
NTLMSSP_AV_DOMAINNAME = 0x02       # Domain name
NTLMSSP_AV_DNS_HOSTNAME = 0x03     # DNS hostname
NTLMSSP_AV_DNS_DOMAINNAME = 0x04   # DNS domain
NTLMSSP_AV_DNS_TREENAME = 0x05     # DNS tree name
NTLMSSP_AV_FLAGS = 0x06            # Flags
NTLMSSP_AV_TIME = 0x07             # Timestamp
NTLMSSP_AV_RESTRICTIONS = 0x08     # Restrictions
NTLMSSP_AV_TARGET_NAME = 0x09      # Target SPN
NTLMSSP_AV_CHANNEL_BINDINGS = 0x0a # Channel bindings

Global Settings

# Set globally to control NTLMv2 usage
ntlm.USE_NTLMv2 = True   # Recommended

# Default LM hash (empty password)
ntlm.DEFAULT_LM_HASH = b'\xaa\xd3\xb4...'

Usage Examples

Basic NTLM Authentication Flow

from impacket import ntlm

# Step 1: Create Type 1 (Negotiate) message
type1 = ntlm.getNTLMSSPType1(
    workstation='WORKSTATION',
    domain='CORP',
    use_ntlmv2=True
)

# Send type1.getData() to server...
# Receive Type 2 (Challenge) from server

# Step 2: Parse Type 2 message
type2_data = b'...'  # Received from server
type2 = ntlm.NTLMAuthChallenge(type2_data)

# Step 3: Create Type 3 (Authenticate) message
type3, session_key = ntlm.getNTLMSSPType3(
    type1=type1,
    type2=type2_data,
    user='admin',
    password='password',
    domain='CORP',
    use_ntlmv2=True
)

# Send type3.getData() to server
print(f"Session key: {session_key.hex()}")

Pass-the-Hash Authentication

from impacket import ntlm

# Use pre-computed hashes instead of password
lmhash = 'aad3b435b51404eeaad3b435b51404ee'
nthash = '8846f7eaee8fb117ad06bdd830b7586c'

type1 = ntlm.getNTLMSSPType1()

# ... receive type2 ...

type3, session_key = ntlm.getNTLMSSPType3(
    type1=type1,
    type2=type2_data,
    user='admin',
    password='',  # Not used
    domain='CORP',
    lmhash=lmhash,
    nthash=nthash,
    use_ntlmv2=True
)

Computing Password Hashes

from impacket import ntlm
import binascii

password = 'Password123!'

# Compute hashes
lmhash = ntlm.compute_lmhash(password)
nthash = ntlm.compute_nthash(password)

print(f"LM Hash: {binascii.hexlify(lmhash).decode()}")
print(f"NT Hash: {binascii.hexlify(nthash).decode()}")

# Use hashes for authentication
type3, key = ntlm.getNTLMSSPType3(
    type1, type2, 'user', '',
    lmhash=binascii.hexlify(lmhash).decode(),
    nthash=binascii.hexlify(nthash).decode()
)

Message Signing

from impacket import ntlm
from Cryptodome.Cipher import ARC4

# After authentication, derive signing key
flags = ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY | \
        ntlm.NTLMSSP_NEGOTIATE_128

signingKey = ntlm.SIGNKEY(flags, session_key, mode='Client')

# Create RC4 cipher handle
cipher = ARC4.new(signingKey)

# Sign a message
message = b"Data to sign"
seqNum = 0

signature = ntlm.SIGN(
    flags=flags,
    signingKey=signingKey,
    message=message,
    seqNum=seqNum,
    handle=cipher.encrypt
)

print(f"Signature: {signature.getData().hex()}")

Message Encryption (Sealing)

from impacket import ntlm
from Cryptodome.Cipher import ARC4

# Derive encryption key
flags = ntlm.NTLMSSP_NEGOTIATE_SEAL | \
        ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY

signingKey = ntlm.SIGNKEY(flags, session_key, mode='Client')
sealingKey = ntlm.SEALKEY(flags, session_key, mode='Client')

cipher = ARC4.new(sealingKey)

# Encrypt and sign message
message = b"Secret data"
seqNum = 0

sealedMsg, signature = ntlm.SEAL(
    flags=flags,
    signingKey=signingKey,
    sealingKey=sealingKey,
    messageToSign=message,
    messageToEncrypt=message,
    seqNum=seqNum,
    handle=cipher.encrypt
)

print(f"Encrypted: {sealedMsg.hex()}")
print(f"Signature: {signature.getData().hex()}")

Working with AV_PAIRS

from impacket import ntlm

# Parse target info from Type 2
type2 = ntlm.NTLMAuthChallenge(type2_data)
av_pairs = ntlm.AV_PAIRS(type2['TargetInfoFields'])

# Extract information
if av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME]:
    hostname = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME][1]
    print(f"Server: {hostname.decode('utf-16le')}")

if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME]:
    domain = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1]
    print(f"Domain: {domain.decode('utf-16le')}")

# Add custom fields
av_pairs[ntlm.NTLMSSP_AV_TARGET_NAME] = \
    b'http/server.domain.com'.encode('utf-16le')

# Serialize back to bytes
modified_target_info = av_pairs.getData()

HTTP NTLM Authentication

from impacket import ntlm
import base64

# Step 1: Send initial request, get 401 with WWW-Authenticate: NTLM
type1 = ntlm.getNTLMSSPType1()
type1_b64 = base64.b64encode(type1.getData()).decode()

# Send Authorization: NTLM <type1_b64>
# Receive 401 with WWW-Authenticate: NTLM <type2_b64>

# Step 2: Parse challenge
type2_b64 = "..."  # From WWW-Authenticate header
type2_data = base64.b64decode(type2_b64)

type3, session_key = ntlm.getNTLMSSPType3(
    type1=type1,
    type2=type2_data,
    user='admin',
    password='password',
    domain='CORP'
)

type3_b64 = base64.b64encode(type3.getData()).decode()

# Send Authorization: NTLM <type3_b64>
print(f"Authorization: NTLM {type3_b64}")

Extracting User Information

from impacket import ntlm

# Parse Type 3 message
type3_data = b'...'  # Captured authenticate message
type3 = ntlm.NTLMAuthChallengeResponse()
type3.fromString(type3_data)

# Get user info
user_string = type3.getUserString()
print(f"User: {user_string}")  # Format: DOMAIN/username

# Get individual fields
if type3['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE:
    user = type3['user_name'].decode('utf-16le')
    domain = type3['domain_name'].decode('utf-16le')
    workstation = type3['host_name'].decode('utf-16le')
else:
    user = type3['user_name'].decode('cp437')
    domain = type3['domain_name'].decode('cp437')
    workstation = type3['host_name'].decode('cp437')

print(f"Username: {user}")
print(f"Domain: {domain}")
print(f"Workstation: {workstation}")

Security Considerations

NTLMv1 is deprecated and insecure. Always use NTLMv2 when possible by setting use_ntlmv2=True.
LM hashes are weak and should not be used for authentication. They are computed only from the first 14 characters of passwords converted to uppercase.

Best Practices

  1. Use NTLMv2 - Set ntlm.USE_NTLMv2 = True globally
  2. Enable signing - Use NTLMSSP_NEGOTIATE_SIGN flag
  3. Enable sealing - Use NTLMSSP_NEGOTIATE_SEAL for encryption
  4. Strong passwords - Use complex passwords to prevent hash cracking
  5. Extended session security - Enable NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY

Helper Functions

NTOWFv2()

Compute NTLMv2 hash.
hash = ntlm.NTOWFv2(user, password, domain, hash='')

LMOWFv2()

Compute LMv2 hash.
hash = ntlm.LMOWFv2(user, password, domain, lmhash='')

hmac_md5()

Compute HMAC-MD5.
result = ntlm.hmac_md5(key, data)

See Also

  • SMBConnection - Uses NTLM for authentication
  • SMB - SMB protocol with NTLM support

Build docs developers (and LLMs) love