winrm我记得认证方式挺多的,但因为只遇到了NTLM认证,就只讲这个叭~
WINRM(NTLM认证)
干讲有点抽象,就直接从代码上看吧
STEP1/NT_HASH计算
from Crypto.Hash import MD4
password = "pass@word1".encode("utf-16le")
md4 = MD4.new()
md4.update(password)
nt_hash = md4.digest()
很简单啊,将password使用utf-16le编码后进行一次MD4哈希就拿到了NT_HASH
STEP2/ResponseKeyNT计算
这一步会引入HMAC_MD5哈希,由于后面会多次用到这个哈希,所以写了个函数
from Crypto.Hash import HMAC, MD5
def HMAC_MD5(key,data):
hash = HMAC.new(key,digestmod=MD5)
hash.update(data)
return hash.digest()
计算方式其实很简单
response = ('ADMINISTRATOR'+'pc').encode("utf-16le")
ResponsekeyNT=HMAC_MD5(nt_hash,response)
将全大写的USERNAME和domain拼接到一块再经过utf-16le编码作为data,以nt_hash为key计算HMAC_MD5就得到了ResponseKeyNT
STEP3/NTProofStr计算
blob = bytes.fromhex('010100000000000022a2d32cbc72dc01ff545caf96411c670000000002000a0044004500310041005900010004005000430004001200640065003100610079002e0063006f006d0003001800500043002e00640065003100610079002e0063006f006d0005001200640065003100610079002e0063006f006d000700080022a2d32cbc72dc010600040002000000080030003000000000000000000000000030000026544cc05c735b21ae876ab6adeaf35030fb649315896d1d685326c99ddb5f6b0a001000000000000000000000000000000000000900220048005400540050002f00310030002e00310030002e00310030002e00320030003100000000000000000000000000')
ServerChallenge = bytes.fromhex('cd0a6722277096c9')
NTProofStr = HMAC_MD5(ResponsekeyNT,ServerChallenge+blob)
这里面啊两个东西
ServerChallenge是由服务端生成的随机数
blob(binary_large_object)是一串二进制数据块,其中包含了用户名啊域啊DNS啊一些七七八八的东西
两个合一块就变成了要哈希的data
STEP4/NtChallengeResponse计算
NtChallengeResponse = NTProofStr + blob
不多说了
STEP5/KeyExchangeKey计算
KeyExchangeKey = SessionBaseKey = HMAC_MD5(ResponsekeyNT,NTProofStr)
在NTLM认证中KeyExchangeKey与SessionBaseKey相同,很方便嗷
STEP6/EncryptedRandomKey计算
这个呢就要分两种情况啦
Key Exchange: set(多数情况)
这个情况下呢EncryptedRandomKey是由ExportedSessionKey 经过RC4加密拿到的
from Crypto.Cipher import ARC4
EncryptedRandomSessionKey = ARC4.new(KeyExchangeKey).encrypt(ExportedSessionKey)
Key Exchange: not set
这个字段直接没了捏,嘻嘻
OK啊,上述所有都是我们的认证阶段,接下来才是流量加密,一个很简单的RC4,密钥的派生需要用到STEP6里提到的ExportedSessionKey
CLIENT_SIGN_MAGIC = b"session key to client-to-server signing key magic constant\x00"
SERVER_SIGN_MAGIC = b"session key to server-to-client signing key magic constant\x00"
CLIENT_SEAL_MAGIC = b"session key to client-to-server sealing key magic constant\x00"
SERVER_SEAL_MAGIC = b"session key to server-to-client sealing key magic constant\x00"
ClientSignKey = md5(ExportedSessionKey + CLIENT_SIGN_MAGIC)
ServerSignKey = md5(ExportedSessionKey + SERVER_SIGN_MAGIC)
ClientSealKey = md5(ExportedSessionKey + CLIENT_SEAL_MAGIC)
ServerSealKey = md5(ExportedSessionKey + SERVER_SEAL_MAGIC)
#client->server
ENC = ARC4.new(ClientSealKey).decrypt(payload).encode()
#server->client
ENC = ARC4.new(ServerSealKey).decrypt(payload).encode()
MAGIC是协议固定的常量,要注意的就是 服务端到客户端与 客户端到服务端用的是两个密钥(加解密只需要关注SealKey就行,SignKey是用来校验的,不参与加解密)
好的,现在为止加密流程已经过完了,那么解密思路应该就很清晰了
- 拿到
ExportedSessionKey派生SealKey RC4解密EncryptedRandomSessionKey(可以从流量中读到)获取ExportedSessionKey- 获取
RC4密钥KeyExchangeKey - 获取
password推导KeyExchangeKey
那么怎么获取password呢?用john或者hashcat爆破NTLMv2(USERNAME::domain:challenge:NTroofStr:blob)
于是,你就解密了winrm的流量
嘻嘻,又水一期