Windows协议学习–NTML协议(上)

Windows协议学习--NTML协议(上)

前言

攻击原理还得先从底层协议和原理开始逐步学习,本章主要是对NTML协议进行学习,其中对Windows凭证的加密方式以及和NTML协议有关的安全缺陷和一些常见横向首发进行学习,文章主要参考daiker师傅的NTML协议介绍

LM Hash & NTLM Hash

在使用mimikatz或者是结合procdump来dump lsass进程内存的时候会将用户LM和NTLM的Hash值,其实对应的是两种加密的方式,在这里也学习一下。

我们知道在版本较高的Windows内部是不会存储明文密码的,只会保存密码的Hash值,其中本机用户的密码hash是放在本地的SAM文件里面,域内用户的密码hash是存在域控的NTDS.DIT文件 里面。
其中SAM文件的路径:
SAM文件路径:%SystemRoot%system32config

在Windows系统导出密码的时候,经常看到这样的密码格式

Administrator:500:AAD3B435B51404EEAAD3B435B51404EE:31D6CFE0D16AE931B73C59D7E0C089C0:::

格式是:用户名称: RID:LM-HASH 值: NT-HASH 值
其中rid是Windows系统账户对应固定的值,类似于linux的uid,gid号,500为 administrator,501为guest等,在这里介绍LM-HASH和NTLM-HASH加密方式的区别。

LM Hash

LM Hash(LAN Manager Hash)是windows最早用的加密算法,由IBM设计。LM Hash使用硬编码秘钥的DES,且存在缺陷。早期的Windows系统如XP、Server 2003等使用LM Hash,而后的系统默认禁用了LM Hash并使用NTLM Hash。

LM Hash的计算方式为:

  • 转换用户的密码为大写,14字节截断
  • 不足14字节则需要在其后添加0×00补足
  • 将14字节分为两段7字节的密码
  • KGS!@#$% 作为秘钥对这两组数据进行DES加密,得到16字节的哈希
    拼接后得到最后的LM Hash。

我们来用加密算法看一下当只有一段密码而后面都是通过0x00填充时的效果:

#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")
    #44efce164ab921ca aad3b435b51404ee
    hash = lm_hash("111111")
    #e8450c7e07112982 aad3b435b51404ee

其中LM作为最早期算法,还是存在比较多的缺陷和问题:

  • 密码长度不会超过14字符,且不区分大小写
  • 如果密码长度小于7位,后一组哈希的值确定,可以通过结尾为 aad3b435b51404ee 来判断密码长度不超过7位
  • 一个14个字符的密码分成7 + 7个字符,并且分别为这两个半部分计算哈希值。这种计算哈希值的方式使破解难度大大降低
  • DES算法强度低

NTLM Hash

为了解决LM Hash的安全问题,微软于1993年在Windows NT 3.1中引入了NTLM协议。

Windows 2000 / XP / 2003在密码超过14位前使用LM Hash,在密码超过14位后使用NTLM Hash。而之后从Vista开始的版本都使用NTLM Hash。
下面是各版本对LM和NTLM的支持:

upload_3eafb71dee4fa1a10cb4e22fa6740fc2

注意:如果空密码或者不储蓄LM Hash的话,我们抓到的LM Hash是AAD3B435B51404EEAAD3B435B51404EE,在有些工具的使用中规定了格式需要LM,此时我们可以填全0即可

NTLM的加密方式并不复杂,介绍一下NTLM的加密方式:

  1. 先将用户密码转换为十六进制格式。
  2. 将十六进制格式的密码进行Unicode编码。
  3. 使用MD4摘要算法对Unicode编码数据进行Hash计算

NTLM验证

学习完LMNTLM的加密方式以及特点之后再来看NTLM身份验证

NTLM验证是一种Challenge/Response验证机制,是 Windows NT 早期版本中的标准安全协议。由三种消息组成:通常称为type 1(协商),类型type 2(质询)和type 3(身份验证)
借用daiker师傅的图和描述:

upload_f94616772624baa74029898bf3acd353

  1. 用户登录客户端电脑
  2. (type 1)客户端向服务器发送type 1(协商)消息,它主要包含客户端支持和服务器请求的功能列表。
  3. (type 2)服务器用type 2消息(质询)进行响应,这包含服务器支持和同意的功能列表。但是,最重要的是,它包含服务器产生的Challenge。
  4. (type 3)客户端用type 3消息(身份验证)回复质询。用户接收到步骤3中的challenge之后,使用用户hash与challenge进行加密运算得到response,将response,username,challeng发给服务器。消息中的response是最关键的部分,因为它们向服务器证明客户端用户已经知道帐户密码。
  5. 服务器拿到type 3之后,使用challenge和用户hash进行加密得到response2与type 3发来的response进行比较。如果用户hash是存储在域控里面的话,那么没有用户hash,也就没办法计算response2。也就没法验证。这个时候用户服务器就会通过netlogon协议联系域控,建立一个安全通道,然后将type 1,type 2,type3 全部发给域控(这个过程也叫作Pass Through Authentication认证流程)
  6. 域控使用challenge和用户hash进行加密得到response2,与type 3的response进行比较

其实这里的chanllenge就是在服务端产生的一个16位的随机数字也就是nonce,客户端用加密后的密码散列来加密challenge,然后返回给服务器,作为response,在这里也一并将用户名返回给服务器方便服务器发送给域控进行查询和验证

在NTLM验证中一共有六种类型的响应,这里我们主要是学习NTLM v1响应和NTLMv2响应

Net-NTLMv1响应

Net-NTLMv1协议的基本流程如下:

  1. 客户端向服务器发送一个请求
  2. 服务器接收到请求后,生成一个8位的Challenge,发送回客户端
  3. 客户端接收到Challenge后,使用登录用户的密码hash对Challenge加密,4. 作为response发送给服务器
  4. 服务器校验response

Net-NTLMv1 response的计算方法为:
将用户的NTLM hash补零至21字节分成三组7字节数据,三组数据作为3DES加密算法的三组密钥,加密Server发来的Challenge,将三个密文值连接起来得到response

这种方式相对脆弱,可以基于抓包工具和彩虹表爆破工具进行破解。

Net-NTLMv2响应

自Windows Vista起,微软默认使用Net-NTLMv2协议,其基本流程如下:

  1. 客户端向服务器发送一个请求
  2. 服务器接收到请求后,生成一个16位的Challenge,发送回客户端
  3. 客户端接收到Challenge后,使用登录用户的密码hash对Challenge加密,作为response发送给服务器
  4. 服务器校验response

这里NTLM v1与NTLM v2最显著的区别就是Challenge与加密算法不同,共同点就是加密的密钥都是NTLM Hash

  • Challage:NTLM v1的Challenge有8位,NTLM v2的Challenge为16位。
  • Net-NTLM Hash:NTLM v1的主要加密算法是3DES,NTLM v2的主要加密算法是HMAC-MD5。

SSP和SSPI

在这里还需要学习两个概念,也是在Windows设计过程中定义的用来获得验证、信息完整性、信息隐私等安全功能的一套安全服务

SSPI

SSPI(Security Support Provider Interface),即安全服务提供接口,这是Windows定义的一套接口,该接口定义了与安全有关的功能函数,但是并没有进行具体的实现,仅是提供了接口,包含但不限于:

  • 身份验证机制
  • 信息完整性
  • 为其他协议提供的会话安全机制

SSP

SSP(Security Service Provider),即安全服务提供,是SSPI的实现者,是对SSPI相关功能函数的具体实现。微软自己实现了如下的 SSP,用于提供安全功能:

NTLM SSP
Kerberos
Digest SSP
Cred SSP
......

在系统层面,SSP就是一个dll,来实现身份验证等安全功能,实现的身份验证机制是不一样的。比如 NTLM SSP 实现的就是一种 Challenge/Response 验证机制。而 Kerberos 实现的就是基于 ticket 的身份验证机制。我们可以编写自己的 SSP,然后注册到操作系统中,让操作系统支持更多的自定义的身份验证方法。

在抓包过程中发现,这里ntml验证是处在GSS-API协议内部的,这是因为其实SSPI是GSSAPI的一个专有变体,进行了扩展并具有许多特定于Windows的数据类型。SSPI生成和接受的令牌大多与GSS-API兼容。

upload_5c11a7379bb410dbdbdea7c8c6cb4e3e

我们注册为SSP的一个好处就是,SSP实现了了与安全有关的功能函数,那上层协议(比如SMB)在进行身份认证等功能的时候,就可以不用考虑协议细节,只需要调用相关的函数即可。而认证过程中的流量嵌入在上层协议里面。不像kerbreos,既可以镶嵌在上层协议里面,也可以作为独立的应用层协议。NTLM是只能镶嵌在上层协议里面,消息的传输依赖于使用NTLM的上层协议。比如镶嵌在SMB协议里面是这样。
upload_fda848f26d0d8ffc0970eb43e8f47d31

相关安全问题

PTH

哈希传递攻击不仅是在Kerberos协议中出现,其实在使用NTLM协议认证中同样存在Pass The Hash,因为在type3阶段中客户端计算response时是使用用户的Hash进行计算的,因此这里同样存在PTH攻击

使用mimikatz进行PTH攻击

privilege::debug
sekurlsa::pth /user:用户名 /domain:域名称 /ntlm:NTLM值

upload_4613e7b36ee05eb5c835ef28979b36ea

impacket进行PTH攻击
impacket底下执行远程命令执行的脚本有5个

psexec.py
smbexec.py
atexec.py
wmiexec.py
dcomexec.py

都支持使用hash进行远程命令执行,通过--hashes指定hash,以wmiexe为例

upload_b31caf0815bf2de8d16afccb5079ca46

还要其他例如在MSF中的模块:
exploit/windows/smb/psexec_psh
以及CS支持的横向psexec

利用ntlm进行的信息收集

upload_c63661dd7b16bbf0e3ddabe96c039cf0

来看一下type2返回的Challenge响应,在type2返回Challenge的过程中,同时返回了操作系统类型,主机名,netbios名等等。这也就意味着如果我们在能跟服务器进行NTLM交流中,给服务器发送一个type1的请求,服务器返回type2的响应,这一步,我们就可以相关机器信息

而NTLM是一个嵌入式的协议,消息的传输依赖于使用NTLM的上层协议,比如SMB,LDAP,HTTP等,因此当对方主机的139或者445端口开放时,我们可以向服务器发送type1请求来获取type2的响应进而收集信息

msf底下也有类似的模块auxiliary/scanner/smb/smb_version模块可以进行信息收集,其原理也就是利用NTLM协议


NTLM relay的学习在后续在填坑

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇