NTLM端口信息探测
SMB(Server Message Block)协议,可用于在计算机间共享文件、打印机、串口等,电脑上的网上邻居就是靠它实现的。SMB使用了NetBIOS的应用程序接口 (Application Program Interface,简称API)。另外,它是一个开放性的协议,允许了协议扩展——使得它变得更大而且复杂;大约有65个最上层的作业,而每个作业都超过120个函数,甚至Windows NT也没有全部支持到,最近微软又把 SMB 改名为 CIFS(Common Internet File System),并且加入了许多新的特色。SMB协议一般端口使用为139,445,CIFS协议有三个版本:SMB、SMB2、SMB3。
NTLM
在type2返回Challenge的过程中,同时返回了操作系统类型,主机名,netbios名等等。这也就意味着如果我们在能跟服务器进行NTLM交流中,给服务器发送一个type1的请求,服务器返回type2的响应,这一步,我们就可以得到很多信息。SMBv1和SMBv2的数据包结构是不同的
SMBv1
使用非攻NTLMINFO探测SMB接口,抓包通过wireshark分析,包含操作系统类型的数据包由SMB Header和Response组成:
我们的目的是获取smb数据包的NTLM数据,然后对NTLM数据包解析,NTLM数据包上一层是GSS-API,首先找到GSS-API在整个数据包的偏移量,SMB的数据包结构长度如下:
SMB Header: 32 byte
Word Count: 1 byte
AndXCommand: 1 byte
Reserved: 1 byte
AndXOffset: 2 byte
Action: 2 byte
Security Blob Length: 2 byte (表示Security Blob的长度,这里的hex是 0f 10,小端转换为010f,再转换成10进制就是271,对应Security Blob的长度)
Byte Count: 2 byte (表示Security Blob加上NativeOS和Native Lan的长度)
Security Blob: 可变长度,取决于Security Blob Length
上面的数据包结构的关键数据是Security Blob Length
和Byte Content
,前者表示GSS-API的整个数据包长度,后者表示GSS-API和Native OS加上Native LM的数据长度:
GSS-API的长度是271 Byte
Native OS的长度是42 Byte
Native LM的长度是38 Byte
所以数学题来了:
Security Blob Length转换成10进制是271 Byte
Byte Count: 271 + 42 + 38 = 351 Byte
我们的目的是获取NTLM的数据和NativeOS和Native LM,回到代码里面去看看,当我们获取到type2的数据,获取到的数据是什么呢?
NetBIOS Session Service这一层的长度是4 Byte,Security Blob Length
的偏移量就出来了:
4 + 32 + 1 + 1 + 1 + 2 + 2 = 43
所以Security Blob Length
偏移从43开始,长度是2 Byte, Security Blob
跟在后面,偏移从45开始,47结束,go语言伪代码:
blob_length := uint16(bytes2Uint(ret[43:45], '<'))
blob_count := uint16(bytes2Uint(ret[45:47], '<'))
//gss变量表示从Security Blob起始位置到数据包结束,包括了Native OS和Native LM
gss := ret[47:]
//找到NTLMSSP在gss的偏移起始位置
off_ntlm := bytes.Index(gss, []byte("NTLMSSP"))
//Native OS和Native LM数据,对应上面的图
native := gss[int(blob_length):blob_count]
//bs表示ntlm的数据,以NTLMSSP开头
bs := gss[off_ntlm:blob_length]
接下来主要工作就是解析NTLM的数据,感谢iv4n师傅的Windows NTLM协议细节,我fork了一份go-ntlmssp,增加了解析NTLM输出字符串函数,可以直接获取NTLMSSP数据的解析结果:
func TestChallengeMsg_String(t *testing.T) {
bs, _ := hex.DecodeString("4e544c4d535350xxxxx")
type2 := ChallengeMsg{}
info := type2.String(bs)
fmt.Println(info)
}
Windows10默认使用SMBv2协议,没有打开SMBv1开关,Rcoll师傅的SharpDetectionNTLMSSP只发送了SMBv1的探测,没有探测SMBv2。非攻NTLMINFO师傅就比较完整,先探测SMBv1,失败之后尝试SMBv2。
SMBv2
参考非攻师傅的代码,先发送第一次的探测请求,找到偏移量70的地方,做一次判断是否发送第二个数据包。换成GO代码就比较简单了,这里的偏移70保存的是是SMBv2的Security mode
:
Go语言如下:
var NTLMSSPNegotiatev2Data []byte
if hex.EncodeToString(r2[70:71]) == "03" {
flags := []byte{0x15, 0x82, 0x08, 0xa0}
NTLMSSPNegotiatev2Data = getNTLMSSPNegotiateData(flags)
} else {
flags := []byte{0x05, 0x80, 0x08, 0xa0}
NTLMSSPNegotiatev2Data = getNTLMSSPNegotiateData(flags)
}
_, err = conn2.Write(NegotiateSMBv2Data2)
if err != nil {
return
}
readBytes(conn2)
_, err = conn2.Write(NTLMSSPNegotiatev2Data)
ret, _ := readBytes(conn2)
ntlmOff := bytes.Index(ret, []byte("NTLMSSP"))
成品主要参考非攻师傅的代码,集成到Cube,完成了winrm、wmi、smb、mssql端口的NTLM信息探测。