安全矩阵

 找回密码
 立即注册
搜索
查看: 2495|回复: 0

一文了解NTLM与NTLM 身份认证

[复制链接]

189

主题

191

帖子

903

积分

高级会员

Rank: 4

积分
903
发表于 2022-3-19 01:22:35 | 显示全部楼层 |阅读模式
原文链接:一文了解NTLM与NTLM 身份认证 (qq.com)


1. LM Hash & NTLM Hash
windows内部是不保存明文密码的,只保存密码的hash。
其中,本机用的密码hash是保存在本地的SAM文件中,域用户的密码hash保存在域控下的NTDS.DIT文件中。那么,hash是以什么样的形式进行保存的呢?

在我们抓取用户hash时,经常看到如下格式:
  1. Administrator:500:AAD3B435B51404EEAAD3B435B51404EE:31D6CFE0D16AE931B73C59D7E0C089C0:::
复制代码



需要明确的是:
  1. <p><code>AAD3B435B51404EEAAD3B435B51404EE</code>是LM Hash,</p>

  2. <p><code>31D6CFE0D16AE931B73C59D7E0C089C0</code>是NTLM Hash。</p>
复制代码


下面就详细的来了解一下这两种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值。


下面贴一个计算脚本,感兴趣的同学可自行实验:
  1. #coding=utf-8
  2. import re
  3. import binascii
  4. from pyDes import *
  5. def DesEncrypt(str, Des_Key):
  6.     k = des(binascii.a2b_hex(Des_Key), ECB, pad=None)
  7.     EncryptStr = k.encrypt(str)
  8.     return binascii.b2a_hex(EncryptStr)
  9. def group_just(length,text):
  10.     # text 00110001001100100011001100110100001101010011011000000000
  11.     text_area = re.findall(r'.{%d}' % int(length), text) # ['0011000', '1001100', '1000110', '0110011', '0100001', '1010100', '1101100', '0000000']
  12.     text_area_padding = [i + '0' for i in text_area] #['00110000', '10011000', '10001100', '01100110', '01000010', '10101000', '11011000', '00000000']
  13.     hex_str = ''.join(text_area_padding) # 0011000010011000100011000110011001000010101010001101100000000000
  14.     hex_int = hex(int(hex_str, 2))[2:].rstrip("L") #30988c6642a8d800
  15.     if hex_int == '0':
  16.         hex_int = '0000000000000000'
  17.     return hex_int
  18. def lm_hash(password):
  19.     # 1. 用户的密码转换为大写,密码转换为16进制字符串,不足14字节将会用0来再后面补全。
  20.     pass_hex = password.upper().encode("hex").ljust(28,'0') #3132333435360000000000000000
  21.     print(pass_hex)
  22.     # 2. 密码的16进制字符串被分成两个7byte部分。每部分转换成比特流,并且长度位56bit,长度不足使用0在左边补齐长度
  23.     left_str = pass_hex[:14] #31323334353600
  24.     right_str = pass_hex[14:] #00000000000000
  25.     left_stream = bin(int(left_str, 16)).lstrip('0b').rjust(56, '0') # 00110001001100100011001100110100001101010011011000000000
  26.     right_stream = bin(int(right_str, 16)).lstrip('0b').rjust(56, '0') # 00000000000000000000000000000000000000000000000000000000
  27.     # 3. 再分7bit为一组,每组末尾加0,再组成一组
  28.     left_stream = group_just(7,left_stream) # 30988c6642a8d800
  29.     right_stream = group_just(7,right_stream) # 0000000000000000
  30.     # 4. 上步骤得到的二组,分别作为key 为 "KGS!@#$%"进行DES加密。
  31.     left_lm = DesEncrypt('KGS!@#$%',left_stream) #44efce164ab921ca
  32.     right_lm = DesEncrypt('KGS!@#$%',right_stream) # aad3b435b51404ee
  33.     # 5. 将加密后的两组拼接在一起,得到最终LM HASH值。
  34.     return left_lm + right_lm
  35. if __name__ == '__main__':
  36.     hash = lm_hash("123456")
复制代码

#coding=utf-8import reimport binasciifrom 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_intdef 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_lmif __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 type1type1过程发送的内容主要包含客户端支持和服务器的功能列表。包结构如下:





数据包中的具体体现如下:





需要澄清的一点是,在网络上现存的大部分文章中,都表示在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,格式为:

           

  1. username::domain:challenge:HMAC-MD5:blob
复制代码







前十六个字节位HMAC-MD5,后面blob,challenge是Type2中的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的格式为:
  1. username::hostname:LM response:NTLM response:challenge
复制代码


Net-ntlm hash v2的格式为:
  1. username::domain:challenge:HMAC-MD5:blob
复制代码



4.会话签名 和 身份验证签名(MIC)在详细说明该体系下存在的问题之前,我们先补充一个概念。
细心的同学们可能会注意到,在上面的ntml协议的数据包中少了点什么,具体少了什么呢?没错,就是MIC。





那么MIC具有什么作用呢?
MIC 保护了 NTLM 不会被修改。什么意思呢?

NTLM身份验证由3种消息类型组成:
NTLM_NEGOTIATE,NTLM_CHALLENGE,NTLM_AUTHENTICATE。为了确保恶意行为者不在传输过程中处理消息,在NTLM_AUTHENTICATE消息中添加了一个额外的MIC(消息完整性代码)字段。MIC是使用会话密钥应用于所有3个NTLM消息的串联的HMAC_MD5,该会话密钥仅对启动认证的帐户和目标服务器是已知的。因此,试图篡改其中一条消息的攻击者(例如,修改签名协商)将无法生成相应的MIC,这将导致攻击失败。

翻译一下就是:会话签名决定了验证通过后,通讯会话是否需要加密。若通讯加密,因为无法获得加密的密钥,则无法完成后面的中间人攻击。
具体在认证过程中,数据包中是通过哪些字段来确认是否需要签名呢?
这里我总结了一下:

           
  •         在谈判阶段,双方都表明他们的要求:其中之一是否需要签名?

           
  •         在身份验证阶段,双方都表明他们支持什么。是否有签名能力?

           
  •         在会话阶段,如果功能和要求兼容,则使用已协商的内容进行会话。


在 NTLM 协商阶段,NEGOTIATE_SIGN 若为 1 则表示有签名能力(但不是一定要签名)。





是否要启用签名主要取决于两个标志位  Signing enabled 和 Signing required
Signing enabled 表示是否支持签名
Signing required 表示是否需要签名

不同协议判定是否需要签名的条件也不同

           
  •         SMB:双方有一方的 Signing required 为 1 时,启用签名。也就是如果有一方明确表示了 需要签名 才会被启用。windows PC默认是不需要签名的,只有 server 版本默认需要签名(域控 2012 碰到过不开签名的情况,判断 SMB 是否需要签名,发送一次请求即可判断)。

           
  •         LDAP:协商签名,双方都支持签名则使用签名。这就是为啥在中继的时候不能从 SMB 中继到 LDAP 的原因。SMB 默认支持签名, 也就是 Signing enabled 字段默认为 1。从 SMB 到 LDAP 会触发 LDAP 签名导致中间人失败。

           
  •         HTTP:不支持签名。所以 可以从 HTTP 中继 LDAP 完成攻击。server2019 默认域控强制开启 LDAP 签名,让 HTTP 无法再中继到 LDAP,但是仍然可以中继到 LDAPS。

5.相关安全问题5.1 PTHpass the hash也叫做hash传递攻击,简称:PTH

根据前一篇文章我们知道,在type3计算response的时候,客户端是使用用户的hash进行计算的,而不是用户密码进行计算的。因此在模拟用户登录的时候。是不需要用户明文密码的,只需要用户hash。

Ps:在此不对pth的工具进行一一展示使用,后续会补上(挖坑),工具根本原理相同,不同的只是使用方法,这里仅选择一款展示效果

pth的效果:





说到PTH,就不得不提kb2871997这款补丁了,kb2871997这款补丁仅能暂缓pth,并不能杜绝pth。至于为什么,这里暂不讨论,后续补上(挖坑)
5.2 利用ntlm进行的信息收集渗透测试的深度取决于信息收集的广度,那么,在打点阶段或者内网渗透阶段,能够有效获取目标主机的信息,就为拿下目标多一层可能。

这里直接看到type2阶段数据包:





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

前面我们说过ntlm是一个嵌入式的协议,消息的传输依赖于使用ntlm的上层协议,比如SMB,LDAP,HTTP等。我们以SMB为例。在目标主机开放了445或者139的情况,通过给服务器发送一个type1的请求,然后解析type2的响应。就可以收集到一些信息。

这里直接贴一下网上找到的大牛的代码:

  1. using System;
  2. using System.Data;
  3. using System.Text;
  4. using System.Text.RegularExpressions;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. using System.Threading;
  8. using System.Diagnostics;
  9. using System.IO;
  10. using System.Security.Cryptography;
  11. using System.Net;
  12. using System.Net.Sockets;
  13. using System.Reflection;
  14. using System.Runtime;
  15. using System.Runtime.InteropServices;
  16. namespace Zcg.Tests
  17. {
  18.     class smbver
  19.     {
  20.         static byte[] d1 ={
  21.     0x00, 0x00, 0x00, 0x85, 0xFF, 0x53, 0x4D, 0x42, 0x72, 0x00, 0x00, 0x00, 0x00, 0x18, 0x53, 0xC8,
  22.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE,
  23.     0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x02, 0x50, 0x43, 0x20, 0x4E, 0x45, 0x54, 0x57, 0x4F,
  24.     0x52, 0x4B, 0x20, 0x50, 0x52, 0x4F, 0x47, 0x52, 0x41, 0x4D, 0x20, 0x31, 0x2E, 0x30, 0x00, 0x02,
  25.     0x4C, 0x41, 0x4E, 0x4D, 0x41, 0x4E, 0x31, 0x2E, 0x30, 0x00, 0x02, 0x57, 0x69, 0x6E, 0x64, 0x6F,
  26.     0x77, 0x73, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x57, 0x6F, 0x72, 0x6B, 0x67, 0x72, 0x6F, 0x75, 0x70,
  27.     0x73, 0x20, 0x33, 0x2E, 0x31, 0x61, 0x00, 0x02, 0x4C, 0x4D, 0x31, 0x2E, 0x32, 0x58, 0x30, 0x30,
  28.     0x32, 0x00, 0x02, 0x4C, 0x41, 0x4E, 0x4D, 0x41, 0x4E, 0x32, 0x2E, 0x31, 0x00, 0x02, 0x4E, 0x54,
  29.     0x20, 0x4C, 0x4D, 0x20, 0x30, 0x2E, 0x31, 0x32, 0x00
  30. };
  31.         static byte[] d2 ={
  32.     0x00, 0x00, 0x01, 0x0A, 0xFF, 0x53, 0x4D, 0x42, 0x73, 0x00, 0x00, 0x00, 0x00, 0x18, 0x07, 0xC8,
  33.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE,
  34.     0x00, 0x00, 0x40, 0x00, 0x0C, 0xFF, 0x00, 0x0A, 0x01, 0x04, 0x41, 0x32, 0x00, 0x00, 0x00, 0x00,
  35.     0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0xA0, 0xCF, 0x00, 0x60,
  36.     0x48, 0x06, 0x06, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x02, 0xA0, 0x3E, 0x30, 0x3C, 0xA0, 0x0E, 0x30,
  37.     0x0C, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0A, 0xA2, 0x2A, 0x04,
  38.     0x28, 0x4E, 0x54, 0x4C, 0x4D, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x82, 0x08,
  39.     0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  40.     0x00, 0x05, 0x02, 0xCE, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6E, 0x00,
  41.     0x64, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00,
  42.     0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00,
  43.     0x20, 0x00, 0x33, 0x00, 0x37, 0x00, 0x39, 0x00, 0x30, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00,
  44.     0x72, 0x00, 0x76, 0x00, 0x69, 0x00, 0x63, 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x61, 0x00,
  45.     0x63, 0x00, 0x6B, 0x00, 0x20, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x69, 0x00,
  46.     0x6E, 0x00, 0x64, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00,
  47.     0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00,
  48.     0x33, 0x00, 0x20, 0x00, 0x35, 0x00, 0x2E, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00
  49. };
  50. static byte[] d3={
  51. 0x81,0x00,0x00,0x44,0x20,0x43,0x4b,0x46,0x44,0x45,0x4e,0x45,0x43,0x46,0x44,0x45
  52. ,0x46,0x46,0x43,0x46,0x47,0x45,0x46,0x46,0x43,0x43,0x41,0x43,0x41,0x43,0x41,0x43
  53. ,0x41,0x43,0x41,0x43,0x41,0x00,0x20,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43
  54. ,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43
  55. ,0x41,0x43,0x41,0x43,0x41,0x41,0x41,0x00
  56. };
  57.         static void Main(string[] args)
  58.         {
  59.             Console.WriteLine("SMB Version Detection tool 0.1");
  60.             Console.WriteLine("Part of GMH's fuck Tools, Code By zcgonvh.\r\n");
  61.             if (args.Length < 1) { Console.WriteLine("usage: smbver host [port]"); return; }
  62.             string host = args[0];
  63.             int port = 445;
  64.             try { port = int.Parse(args[1]); }
  65.             catch { }
  66.             try
  67.             {
  68.                 byte[] buf = new byte[1024];
  69.                 Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  70.                 sock.Connect(host, port);
  71.                 if(port==139)
  72.                 {
  73.                   sock.Send(d3);
  74.                   sock.Receive(buf);
  75.                 }
  76.                 sock.Send(d1);
  77.                 sock.Receive(buf);
  78.                 sock.Send(d2);
  79.                 sock.Receive(buf);
  80.                 int len = BitConverter.ToInt16(buf, 43);
  81.                 string[] ss = Encoding.Unicode.GetString(buf, len + 47, buf.Length - len - 47).Split('\0');
  82.                 Console.WriteLine("native os: " + ss[0]);
  83.                 Console.WriteLine("native lan manager: " + ss[1]);
  84.                 int off = 0;
  85.                 for (int i = 47; i < len - 7; i++)
  86.                 {
  87.                     if (buf[i] == 'N' && buf[i + 1] == 'T' && buf[i + 2] == 'L' && buf[i + 3] == 'M' && buf[i + 4] == 'S' && buf[i + 5] == 'S' && buf[i + 6] == 'P') { off = i; break; }
  88.                 }
  89.                 byte[] ntlm = new byte[len];
  90.                 Array.Copy(buf, off, ntlm, 0, len);
  91.                 len = BitConverter.ToInt16(ntlm, 0xc);
  92.                 off = BitConverter.ToInt16(ntlm, 0x10);
  93.                 Console.WriteLine("negotiate target: " + Encoding.Unicode.GetString(ntlm, off, len));
  94.                 Console.WriteLine("os major version: " + ntlm[off - 8]);
  95.                 Console.WriteLine("os minor version: " + ntlm[off - 7]);
  96.                 Console.WriteLine("os build number: " + BitConverter.ToInt16(ntlm, off - 6));
  97.                 Console.WriteLine("ntlm current revision: " + ntlm[off - 1]);
  98.                 off += len;
  99.                 int type = BitConverter.ToInt16(ntlm, off);
  100.                 while (type != 0)
  101.                 {
  102.                     off += 2;
  103.                     len = BitConverter.ToInt16(ntlm, off);
  104.                     off += 2;
  105.                     switch (type)
  106.                     {
  107.                         case 1:
  108.                             {
  109.                                 Console.WriteLine("NetBIOS computer name: " + Encoding.Unicode.GetString(ntlm, off, len));
  110.                                 break;
  111.                             }
  112.                         case 2:
  113.                             {
  114.                                 Console.WriteLine("NetBIOS domain name: " + Encoding.Unicode.GetString(ntlm, off, len));
  115.                                 break;
  116.                             }
  117.                         case 3:
  118.                             {
  119.                                 Console.WriteLine("DNS computer name: " + Encoding.Unicode.GetString(ntlm, off, len));
  120.                                 break;
  121.                             }
  122.                         case 4:
  123.                             {
  124.                                 Console.WriteLine("DNS domain name: " + Encoding.Unicode.GetString(ntlm, off, len));
  125.                                 break;
  126.                             }
  127.                         case 5:
  128.                             {
  129.                                 Console.WriteLine("DNS tree name: " + Encoding.Unicode.GetString(ntlm, off, len));
  130.                                 break;
  131.                             }
  132.                         case 7:
  133.                             {
  134.                                 Console.WriteLine("time stamp: {0:o}", DateTime.FromFileTime(BitConverter.ToInt64(ntlm, off)));
  135.                                 break;
  136.                             }
  137.                         default:
  138.                             {
  139.                                 Console.Write("Unknown type {0}, data: ", type);
  140.                                 for (int i = 0; i < len; i++)
  141.                                 {
  142.                                     Console.Write(ntlm[i + off].ToString("X2"));
  143.                                 }
  144.                                 Console.WriteLine();
  145.                                 break;
  146.                             }
  147.                     }
  148.                     off += len;
  149.                     type = BitConverter.ToInt16(ntlm, off);
  150.                 }
  151.             }
  152.             catch (Exception ex)
  153.             {
  154.                 Console.WriteLine("err: " + ex);
  155.             }
  156.         }
  157.     }
  158. }
复制代码

大家也可以仿造代码的形式,自己实现其他上层协议下的信息收集。网图效果如下:





msf底下也有类似的模块
  1. <p><code>auxiliary/scanner/smb/smb_version</code></p><p></p>
复制代码





5.3 ntlm relay作为一个在上世纪就被提出的安全问题,时至2022的今天,ntlm_relay仍然在远程命令执行。横向扩展,权限提升等方面发挥着巨大的作用。本篇文章剩余部门简单的介绍一些ntlm_relay相关的概念。

工具流程如下:





看图已经能够很清晰得理解ntlm_relay的一般过程,作为中间人,攻击者将来自客户端的包(type 1)转发给服务端,将来自服务端的challenge(type 2)转发给客户端,然后客户端计算完response 之后,再把response(type 3) 转发给服务端,服务端验证rsponse通过之后,授予攻击者访问的权限。

局限性:
中继的前提是目标 SMB 签名需要关闭,在 SMB 协议中,需要使用安全机制来保护服务器和客户端之间传输数据的完整性,而这种安全机制就是 SMB 签名和加密,如果关闭 SMB 签名,会允许攻击者拦截认证过程,并且将获得 hash 在其他机器上进行重放,从而获得权限。在工作组环境里面,工作组中的机器之间相互没有信任关系,每台机器的账号密码 Hash 只是保存在自己的 SAM 文件中,这个时候 Relay 到别的机器,除非两台机器的账号密码一样,不然没有别的意义了。

扩展:
由于NTLM协议为嵌入式协议,中继的方式并不局限于SMBtoSMB,也可以HTTPtoSMB,还有一个需要注意的地方就是,不同协议之间的特性,例如上面提到的SMB签名问题,又例如LDAP签名问题。

攻击效果:





本次攻击实验利用Inveigh做了一个局域网投毒+中继操作,(局域网投毒还涉及几个协议,这里放到下一篇来讲(挖坑))通过Inveigh监听中间人地址,当被投毒计算机进行某些操作时(可以发起NTLM请求的操作),就会直接将请求发送到中间人,中间人将请求再原封不动的发送给被中继机器。
Ps:局域网环境下,该操作其实并不常用,域环境下会遇到
6.总结以上部分是针对局域网下的一些常用手法,也简单介绍了一下这些手法下的局限性,下一篇将讲讲本次未涉及的一些其他协议。不可去名上理会。须求其所以然。





回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-24 12:09 , Processed in 0.023177 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表