NTLM认证详解
字数 1873 2025-08-11 17:40:15
NTLM认证协议详解
0x01 NTLM简介
NTLM(NT LAN Manager)是Windows操作系统提供的身份认证协议,主要功能包括:
- 身份认证:采用challenge-response机制
- 会话安全:支持消息签名和加密
认证流程:
- 服务器发送8字节随机数challenge
- 客户端根据密钥、challenge等信息计算response
- 服务器验证response是否匹配
0x02 NTLM消息结构
NTLM协议包含三种消息类型:
2.1.1 NEGOTIATE_MESSAGE
客户端发起协商,包含:
- Signature:固定"NTLMSSP\0"
- MessageType:0x00000001
- NegotiateFlags:协商标志位
- DomainName:客户端域名(可选)
- Workstation:客户端工作组名(可选)
2.1.2 CHALLENGE_MESSAGE
服务器响应挑战,包含:
- Signature:固定"NTLMSSP\0"
- MessageType:0x00000002
- ServerChallenge:8字节随机数
- TargetName:服务器端名称(可选)
- TargetInfo:服务器信息(可选)
2.1.3 AUTHENTICATE_MESSAGE
客户端认证响应,包含:
- Signature:固定"NTLMSSP\0"
- MessageType:0x00000003
- LmChallengeResponse:LM响应
- NtChallengeResponse:NTLM响应
- DomainName:域名
- UserName:用户名
- Workstation:工作站名
- EncryptedRandomSessionKey:加密的会话密钥(可选)
- MIC:消息完整性校验(16字节)
0x03 NTLM认证类型
3.1 哈希类型
- LM Hash:通过LMOWFv1计算
- NTLM Hash:通过NTOWFv1计算
- Net-NTLM Hash:客户端响应服务器挑战的数据
3.2 NTLM版本配置
通过注册表HKLM\SYSTEM\CurrentControlSet\Control\Lsa的LmCompatibilityLevel配置:
| 值 | 设置 | 描述 |
|---|---|---|
| 0 | 发送LM & NTLM响应 | 不使用NTLMv2 |
| 1 | 发送LM & NTLM - 如果协商使用NTLMv2 | 支持NTLMv2会话安全 |
| 2 | 仅发送NTLM响应 | 支持NTLMv2会话安全 |
| 3 | 仅发送NTLMv2响应 | 强制NTLMv2 |
| 4 | 仅发送NTLMv2响应/拒绝LM | 拒绝LM |
| 5 | 仅发送NTLMv2响应/拒绝LM & NTLM | 强制NTLMv2 |
3.3 NTLM v1认证
3.3.1 LM Hash计算
def lm_hash(password:str):
data = "KGS!@#$%".encode('utf-8')
password = password.upper().encode("utf-8")
if len(password) < 14:
password += b'\x00'*(14-len(password))
des1 = DES.new(des_7key28key(password[0:7]),DES.MODE_ECB)
des2 = DES.new(des_7key28key(password[7:14]),DES.MODE_ECB)
hash = des1.encrypt(data)+des2.encrypt(data)
return hash
3.3.2 NTLM Hash计算
def ntlm_hash(password):
hash = hashlib.new("md4",password.encode("utf-16le")).digest()
return hash
3.3.3 Response计算
不启用NTLMv2会话安全:
def ntlmv1_response(hash,server_challenge):
return desl(hash,server_challenge)
启用NTLMv2会话安全:
def ntlmv1_session(passwd:str,server_challenge:str,client_challenge:str):
ntlmhash = ntlm_hash(passwd)
challenge = server_challenge+client_challenge
challenge_md5 = hashlib.md5(challenge).digest()
lm_response = client_challenge+16*b'\x00'
ntlm_reponse = ntlmv1_response(ntlmhash,challenge_md5[0:8])
return lm_response,ntlm_reponse
3.4 NTLM v2认证
3.4.1 NTLMv2 Hash计算
def ntlmv2_hash(password,username,domain):
ntlmhash = ntlm_hash(password)
username = username.upper().encode('utf-16le')
domain = domain.encode('utf-16le')
ntlmv2hash = hmac.new(ntlmhash,username+domain,hashlib.md5).digest()
return ntlmv2hash
3.4.2 Response计算
def ntlmv2(passwd,username,domain,server_challenge,client_challenge,timestamp,avpairs):
ntlmv2hash = ntlmv2_hash(passwd,username,domain)
temp = RespType_HiRespType_Reserved+timestamp+client_challenge+b'\x00'*4
for avpair in avpairs:
temp = temp + avpair
temp = temp + b'\x00'*4
NTProofStr = hmac.new(ntlmv2hash,server_challenge+temp,hashlib.md5).digest()
ntlmv2_response = NTProofStr+temp
lmv2_response = hmac.new(ntlmv2hash,server_challenge+client_challenge,hashlib.md5).digest()+client_challenge
return lmv2_response,ntlmv2_response
0x04 会话安全
4.1 ExportedSessionKey生成
if (NTLMSSP_NEGOTIATE_KEY_EXCH bit is set):
Set ExportedSessionKey to NONCE(16) # 16字节随机数
Set EncryptedRandomSessionKey to RC4(KeyExchangeKey, ExportedSessionKey)
Else:
Set ExportedSessionKey to KeyExchangeKey
4.2 KXKEY计算
NTLMv1不启用v2会话安全:
def kxkey_ntlmv1_LMKEY(lm_hash,lm_response):
des1 = DES.new(des_7key28key(lm_hash[0:7]),DES.MODE_ECB)
des2 = DES.new(des_7key28key(lm_hash[7:8]+b'\xbd'*6),DES.MODE_ECB)
return des1.encrypt(lm_response[0:8])+des2.encrypt(lm_response[0:8])
NTLMv1启用v2会话安全:
def kxkey_ntlmv1_WITH_NT_SESSION(sessionbasekey,lm_response,server_challenge):
return hmac.new(sessionbasekey,server_challenge+lm_response[0:8],hashlib.md5).digest()
4.3 SIGNKEY计算
用于消息完整性校验:
if (NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY):
if (Mode == "Client"):
SignKey = MD5(ExportedSessionKey + "client-to-server signing key magic constant")
else:
SignKey = MD5(ExportedSessionKey + "server-to-client signing key magic constant")
4.4 SEALKEY计算
用于消息加密:
if (NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY):
if (NTLMSSP_NEGOTIATE_128):
SealKey = ExportedSessionKey
elif (NTLMSSP_NEGOTIATE_56):
SealKey = ExportedSessionKey[0..6]
else:
SealKey = ExportedSessionKey[0..4]
0x05 安全特性
5.1 MIC(消息完整性校验)
使用ExportedSessionKey作为HMAC_MD5密钥,计算三个消息的哈希值,防止消息被篡改。
5.2 ChannelBinding
将服务器证书哈希存入AV_PAIR(MsvAvChannelBindings),防止中间人攻击。
0x06 攻击方法
6.1 Pass The Hash
利用已知的NTLM Hash计算NTLM Response,绕过密码验证。
6.2 NTLM Relay
中间人攻击,转发客户端的认证响应到服务器。
0x07 防御建议
- 禁用LM认证,使用NTLMv2
- 启用SMB签名
- 限制NTLM使用范围
- 启用EPA(Extended Protection for Authentication)
- 使用Kerberos替代NTLM
附录:完整Python实现
[见原文0x06节完整代码]