1.LM Hash & NTLM Hash
windows内部是不保存明文密码的,只保存密码的hash。
其中,本机用的密码hash是保存在本地的SAM文件中,域用户的密码hash保存在域控下的NTDS.DIT文件中。那么,hash是以什么样的形式进行保存的呢?
在我们抓取用户hash时,经常看到如下格式:
Administrator:500:AAD3B435B51404EEAAD3B435B51404EE:31D6CFE0D16AE931B73C59D7E0C089C0:::
需要明确的是:AAD3B435B51404EEAAD3B435B51404EE
是LM Hash,31D6CFE0D16AE931B73C59D7E0C089C0
是NTLM Hash
下面就详细的来了解一下这两种hash。
1.1.LM Hash
全称是LAN Manager Hash, windows最早用的加密算法,由IBM设计。
LM Hash的计算:
- 用户的密码转换为大写,密码转换为16进制字符串,不足14字节将会用0来再后面补全。
- 密码的16进制字符串被分成两个7byte部分。每部分转换成比特流,并且长度位56bit,长度不足使用0在左边补齐长度
- 再分7bit为一组,每组末尾加0,再组成一组
- 上步骤得到的二组,分别作为key 为 KGS!@#$%进行DES加密。
- 将加密后的两组拼接在一起,得到最终LM HASH值。
下面贴一个计算脚本,感兴趣的同学可自行实验:
#coding=utf-8
import re
import binascii
from pyDes import *
def DesEncrypt(str, Des_Key):
k = des(binascii.a2b_hex(Des_Key), ECB, pad=None)
EncryptStr = k.encrypt(str)
return binascii.b2a_hex(EncryptStr)
def group_just(length,text):
# text 00110001001100100011001100110100001101010011011000000000
text_area = re.findall(r'.{%d}' % int(length), text) # ['0011000', '1001100', '1000110', '0110011', '0100001', '1010100', '1101100', '0000000']
text_area_padding = [i + '0' for i in text_area] #['00110000', '10011000', '10001100', '01100110', '01000010', '10101000', '11011000', '00000000']
hex_str = ''.join(text_area_padding) # 0011000010011000100011000110011001000010101010001101100000000000
hex_int = hex(int(hex_str, 2))[2:].rstrip("L") #30988c6642a8d800
if hex_int == '0':
hex_int = '0000000000000000'
return hex_int
def lm_hash(password):
# 1. 用户的密码转换为大写,密码转换为16进制字符串,不足14字节将会用0来再后面补全。
pass_hex = password.upper().encode("hex").ljust(28,'0') #3132333435360000000000000000
print(pass_hex)
# 2. 密码的16进制字符串被分成两个7byte部分。每部分转换成比特流,并且长度位56bit,长度不足使用0在左边补齐长度
left_str = pass_hex[:14] #31323334353600
right_str = pass_hex[14:] #00000000000000
left_stream = bin(int(left_str, 16)).lstrip('0b').rjust(56, '0') # 00110001001100100011001100110100001101010011011000000000
right_stream = bin(int(right_str, 16)).lstrip('0b').rjust(56, '0') # 00000000000000000000000000000000000000000000000000000000
# 3. 再分7bit为一组,每组末尾加0,再组成一组
left_stream = group_just(7,left_stream) # 30988c6642a8d800
right_stream = group_just(7,right_stream) # 0000000000000000
# 4. 上步骤得到的二组,分别作为key 为 "KGS!@#$%"进行DES加密。
left_lm = DesEncrypt('KGS!@#$%',left_stream) #44efce164ab921ca
right_lm = DesEncrypt('KGS!@#$%',right_stream) # aad3b435b51404ee
# 5. 将加密后的两组拼接在一起,得到最终LM HASH值。
return left_lm + right_lm
if __name__ == '__main__':
hash = lm_hash("123456")
lm协议的脆弱之处在于
- des的key是固定的
- 可以根据hash判断密码长度是否大于7位,如果密码强度是小于7位,那么第二个分组加密后的结果肯定是aad3b435b51404ee
- 密码不区分大小写并且长度最大为14位
- 7+7字符分开加密明显复杂度降低14个字符整体加密 957+ 957 <95 14
- Des密码强度不高
1.2.NTLM Hash
由上面可知,LM hash其实是很脆弱的,对此,微软于1993年在Windows NT 3.1中引入了NTLM协议。NTLM Hash是支持Net NTLM认证协议及本地认证过程中的一个重要参与物,其长度为32位,由数字与字母组成。
下面是各个版本针对LM和NTLM的支持。
也就是说,从windows visita和 Windows Server 2008开始,默认情况下只存储NTLM Hash,LM Hash将不再存在。如果密码为空或者计算机不存储LM Hash,那么我们抓取到的hash为
AAD3B435B51404EEAAD3B435B51404EE
所以,在windows7中,我们抓取到的LM Hash都是
AAD3B435B51404EEAAD3B435B51404EE
2.NTLM身份验证
NTLM验证是一种Challenge/Response 验证机制,由三种消息组成:通常称为type 1(协商),类型type 2(质询)和type 3(身份验证)。值得注意的是,NTLM协议为嵌入式协议。
它基本上是这样工作的:
- 用户登录客户端电脑
- (type 1)客户端向服务器发送type 1(协商)消息,它主要包含客户端支持和服务器请求的功能列表。
- (type 2)服务器用type 2消息(质询)进行响应,这包含服务器支持和同意的功能列表。但是,最重要的是,它包含服务器产生的Challenge。
- (type 3)客户端用type 3消息(身份验证)回复质询。用户接收到步骤3中的challenge之后,使用用户hash与challenge进行加密运算得到response,将response,username,challeng发给服务器。消息中的response是最关键的部分,因为它们向服务器证明客户端用户已经知道帐户密码。
- 服务器拿到type 3之后,使用challenge和用户hash进行加密得到response2与type 3发来的response进行比较。如果用户hash是存储在域控里面的话,那么没有用户hash,也就没办法计算response2。也就没法验证。这个时候用户服务器就会通过netlogon协议联系域控,建立一个安全通道,然后将type 1,type 2,type3 全部发给域控(这个过程也叫作Pass Through Authentication认证流程)
-
域控使用challenge和用户hash进行加密得到response2,与type 3的response进行比较
下面我将详细介绍一下type1,type2,type3
2.1.type1
type1过程发送的内容主要包含客户端支持和服务器的功能列表。包结构如下:
数据包中的具体体现如下:
需要澄清的一点是,在网络上现存的大部分文章中,都表示在type1阶段会发送明文用户名,这样的说法是存在问题的,数据包中根本就找不到用户名信息,用户名是在 type 和 net-hash,一起发送的,还有些文章不仅在 type1 中加入了用户名,还给 NTLM 的过程增加了几步,属实给读者带来很大困扰。
2.2.type2
这个过程是服务器用type 2消息(质询)进行响应,这包含服务器支持和同意的功能列表。但是,最重要的是,它包含服务器产生的Challenge。
主要 包含以下结构:
其中最主要的信息是challenge。后面加密验证依赖于challenge
抓包查看对应的信息如下:
可以看到 TYPE 2 信息中,操作系统类型,主机名,netbios名,通常会利用改特性收集系统信息。
2.3.type3
这个过程客户端接收到challenge之后,使用用户hash与challenge进行加密运算得到response,将response,username,challenge发给服务器。消息中的response是最关键的部分,因为它向服务器证明客户端用户已经知道帐户密码。
主要包含以下结构:
数据包的表现形式:
这里的Challeng不同于type2 的Challenge,这里的Challenge是一个随机的客户端nonce。
提取 ntlmv2 hash,格式
username::domain:challenge:HMAC-MD5:blob
前十六个字节位 HMAC-MD5,后面blob,challenge是 Type 2 中的 ServerChallenge,拼接起来就是 ntlmv2 hash
3.Net-ntlm hash
在type3中的响应,有六种类型的响应
● LM(LAN Manager)响应 - 由大多数较早的客户端发送,这是“原始”响应类型。
● NTLM v1响应 - 这是由基于NT的客户端发送的,包括Windows 2000和XP。
● NTLMv2响应 - 在Windows NT Service Pack 4中引入的一种较新的响应类型。它替换启用了 NTLM版本2的系统上的NTLM响应。
● LMv2响应 - 替代NTLM版本2系统上的LM响应。
● NTLM2会话响应 - 用于在没有NTLMv2身份验证的情况下协商NTLM2会话安全性时,此方案会更改LM NTLM响应的语义。
● 匿名响应 - 当匿名上下文正在建立时使用; 没有提供实际的证书,也没有真正的身份验证。“存 根”字段显示在类型3消息中。
这六种使用的加密流程一样,都是前面我们说的Challenge/Response 验证机制,区别在Challenge和加密算法不同。
这里我们侧重讲下NTLM v1响应和NTLMv2响应
● v2是16位的Challenge,而v1是8位的Challenge
v1是将 16字节的NTLM hash空填充为21个字节,然后分成三组,每组7比特,作为3DES加密算法的三组密钥,加密Server发来的Challenge。 将这三个密文值连接起来得到response。
而v2是的加密算法是:
(1). 将Unicode后的大写用户名与Unicode后的身份验证目标(在Type 3消息的"TargetName"字段中指定的域或服务器名称)拼在一起。请注意,用户名将转换为大写,而身份验证目标区分大小写,并且必须与“TargetName”字段中显示的大小写匹配。使用16字节NTLM哈希作为密钥,得到一个值。
(2) 构建一个blob信息
(3). 使用16字节NTLMv2哈希作为密钥,将HMAC-MD5消息认证代码算法加密一个值(来自type 2的Challenge与Blob拼接在一起)。得到一个16字节的NTProofStr。
(4). 将NTProofStr与Blob拼接起来形成得到response。
至于选择哪个版本的响应由LmCompatibilityLevel决定。
Challenge/Response验证机制里面type3 response里面包含Net-ntlm hash,NTLM v1响应和NTLMv2响应对应的就是Net-ntlm hash分为Net-ntlm hash v1和Net-ntlm hash v2。
Net-ntlm hash v1的格式为:
username::hostname:LM response:NTLM response:challenge
Net-ntlm hash v2的格式为:
username::domain:challenge:HMAC-MD5:blob