NTLM 认证协议

1. 什么是NTLM

在AD中,我们会遇到多种身份认证协议,其中Kerberos协议是最常见的,另一种协议 NTLM 尽管存在已知漏洞和加密弱点,但至今仍被广泛使用

1.1. NT Lan Manager

NT Lan Manager( NTLM / MS-NLMP)是一系列安全协议的名称,包括 LM 、 NTLMv1 和 NTLMv2 ,被各种基于 Windows 的网络上的应用协议用于验证远程用户,并在应用请求时可选地提供会话安全。 NTLM 安全协议都是嵌入式协议,这意味着尽管 NTLM 像其他协议一样具有消息和状态机,但它没有网络协议栈层。 NTLM 的这种特性允许任何在网络栈中定义了层的协议(例如 SMB 、 HTTP(s)和 LDAP(s))来利用它。对于使用它的应用协议, NTLMv2 提供了三种主要操作:

  • 身份验证。
  • 信息完整性(消息signing)
  • 信息机密性(信息sealing)

1.2. 挑战-响应协议

NTLM 是一种挑战-响应协议,它使用 nonces(为一次性使用而生成的伪随机数)作为防御重放攻击的机制。尽管该协议有两种变体——面向连接和无连接,但我们主要只关注前者(有关两者之间的差异,请参阅MS-NLMP 中的概述部分)

1.3. NTLM SSP

与普通的协议实现不同,NTLM 最好被实现为一个可由应用协议调用的函数库,而不是网络协议栈中的一层。安全支持提供程序接口 (SSPI) 是 Windows 身份验证的基础,它是一个 API,允许连接的应用程序调用多个安全提供程序之一,以建立经过身份验证的连接并通这些连接安全地交换数据。

安全支持提供程序 (SSP) 是一个DLL,负责通过向应用程序公开一个或多个安全包来实现 SSPI;每个安全包提供应用程序的 SSPI 函数调用与实际安全模型函数之间的映射。这些安全包支持各种安全协议,包括NTLM

NTLM SSP(位于 %Windir%\System32\msv1_0.dll)是一种二进制消息协议,由 SSPI 调用,用于实现 NTLM 质询-响应身份验证,并协商完整性和机密性选项。NTLM SSP 涵盖了 NTLM 和 NTLMv2 身份验证协议。

加入域的计算机和工作站机器都可以使用NTLM进行身份验证
请记住以下内容

  • 主机上使用的 NTLM 版本(无论是 NTLMv1 还是 NTLMv2)是在身份验证之前通过带外方式配置的。
  • 通过一种安全机制,客户端与服务器/域控制器(DC)在身份验证之前共享一个密钥(用户密码的哈希值)。
  • 明文凭据和共享密钥都不会在网络上传输。

1.4. 认证工作流程

加入域的计算机的 NTLM 身份验证工作流程始于客户端与服务器(所需服务所在的位置)交换特定于实现的应用协议消息,表明其想要进行身份验证。随后,客户端和服务器在身份验证期间交换三条特定的 NTLM 消息(嵌入在应用协议消息中):

一旦服务器收到 AUTHENTICATE_MESSAGE,由于它并不拥有客户端的密钥,服务器会通过调用 NetrLogonSamLogonWithFlags 将用户身份的验证委托给域控制器(DC)(这一过程称为直通身份验证 Pass-through authentication)。该调用包含 NETLOGON_NETWORK_INFO,这是一个填充了 DC 验证用户所需各字段的数据结构。如果身份验证成功,DC 会向服务器返回 NETLOGON_VALIDATION_SAM_INFO4 数据结构,随后服务器与客户端建立经过身份验证的会话;否则,DC 会返回错误,服务器可能会向客户端返回错误消息,或者直接终止连接。

下图是一个NTLM传递身份认证的流程
Pasted image 20260310175249.png
下面是工作站的验证流程,(直接由服务器验证用户身份,而非是委托给域控)
Pasted image 20260310175254.png

1.5. NTLM 消息

NTLM 消息的长度是可变的,头部为固定长度,消息载荷是可变的。头部始终以协议标识符和消息类型字段开始,根据消息类型的不同,消息可能包含额外的与消息相关的固定长度字段。这些字段之后是可变长度的消息载荷
Pasted image 20260310175519.png

字段 含义
Signature 一个 8 字节的以 NULL 结尾的 ASCII 字符串,始终设置为 [N, T, L, M, S, S, P, \0]。
MessageType 一个 4 字节无符号整数,始终设置为以下值之一:0x00000001 (NtLmNegotiate) 表示该 NTLM 消息是 NEGOTIATE_MESSAGE;0x00000002 (NtLmChallenge) 表示该 NTLM 消息是 CHALLENGE_MESSAGE;或者 0x00000003 (NtLmAuthenticate) 表示该 NTLM 消息是 AUTHENTICATE_MESSAGE。
MessageDependentFields 一个可变长度字段,包含 NTLM 消息内容。
payload 一个可变长度字段,包含与消息相关的若干个独立有效载荷消息,通过 MessageDependentFields 中的字节偏移量进行引用。

1.5.1. NEGOTIATE_MESSAGE  协商消息

NEGOTIATE_MESSAGE 是第一条特定于 NTLM 的消息,由客户端发送,表明其希望向服务器进行身份验证,并指定其支持或请求的 NTLM 选项。它包含四个与消息相关的固定长度字段。

其中一个需要了解的重要字段是 NegotiateFlags;这个 4 字节字段存在于所有三条 NTLM 消息中,并非 NEGOTIATE_MESSAGE 所特有。它是一个由 32 个 1 位标志组成的 NEGOTIATE 结构,用于指明发送方支持或请求哪些 NTLM 功能。

1.5.2. CHALLENGE_MESSAGE  挑战消息

CHALLENGE_MESSAGE 是第二条特定于 NTLM 的消息,由服务器发送给客户端,用于声明其能够支持的 NTLM 选项,并挑战客户端以证明其身份

它包含六个与消息相关的固定长度字段,其中有两个需要重点了解:NegotiateFlags 和 ServerChallenge。NegotiateFlags 保存了服务器从客户端在 NEGOTIATE_MESSAGENegotiateFlags 字段中提供或请求的选项中所选择的标志。同时,ServerChallenge 是一个 64 位的随机数,保存了服务器生成的 NTLM 挑战值。

可以使用一些工具,如 NTLM Challengerntlm-infoNTLMReconDumpNTLMInfo.py来解析 CHALLENGE_MESSAGE 中返回的信息

python3 examples/DumpNTLMInfo.py 172.16.117.3

Impacket v0.12.0.dev1+20230803.144057.e2092339 - Copyright 2023 Fortra

[+] SMBv1 Enabled   : False
[+] Prefered Dialect: SMB 3.0
[+] Server Security : SIGNING_ENABLED | SIGNING_REQUIRED
[+] Max Read Size   : 8.0 MB (8388608 bytes)
[+] Max Write Size  : 8.0 MB (8388608 bytes)
[+] Current Time    : 2023-08-14 17:39:26.822236+00:00
[+] Name            : DC01
[+] Domain          : INLANEFREIGHT
[+] DNS Tree Name   : INLANEFREIGHT.LOCAL
[+] DNS Domain Name : INLANEFREIGHT.LOCAL
[+] DNS Host Name   : DC01.INLANEFREIGHT.LOCAL
[+] OS              : Windows NT 10.0 Build 17763
[+] Null Session    : True

1.5.3. AUTHENTICATE_MESSAGE 认证消息

AUTHENTICATE_MESSAGE 是第三条也是最后一条特定于 NTLM 的消息,由客户端发送给服务器,用以证明其拥有共享密钥

它包含九个与消息相关的固定长度字段,其中有两个需要重点了解:LmChallengeResponseFieldsNtChallengeResponseFields。对于 MS-NLMP 提供的伪代码,可以参考 Impacket 中 NTLM 的相关实现。

1.5.4. NTLMv1 响应计算

在身份验证过程中,如果双方协商启用了 NTLMSSP_NEGOTIATE_LM_KEY,客户端将根据协议版本构建不同的响应结构。

如果在 NegotiateFlags 中,服务器和客户端协商一致启用了 NTLMSSP_NEGOTIATE_LM_KEY(即 NEGOTIATE 消息中的 G 位),则响应结构如下:

对于 LmChallengeResponse 字段结构:

  • NTLMv1 模式:使用 LM_RESPONSE 结构。
    • Response:24 字节数组,存放客户端的 LmChallengeResponse。
  • NTLMv2 模式:使用 LMv2_RESPONSE 结构。
    • Response:16 字节数组,存放客户端的 LM 挑战响应。
    • ChallengeFromClient:8 字节数组,存放客户端生成的挑战值。

MS-NLMP 提供了计算上述 Response 字段的 伪代码

对于核心单向函数 (OWF):

  • NTOWFv1:基于用户密码创建哈希,在 compute_nthash 函数中实现。计算时使用哈希结果而非明文密码。
  • LMOWFv1:基于用户密码创建哈希,在 compute_lmhash 函数中实现。仅由 LM 和 NTLMv1 使用。
  • 计算结果:客户端利用上述函数通过 computeResponseNTLMv1 完成最终响应。

对于 NtChallengeResponse Fields:

  • 使用 NTLMv1,NtChallengeResponse 将包含一个 NTLM_RESPONSE 结构。
  • 使用 NTLMv2,则 NtChallengeResponse 将包含一个 NTLMv2_RESPONSE 结构。

NTLM_RESPONSE

  • 包含一个字段 Response,这是一个 24 字节的无符号字符数组,其中包含客户端的 NtChallengeResponse。

NTLMv2_RESPONSE:

  • 包含两个字段:Response 和一个 NTLMv2_CLIENT_CHALLENGE 结构。
  • Response 是一个 16 字节的无符号字符数组,包含客户端的 NtChallengeResponse。
  • NTLMv2_CLIENT_CHALLENGE 是一个可变长度的字节数组,包含八个固定长度的变量,其中包括 ChallengeFromClient。

如果我们使用 Responder(一个我们将在下一节学习的强大工具)捕获 NTLMv1 哈希,它将使用以下格式进行显示:
User::HostName:LmChallengeResponse:NtChallengeResponse:ServerChallenge:

[SMB] NTLMv1 Client   : 172.19.117.36
[SMB] NTLMv1 Username : Support1
[SMB] NTLMv1 Hash     : Support1::WIN-OLMHXGAP0V2:e2dL3196O8f55fB6:Q49S19A2937J6XC3CKA418EI4958OHB9:xF2K324O5L6Q7V8C

1.5.5. NTLMv2 响应计算

在身份验证过程中,对于 NTLMv2 响应计算:

  • NTOWFv2LMOWFv2:两者均为版本相关且仅由 NTLMv2 使用(分别通过 LMOWFv2NTOWFv2 函数实现),是基于用户密码生成哈希以创建主体安全密钥的单向函数。
  • 响应计算:借助这两个函数,客户端按照 伪代码(通过 computeResponseNTLMv2 函数实现)所述,计算并返回给服务器响应值。

若使用 Responder 捕获 NTLMv2 哈希,其显示格式如下:
User::Domain:ServerChallenge:Response:NTLMv2_CLIENT_CHALLENGE:

[SMB] NTLMv2-SSP Client   : 172.19.117.55
[SMB] NTLMv2-SSP Username : INLANEFREIGHT\Support2
[SMB] NTLMv2-SSP Hash     : Support2::INLANEFREIGHT:e2d2339638fc5fd6:D4979A923DD76BC3CFA418E94958E2B0:010100000000000000E0550D97CCD901509F9CE743AB58760000000002000800350034005800360001001E00570049004E002D00390038004B005100480054005300390048004200550004003400570049004E002D00390038004B00510048005400530039004800420055002E0035003400580036002E004C004F00430041004C000300140035003400580036002E004C004F00430041004C000500140035003400580036002E004C004F00430041004C000700080000E0550D97CCD901060004000200000008003000300000000000000000000000004000002DB95E9E27F0AD66CAA477372F555B500CFEA9C5A231FC68F0DA4FABFF76607E0A001000000000000000000000000000000000000900240063006900660073002F003100370032002E00310036002E003100310037002E00330030000000000000000000

1.6. NTLM 会话安全

如果客户端和服务器协商一致,会话安全可提供消息完整性(签名/signing)和消息保密性(密封/sealing)。

  • NTLM 协议本身不提供会话安全,而是由 SSPI 提供
  • NTLMv1 已被 NTLMv2 取代,它不支持密封(sealing),仅支持签名(signing)
  • 因此,微软强烈建议不要使用 NTLMv1(以及已弃用的 LM 身份验证协议)

1.7. 消息签名与密封

在 NTLM 认证和 SMB 通信的上下文中,Signing(签名)和 Sealing(密封)是确保会话安全的两大核心机制:

1.7.1. Signing (签名) —— 确保“完整性”

Signing 就像是在信封上盖了一个防伪印章

  • 作用: 消息签名用于提供消息完整性,防止重放攻击(Relay Attacks)。
  • 原理: 客户端和服务器会协商一个会话密钥(Session Key)。发送方使用该密钥对消息内容进行加密计算,生成一个 MAC(消息认证码)。
  • 结果: 接收方收到消息后会校验 MAC。如果消息在传输过程中被黑客篡改,校验就会失败。这保证了消息确实来自声称的一方,且内容未被动过。

1.7.2. Sealing (密封) —— 确保“机密性”

Sealing 就像是将信件放入一个保险箱

  • 作用: 消息密封通过对称加密机制提供消息保密性
  • 原理: 它会对整个消息载荷进行加密。
  • 结果: 即使黑客在网络上截获了数据包,看到的也只是乱码,无法读取其中的实际内容。
  • 注意: 在 NTLM 的上下文中,密封隐含了签名——也就是说,每一个被密封(加密)的消息也必然是经过签名(完整性校验)的。
特性 Signing (签名) Sealing (密封)
安全目标 完整性 机密性
主要防范 数据篡改、中继攻击 数据窃听、隐私泄露
效果 能看到内容,但改不了 看不到内容,也改不了
版本支持 NTLMv1 和 NTLMv2 均支持 仅 NTLMv2 支持

SMB签名的更新
由于攻击者经常用默认的 SMB 签名设置,微软发布了更新,在 Windows 11 Insider 版本(以及后续的主要版本)中强制启用 SMB 签名。
在以往的 Windows 10 和 11 中,默认仅在连接到名为 SYSVOL 和 NETLOGON 的共享时才要求 SMB 签名;而域控制器仅在任何客户端与其连接时才要求 SMB 签名。

消息密封
消息密封(Message sealing)通过实现对称密钥加密机制来提供消息的机密性;它确保客户端与服务器之间交换的消息内容保持安全,使攻击者无法读取或篡改。在 NTLM 的语境下,密封(sealing)也意味着签名(signing),因为每条经过密封的消息同时也都经过了签名。

1.8. 身份验证扩展保护(EPA)

Extended Protection for Authentication (EPA),基于 RFC 5056,是 Windows Server 2008 及后续版本中引入的一项功能,旨在增强 NTLM 认证的安全性。

  • 建立安全通道:当 EPA 启用时,客户端和服务器会使用通道绑定令牌(CBT)建立一个安全通道。
  • 绑定认证特性:CBT 将认证绑定到特定的通道特性(如 IP 地址和端口),从而防止认证在不同的通道上被重放。
  • 协议支持:EPA 设计用于与 SMB 和 HTTP 协议配合工作,为依赖 NTLM 认证的应用程序和服务提供额外安全性。
  • 前提条件:建立安全通道需要客户端和服务器双方都支持该功能。