NT(New Technology) LAN(Local Area Network) Manager Authentication Protocol
零、序
首先需要讲解一下非常容易与NTLM协议混淆在一起的那些Hash名称
根据年代不同, 密码哈希的存储方式也会有所不同, 并且这些哈希名称和NTLM身份认证协议这个名称也十分相似(比如说: Net-NTLMv2 Hash和Net-NTLMv2, 前者是密码Hash, 而后者则是身份认证协议)
过去: 我们为了解决用户需求,现在有14个标准正在运行。
设计师A: 14个?太荒谬了吧???我们需要制定一个能解决所有人问题的通用标准。
设计师B: 确实!
现在: 我们成功的从14个标准减少到了15个标准。
研究员: 6
简单来说,它们的关系如下:
这是密码Hash的种类 | 这是身份认证协议 |
---|---|
LM Hash NT Hash(A.K.A NTLM Hash) Net-NTLMv1 Hash(A.K.A. NTLMv1 Hash) Net-NTLMv2 Hash(A.K.A. NTLMv2 Hash) |
NTLM(包含如下两个版本) NTLMv1 NTLMv2 |
而不同的认证方式,所涉及到的Hash种类也不同,它们的关系如下:
Windows本地认证 | Windows网络认证 |
---|---|
LM Hash NT Hash(A.K.A NTLM Hash) |
Net-NTLMv1 Hash(A.K.A. NTLMv1 Hash) Net-NTLMv2 Hash(A.K.A. NTLMv2 Hash) |
一、Windows本地认证介绍
用户输入密码 -> winlogon.exe -> lsass.exe -> SAM
用户登陆 Windows 时,Windows 首先会调用 winlogon.exe 进程接收用户输入的密码,之后密码会被传递给 lsass.exe 进程,进程会先在内存中存储一份明文密码,并将密码加密为 NTLM hash,与本地 SAM 数据库中用户的 NTLM hash 对比,一致则登陆成功。
LM Hash – 1987年
LM Hash是Windows使用的最古老的密码存储方式,非常容易被破解,通常位于Windows系统中SAM数据库里面或者域控中NTDS数据库里面。
e.g. LM-Hash.txt(pass: password)
1 | e52cac67419a9a224a3b108f3fa6cb6d |
LM Hash加密
- 将用户密码中所有的小写字母转换为大写,并转换为16进制字符串
- 如果转换后的16进制字符串的长度不足14字节,用0补全。
- 然后将这14字节分为两组,每组7个字节,然后转为二进制数据,每组二进制数据长度为
7*8 = 56bit
然后每组二进制数据按7bit为一组,分为8组,每组末尾加0又补为8bit,然后转为16进制,这样每组就变成了8字节的16进制数据 - 然后用上面生成的两组8字节的16进制数据作为密钥,分别对
KGS!@#$%
这串明文进行DES加密(传闻可能是Key of Glen and Steve and then the combination of Shift + 12345.) - 然后将DES加密之后的两组密文进行拼接,得到最终的LM Hash值。
用python实现一下LM Hash生成流程(见附录:LM Hash加密脚本)
LM Hash爆破
使用常见hash爆破工具john进行爆破
1 | john --format=lm LM-Hash.txt |
NT Hash – 1993
这是现代 Windows 系统上存储密码的方式,可以通过转储 SAM 数据库或使用 Mimikatz 来获取。它们还存储在域控制器上的 NTDS.dit 文件中。NTLM Hash可以用来进行哈希传递攻击(Pass The Hash)。
e.g. NT-Hash.txt(pass: 123456)
1 | 32ed87bdb5fdc5e9cba88547376818d4 |
e.g. 通过hashdump的方式抓取到的hash(hashdump.txt)
1 | Administrator:500:aad3b435b51404eeaad3b435b51404ee:32ed87bdb5fdc5e9cba88547376818d4::: |
NT Hash加密
- 先将用户密码转换为16进制格式
- 然后将16进制格式的密码转为unicode编码
- 然后将unicode编码用md4算法加密生成哈希值
用python实现一下NT Hash生成流程(见附录:NT Hash加密脚本)
NT Hash爆破
单独爆破NT Hash部分
1 | john --format=nt NT-Hash.txt |
爆破hashdump的方式抓取到的hash(hashdump.txt)
LM Hash和NT Hash的区别
- LM Hash从Windows Vista/Windows Server 2008开始就已经默认关闭了,但是可以在GPO中启用它
- LM Hash不区分大小写, NT Hash区分大小写。
- LM Hash字符集有限,只有142个字符,NT Hash支持整个unicode字符集(65535个字符)
- LM Hash将用户密码按7个字符为一组拆分成两组,并且会用0填充不足的部分,NT Hash根据用户输入的整个密码计算Hash值。
LM Hash是一种非常弱的单向函数,用于存储密码。LM 哈希最初是为 LAN Manager 操作系统发明的,后来被包含在Windows NT中以实现向后兼容性。
二、Windows网络认证介绍
为了避免在客户端和服务端进行身份验证的时候,直接通过网络直接发送用户的哈希值。所以发明了Net-NTLM协议,用来进行身份验证
NTLM协议流程
NTLM是一种基于质询(Challenge)和响应(Response)的协议,由以下三种消息组成:
- Type 1 – 协商(Negotiation)
- Type 2 – 质询(Challenge)
- Type 3 – 认证(Authentication)
NTLM协议的整体流程大致如下:
0. 用户输入用户名和密码
客户端发起与服务器的协商请求(Negotiate protocol request),类似于握手过程
服务端响应,然后身份验证流程开始
然后客户端向服务端发起协商请求(Negotiation),该请求还包含有关客户端的一些信息
服务器端向客户端发起质询(Challenge)请求,里面包含NTLM Server Challenge
客户端接收到 Challenge 后,使用登录用户的密码 hash 对 Challenge 加密,作为 response 发送给服务器
服务器校验 response
NTLM协议有两个版本
- NTLMv1
- Server Challenge: 8 Byte
- Net-NTLMv1 Hash: DES加密
- NTLMv2
- Server Challenge: 16 Byte
- Net-NTLMv2 Hash: HMAC-MD5加密(Ps: HMAC-MD5与普通MD5相比,简单说就是多了个Key)
Net-NTLMv1 Hash(又被称为 NTLMv1 Hash) – 1993年
服务器发送8个字节的随机数(Server Challenge)来验证客户端身份
Net-NTLMv1 Hash的结构
1 | username:hostname:LM responce:NTLM responce:challenge |
e.g. Net-NTLMv1 Hash
1 | u4-netntlm::kNS:338d08f8e26de93300000000000000000000000000000000:9526fb8c23a90751cdd619b6cea564742e1e4bf33006ba41:cb8086049ec4736c |
Net-NTLMv1 Hash加密
Net-NTLMv2 Hash(又被称为 NTLMv2 Hash) – 1998年
Net-NTLMv2 Hash的结构
1 | [User name]::[Domain name]:[NTLM Server Challenge]:[HMAC-MD5]:[blob] |
e.g. Net-NTLMv2 Hash:
1 | jack::WidgetLLC.Internal:9b5afa46ef7bd599:ad3157225b1766669a2a61c158c47db8:010100000000000071ac95fe34edd90140a80b1bed9758f400000000020012005700490044004700450054004c004c0043000100100043004c00490045004e00540030003300040024005700690064006700650074004c004c0043002e0049006e007400650072006e0061006c000300360043004c00490045004e005400300033002e005700690064006700650074004c004c0043002e0049006e007400650072006e0061006c00050024005700690064006700650074004c004c0043002e0049006e007400650072006e0061006c000700080071ac95fe34edd90106000400020000000800300030000000000000000000000000300000c78e803920758ec5672c36696ee163f6a4e61c8b5463c247daef8571677995a40a001000000000000000000000000000000000000900200053004d00540050002f0075006e007300700065006300690066006900650064000000000000000000 |
Net-NTLMv2 Hash加密
弱口令判断逻辑
通过sensor log的
- server Chanllge
- ntlmv2_response
- username
- domain name
- 结合ntlm hash(遍历弱口令列表)
算出一个值,来和流量中的NTPoofStr对比,若相等则判断为弱口令。
具体加密算法如下
- username转换成大写,之后进行unicode编码,之后拼接domain name,使用HMAC-MD5算法,NTLM hash为key,计算出一个NTLMv2 hash。
- 接下来使用server Chanllge拼接上ntlmv2_response 16个字节之后的内容,使用HMAC-MD5算法算法,NTLMv2 hash作为key,算出来一个值,这个值就是流量中的NTProofStr。
python脚本如下
1 | import hmac |
Net-NTLMv2 Hash爆破
Net-NTLMv2 Hash爆破(其余Hash爆破方式类似)
使用工具john进行爆破:
1 | john --wordlist=/usr/share/wordlists/rockyou.txt --format=netntlmv2 Net-NTLMv2-Hash.txt |
将Net-NTLMv2 Hash先写入一个文件, 然后使用john进行爆破, 爆破结果比较明显, 如图所示:
使用工具hashcat进行爆破
1 | hashcat -m 5600 "jack::WidgetLLC.Internal:9b5afa46ef7bd599:ad3157225b1766669a2a61c158c47db8:010100000000000071ac95fe34edd90140a80b1bed9758f400000000020012005700490044004700450054004c004c0043000100100043004c00490045004e00540030003300040024005700690064006700650074004c004c0043002e0049006e007400650072006e0061006c000300360043004c00490045004e005400300033002e005700690064006700650074004c004c0043002e0049006e007400650072006e0061006c00050024005700690064006700650074004c004c0043002e0049006e007400650072006e0061006c000700080071ac95fe34edd90106000400020000000800300030000000000000000000000000300000c78e803920758ec5672c36696ee163f6a4e61c8b5463c247daef8571677995a40a001000000000000000000000000000000000000900200053004d00540050002f0075006e007300700065006300690066006900650064000000000000000000" /usr/share/wordlists/rockyou.txt --force |
爆破结果会附加在Net-NTLMv2 Hash之后, 如图所示:
Net-NTLMv1 Hash 与 Net-NTLMv2 Hash的区别
- Net-NTLMv1 Hash使用DES加密,Net-NTLMv2 Hash使用HMAC-MD5加密
三、搭建NTLM协议认证环境进行流量分析
所有Windows虚拟机镜像都在微软官网进行下载,没有在任何第三方渠道进行下载。完整域环境搭建,请参考另一篇文章。
域控:
DC01
- 内网IP地址: 192.168.0.1
- 操作系统: Windows Server 2016 Standard Evaluation
域成员:
Client01 - 内网IP地址: 192.168.0.3
- 操作系统: Windows10
- 在该篇文章中所扮演的角色: Wireshark(在此台机器上进行抓取流量包的操作)
Client02 - 内网IP地址: 192.168.0.4
- 操作系统: Windows Server 2012 R2 Standard Evaluation
- 在该篇文章中所扮演的角色: IIS Web Server
Client03 - 内网IP地址: 192.168.0.5
- 操作系统: Windows Server 2016 Standard Evaluation
- 在该篇文章中所扮演的角色: ExchangeServer2016-x64-cu11
1. 在DC01上创建测试用账户
在域控 DC01 上,添加账户jack,密码123456。(此处使用弱密码是因为更改过域内策略,如果无法使用弱密码,就按照要求使用强密码即可)
服务器管理器 -> 工具 -> Active Directory用户和计算机 -> Users -> 新建 -> 用户
2. 在Client02上搭建IIS Web Server
在Client02上搭建IIS Web服务器并且启用NTLM认证,步骤如下:
首先安装IIS服务,服务管理器 -> 管理 -> 添加角色和功能 -> … -> 服务器角色 -> Web服务器(IIS) -> … -> 角色服务 -> Windows身份验证。中间省略掉的部分则采用默认值即可。
然后启用Windows身份验证,步骤如下:
服务管理器 -> 工具 -> Internet Information Services (IIS)管理器 -> CLIENT02 -> Default Web Site -> 身份验证
注意,这里将 匿名身份验证
禁用,将 Windows 身份验证
启用。目的是为了让用户在访问这个Web服务器的时候,必须要先经过 Windows
身份验证,也就是这篇文章所述的NTLM认证
3. 在Client03上搭建ExchangeServer
在Client03上搭建ExchangeServer2016-x64-cu11服务,用来进行SMTP协议中的NTLM认证
首先根据上面提供的链接,下载ExchangeServer,然后直接安装即可,中途会反复遇到几次报错,按照报错提示,安装缺失的软件即可。
安装完毕之后,打开 Exchange Management Shell
使用如下命令检查pop3服务的状态
1 | Get-Service MSExchangePOP3; Get-Service MSExchangePOP3BE |
如果显示 Stop
则需要手动开启一下 pop3服务
1 | Start-Service MSExchangePOP3; Start-Service MSExchangePOP3BE |
四、各种场景下Net-NTLM认证的流量分析
1、在SMB协议中使用NTLM认证
在Client01机器的命令行中,使用 net use \\192.168.0.1 /user:jack 123456
命令进行认证。
Wireshark使用过滤器 ip.addr == 192.168.0.1 && ip.addr == 192.168.0.3
抓取干净完整的流量包,可以看到在SMB协议中,NTLM认证过程如下所示:
2、在HTTP协议中使用NTLM认证
打开Client01机器的浏览器,访问环境搭建中在Client02里搭建好的IIS Web Server。在本示例中的地址为 http://192.168.0.4
。
Wireshark使用过滤器 ip.addr == 192.168.0.3 && ip.addr == 192.168.0.4
抓取干净完整的流量包,可以看到在HTTP协议中,NTLM认证过程如下所示:
3、在SMTP协议中使用NTLM认证
使用NTLM认证的方式向搭建好的Exchange服务器发送邮件
用python写了一个在SMTP服务中发起NTLM认证通信的脚本(见附录:NTLM认证通信脚本)
运行脚本,抓取流量
认证流量的文本如下(在SMTP协议认证中,NTLM认证过程是将数据通过base64编码传递的):
1 | Client-negotiation: |
可以看到,wireshark并未像之前几种情况一样,对Net-NTLM通信过程进行字段解析。
尝试根据Net-NTLM协议的RFC,构造一个提取Net-NTLMv2 Hash的脚本,模拟wireshark解析字段的过程。
Net-NTLMv2 Hash的结构如下
1 | [User name]::[Domain name]:[NTLM Server Challenge]:[HMAC-MD5]:[blob] |
根据RFC规范,去提取所需要的字段
User name: 查阅上图的The Type 3 Message可以看到,User name的security buffer在36字节到44字节这个位置。继续查阅RFC规范:
byte | example | description |
---|---|---|
36 | 0x080008004c000000 | User Name Security Buffer: Length: 8 bytes (0x0800) Allocated Space: 8 bytes (0x0800) Offset: 76 bytes (0x4c000000) |
发现在User name的这8个字节中,前两个字节代表User name的长度(A),后四个字节代表User name的偏移(B)。也就是说,在NTLM通信数据中,从第B个字节开始,数A个字节长度,就能得到User name这个字段的值。同理,按照同样的方法,查阅上图的The Type 3 Message也能取到Domain name。
然后我们取NTLM Server Challenge的值,在The Type 2 Message中,可以看到NTLM Server Challenge是从第24个字节开始,往后数8个字节。
最后取[HMAC-MD5]:[blob]
部分,根据The Type 3 Message取到NTLM Response的值,然后以0101数据开始的部分为blob部分,0101之前的数据为 HMAC-MD5(NTProofStr)的值。
例如取到的NTLM Response的值为
1 | ad3157225b1766669a2a61c158c47db8010100000000000071ac95fe34edd90140a80b1bed9758f400000000020012005700490044004700450054004c004c0043000100100043004c00490045004e00540030003300040024005700690064006700650074004c004c0043002e0049006e007400650072006e0061006c000300360043004c00490045004e005400300033002e005700690064006700650074004c004c0043002e0049006e007400650072006e0061006c00050024005700690064006700650074004c004c0043002e0049006e007400650072006e0061006c000700080071ac95fe34edd90106000400020000000800300030000000000000000000000000300000c78e803920758ec5672c36696ee163f6a4e61c8b5463c247daef8571677995a40a001000000000000000000000000000000000000900200053004d00540050002f0075006e007300700065006300690066006900650064000000000000000000 |
则HMAC-MD5(NTProofStr)为:
1 | ad3157225b1766669a2a61c158c47db8 |
blob为:
1 | 010100000000000071ac95fe34edd90140a80b1bed9758f400000000020012005700490044004700450054004c004c0043000100100043004c00490045004e00540030003300040024005700690064006700650074004c004c0043002e0049006e007400650072006e0061006c000300360043004c00490045004e005400300033002e005700690064006700650074004c004c0043002e0049006e007400650072006e0061006c00050024005700690064006700650074004c004c0043002e0049006e007400650072006e0061006c000700080071ac95fe34edd90106000400020000000800300030000000000000000000000000300000c78e803920758ec5672c36696ee163f6a4e61c8b5463c247daef8571677995a40a001000000000000000000000000000000000000900200053004d00540050002f0075006e007300700065006300690066006900650064000000000000000000 |
根据协议规范写了一个提取Net-NTLMv2 Hash的脚本(Net-NTLMv2_Hash_extract.py),输入authentication和challenge即可提取出Net-NTLMv2 Hash。(见附录:[Net-NTLMv2 Hash提取](#Net-NTLMv2 Hash提取))
运行脚本:
1 | python Net-NTLMv2_Hash_extract.py --authentication TlRMTVNTUAADAAAAGAAYAJQAAABoAWgBrAAAACQAJABYAAAACAAIAHwAAAAQABAAhAAAABAAEAAUAgAANYKI4goAYUoAAAAPlpzQGvcgZYNK+bFD8MLj4lcAaQBkAGcAZQB0AEwATABDAC4ASQBuAHQAZQByAG4AYQBsAGoAYQBjAGsAQwBMAEkARQBOAFQAMAAxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK0xVyJbF2ZmmiphwVjEfbgBAQAAAAAAAHGslf407dkBQKgLG+2XWPQAAAAAAgASAFcASQBEAEcARQBUAEwATABDAAEAEABDAEwASQBFAE4AVAAwADMABAAkAFcAaQBkAGcAZQB0AEwATABDAC4ASQBuAHQAZQByAG4AYQBsAAMANgBDAEwASQBFAE4AVAAwADMALgBXAGkAZABnAGUAdABMAEwAQwAuAEkAbgB0AGUAcgBuAGEAbAAFACQAVwBpAGQAZwBlAHQATABMAEMALgBJAG4AdABlAHIAbgBhAGwABwAIAHGslf407dkBBgAEAAIAAAAIADAAMAAAAAAAAAAAAAAAADAAAMeOgDkgdY7FZyw2aW7hY/ak5hyLVGPCR9rvhXFneZWkCgAQAAAAAAAAAAAAAAAAAAAAAAAJACAAUwBNAFQAUAAvAHUAbgBzAHAAZQBjAGkAZgBpAGUAZAAAAAAAAAAAAB9PQ7SVTdDE/LiHVir1drI= --challenge TlRMTVNTUAACAAAAEgASADgAAAA1gonim1r6Ru971ZkAAAAAAAAAAMQAxABKAAAACgA5OAAAAA9XAEkARABHAEUAVABMAEwAQwACABIAVwBJAEQARwBFAFQATABMAEMAAQAQAEMATABJAEUATgBUADAAMwAEACQAVwBpAGQAZwBlAHQATABMAEMALgBJAG4AdABlAHIAbgBhAGwAAwA2AEMATABJAEUATgBUADAAMwAuAFcAaQBkAGcAZQB0AEwATABDAC4ASQBuAHQAZQByAG4AYQBsAAUAJABXAGkAZABnAGUAdABMAEwAQwAuAEkAbgB0AGUAcgBuAGEAbAAHAAgAcayV/jTt2QEAAAAA |
输出结果:
1 | 生成的Net-NTLMv2 Hash为: |
4、在POP3协议中使用NTLM认证
与SMTP协议类似,但是在此域环境的配置下有问题,不支持NTLM认证,在这里先放一个完整的认证流量以作示例。
1 | +OK The Microsoft Exchange POP3 service is ready. |
5、在LDAP协议中使用NTLM认证
首先用ldp命令打开ldap客户端,然后选择 连接 -> 绑定 -> 与凭据绑定
可以看到在LDAP中使用了NTLM认证
附
LMHash加密脚本
1 | #coding=utf-8 |
NTHash加密脚本
1 | import binascii |
NTLM认证通信脚本
1 | import base64 |
Net-NTLMv2 Hash提取
1 | import base64 |
参考文献:
【1】Deep dive: Logging on to Windows