not handle correctly the ntlmv2 auth message
This commit is contained in:
@@ -22,7 +22,10 @@
|
|||||||
@see: https://msdn.microsoft.com/en-us/library/cc236621.aspx
|
@see: https://msdn.microsoft.com/en-us/library/cc236621.aspx
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from rdpy.core.type import CompositeType, CallableValue, String, UInt8, UInt16Le, UInt24Le, UInt32Le
|
import hashlib, hmac
|
||||||
|
import rdpy.security.pyDes as pyDes
|
||||||
|
from rdpy.security.rsa_wrapper import random
|
||||||
|
from rdpy.core.type import CompositeType, CallableValue, String, UInt8, UInt16Le, UInt24Le, UInt32Le, sizeof
|
||||||
|
|
||||||
class MajorVersion(object):
|
class MajorVersion(object):
|
||||||
"""
|
"""
|
||||||
@@ -99,12 +102,12 @@ class NegotiateMessage(CompositeType):
|
|||||||
|
|
||||||
self.NegotiateFlags = UInt32Le(Negotiate.NTLMSSP_NEGOTIATE_KEY_EXCH |
|
self.NegotiateFlags = UInt32Le(Negotiate.NTLMSSP_NEGOTIATE_KEY_EXCH |
|
||||||
Negotiate.NTLMSSP_NEGOTIATE_128 |
|
Negotiate.NTLMSSP_NEGOTIATE_128 |
|
||||||
|
Negotiate.NTLMSSP_NEGOTIATE_56 |
|
||||||
Negotiate.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY |
|
Negotiate.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY |
|
||||||
Negotiate.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
|
Negotiate.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
|
||||||
Negotiate.NTLMSSP_NEGOTIATE_NTLM |
|
Negotiate.NTLMSSP_NEGOTIATE_NTLM |
|
||||||
Negotiate.NTLMSSP_NEGOTIATE_SIGN |
|
|
||||||
Negotiate.NTLMSSP_NEGOTIATE_SEAL |
|
|
||||||
Negotiate.NTLMSSP_REQUEST_TARGET |
|
Negotiate.NTLMSSP_REQUEST_TARGET |
|
||||||
|
Negotiate.NTLMSSP_NEGOTIATE_TARGET_INFO |
|
||||||
Negotiate.NTLMSSP_NEGOTIATE_VERSION |
|
Negotiate.NTLMSSP_NEGOTIATE_VERSION |
|
||||||
Negotiate.NTLMSSP_NEGOTIATE_UNICODE)
|
Negotiate.NTLMSSP_NEGOTIATE_UNICODE)
|
||||||
|
|
||||||
@@ -116,7 +119,7 @@ class NegotiateMessage(CompositeType):
|
|||||||
self.WorkstationMaxLen = UInt16Le(lambda:self.WorkstationLen.value)
|
self.WorkstationMaxLen = UInt16Le(lambda:self.WorkstationLen.value)
|
||||||
self.WorkstationBufferOffset = UInt32Le()
|
self.WorkstationBufferOffset = UInt32Le()
|
||||||
|
|
||||||
self.Version = Version(conditional = lambda:(self.NegotiateFlags & Negotiate.NTLMSSP_NEGOTIATE_VERSION))
|
self.Version = Version(conditional = lambda:(self.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_VERSION))
|
||||||
|
|
||||||
self.Payload = String()
|
self.Payload = String()
|
||||||
|
|
||||||
@@ -143,5 +146,250 @@ class ChallengeMessage(CompositeType):
|
|||||||
self.TargetInfoMaxLen = UInt16Le(lambda:self.TargetInfoLen.value)
|
self.TargetInfoMaxLen = UInt16Le(lambda:self.TargetInfoLen.value)
|
||||||
self.TargetInfoBufferOffset = UInt32Le()
|
self.TargetInfoBufferOffset = UInt32Le()
|
||||||
|
|
||||||
self.Version = Version(conditional = lambda:(self.NegotiateFlags & Negotiate.NTLMSSP_NEGOTIATE_VERSION))
|
self.Version = Version(conditional = lambda:(self.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_VERSION))
|
||||||
self.Payload = String()
|
self.Payload = String()
|
||||||
|
|
||||||
|
def getTargetName(self):
|
||||||
|
if self.TargetNameLen.value == 0:
|
||||||
|
return None
|
||||||
|
offset = sizeof(self) - sizeof(self.Payload)
|
||||||
|
start = self.TargetNameBufferOffset.value - offset
|
||||||
|
end = start + self.TargetNameLen.value
|
||||||
|
return self.Payload.value[start:end]
|
||||||
|
|
||||||
|
def getTargetInfo(self):
|
||||||
|
if self.TargetInfoLen.value == 0:
|
||||||
|
return None
|
||||||
|
offset = sizeof(self) - sizeof(self.Payload)
|
||||||
|
start = self.TargetInfoBufferOffset.value - offset
|
||||||
|
end = start + self.TargetInfoLen.value - 4
|
||||||
|
return self.Payload.value[start:end]
|
||||||
|
|
||||||
|
class AuthenticateMessage(CompositeType):
|
||||||
|
"""
|
||||||
|
@summary: Last message in ntlm authentication
|
||||||
|
@see: https://msdn.microsoft.com/en-us/library/cc236643.aspx
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
CompositeType.__init__(self)
|
||||||
|
self.Signature = String("NTLMSSP\x00", readLen = CallableValue(8), constant = True)
|
||||||
|
self.MessageType = UInt32Le(0x00000003, constant = True)
|
||||||
|
|
||||||
|
self.LmChallengeResponseLen = UInt16Le()
|
||||||
|
self.LmChallengeResponseMaxLen = UInt16Le(lambda:self.LmChallengeResponseLen.value)
|
||||||
|
self.LmChallengeResponseBufferOffset = UInt32Le()
|
||||||
|
|
||||||
|
self.NtChallengeResponseLen = UInt16Le()
|
||||||
|
self.NtChallengeResponseMaxLen = UInt16Le(lambda:self.NtChallengeResponseLen.value)
|
||||||
|
self.NtChallengeResponseBufferOffset = UInt32Le()
|
||||||
|
|
||||||
|
self.DomainNameLen = UInt16Le()
|
||||||
|
self.DomainNameMaxLen = UInt16Le(lambda:self.DomainNameLen.value)
|
||||||
|
self.DomainNameBufferOffset = UInt32Le()
|
||||||
|
|
||||||
|
self.UserNameLen = UInt16Le()
|
||||||
|
self.UserNameMaxLen = UInt16Le(lambda:self.UserNameLen.value)
|
||||||
|
self.UserNameBufferOffset = UInt32Le()
|
||||||
|
|
||||||
|
self.WorkstationLen = UInt16Le()
|
||||||
|
self.WorkstationMaxLen = UInt16Le(lambda:self.WorkstationLen.value)
|
||||||
|
self.WorkstationBufferOffset = UInt32Le()
|
||||||
|
|
||||||
|
self.EncryptedRandomSessionLen = UInt16Le()
|
||||||
|
self.EncryptedRandomSessionMaxLen = UInt16Le(lambda:self.EncryptedRandomSessionLen.value)
|
||||||
|
self.EncryptedRandomSessionBufferOffset = UInt32Le()
|
||||||
|
|
||||||
|
self.NegotiateFlags = UInt32Le()
|
||||||
|
self.Version = Version(conditional = lambda:(self.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_VERSION))
|
||||||
|
|
||||||
|
#self.MIC = String("\x00" * 16, readLen = CallableValue(16))
|
||||||
|
self.Payload = String()
|
||||||
|
|
||||||
|
def getUserName(self):
|
||||||
|
if self.UserNameLen.value == 0:
|
||||||
|
return None
|
||||||
|
offset = sizeof(self) - sizeof(self.Payload)
|
||||||
|
start = self.UserNameBufferOffset.value - offset
|
||||||
|
end = start + self.UserNameLen.value
|
||||||
|
return self.Payload.value[start:end]
|
||||||
|
|
||||||
|
def getDomainName(self):
|
||||||
|
if self.DomainNameLen.value == 0:
|
||||||
|
return None
|
||||||
|
offset = sizeof(self) - sizeof(self.Payload)
|
||||||
|
start = self.DomainNameBufferOffset.value - offset
|
||||||
|
end = start + self.DomainNameLen.value
|
||||||
|
return self.Payload.value[start:end]
|
||||||
|
|
||||||
|
def expandDesKey(key):
|
||||||
|
"""
|
||||||
|
@summary: Expand the key from a 7-byte password key into a 8-byte DES key
|
||||||
|
"""
|
||||||
|
s = chr(((ord(key[0]) >> 1) & 0x7f) << 1)
|
||||||
|
s = s + chr(((ord(key[0]) & 0x01) << 6 | ((ord(key[1]) >> 2) & 0x3f)) << 1)
|
||||||
|
s = s + chr(((ord(key[1]) & 0x03) << 5 | ((ord(key[2]) >> 3) & 0x1f)) << 1)
|
||||||
|
s = s + chr(((ord(key[2]) & 0x07) << 4 | ((ord(key[3]) >> 4) & 0x0f)) << 1)
|
||||||
|
s = s + chr(((ord(key[3]) & 0x0f) << 3 | ((ord(key[4]) >> 5) & 0x07)) << 1)
|
||||||
|
s = s + chr(((ord(key[4]) & 0x1f) << 2 | ((ord(key[5]) >> 6) & 0x03)) << 1)
|
||||||
|
s = s + chr(((ord(key[5]) & 0x3f) << 1 | ((ord(key[6]) >> 7) & 0x01)) << 1)
|
||||||
|
s = s + chr((ord(key[6]) & 0x7f) << 1)
|
||||||
|
return s
|
||||||
|
|
||||||
|
def DES(key, data):
|
||||||
|
"""
|
||||||
|
@summary: DES use in microsoft specification
|
||||||
|
@param key: {str} Des key on 56 bits or 7 bytes
|
||||||
|
@param data: {str} data to encrypt
|
||||||
|
"""
|
||||||
|
return pyDes.des(expandDesKey(key)).encrypt(data)
|
||||||
|
|
||||||
|
def DESL(key, data):
|
||||||
|
"""
|
||||||
|
@summary: an krosoft security function (triple des = des + des + des ;-))
|
||||||
|
@param key: {str} Des key
|
||||||
|
@param data: {str} encrypted data
|
||||||
|
"""
|
||||||
|
return DES(key[0:7], data) + DES(key[7:14], data) + DES(key[14:16] + "\x00" * 5, data)
|
||||||
|
|
||||||
|
def UNICODE(s):
|
||||||
|
"""
|
||||||
|
@param s: source
|
||||||
|
@return: {str} encoded in unicode
|
||||||
|
"""
|
||||||
|
return s.encode('utf-16le')
|
||||||
|
|
||||||
|
def MD4(s):
|
||||||
|
"""
|
||||||
|
@summary: compute the md4 sum
|
||||||
|
@param s: {str} input data
|
||||||
|
@return: {str} MD4(s)
|
||||||
|
"""
|
||||||
|
return hashlib.new('md4', s).digest()
|
||||||
|
|
||||||
|
def MD5(s):
|
||||||
|
"""
|
||||||
|
@summary: compute the md5 sum
|
||||||
|
@param s: {str} input data
|
||||||
|
@return: {str} MD5(s)
|
||||||
|
"""
|
||||||
|
return hashlib.new('md5', s).digest()
|
||||||
|
|
||||||
|
def Z(m):
|
||||||
|
"""
|
||||||
|
@summary: fill m zero in string
|
||||||
|
@param m: {int} size of string
|
||||||
|
@return: \x00 * m
|
||||||
|
"""
|
||||||
|
return "\x00" * m
|
||||||
|
|
||||||
|
def NTOWFv1(Passwd, User, UserDom):
|
||||||
|
"""
|
||||||
|
@see: https://msdn.microsoft.com/en-us/library/cc236699.aspx
|
||||||
|
"""
|
||||||
|
return MD4(UNICODE(Passwd))
|
||||||
|
|
||||||
|
def LMOWFv1(Passwd, User, UserDom):
|
||||||
|
"""
|
||||||
|
@see: https://msdn.microsoft.com/en-us/library/cc236699.aspx
|
||||||
|
"""
|
||||||
|
password = (Passwd.upper() + "\x00" * 14)[:14]
|
||||||
|
return DES(password[:7], "KGS!@#$%") + DES(password[7:], "KGS!@#$%")
|
||||||
|
|
||||||
|
def NTLMv1Anthentication(negFlag, domain, user, password, serverChallenge, serverName):
|
||||||
|
"""
|
||||||
|
@summary: Not tested yet
|
||||||
|
@param negFlag: {int} use another non secure way
|
||||||
|
@param domain: {str} microsoft domain
|
||||||
|
@param user: {str} username
|
||||||
|
@param password: {str} password
|
||||||
|
@param serverChallenge: {str[8]} server challenge
|
||||||
|
"""
|
||||||
|
ResponseKeyNT = NTOWFv1(password, user, domain)
|
||||||
|
ResponseKeyLM = LMOWFv1(password, user, domain)
|
||||||
|
|
||||||
|
if negFlag & Negotiate.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
|
||||||
|
ClientChallenge = random(64)
|
||||||
|
NtChallengeResponse = DESL(ResponseKeyNT, MD5(serverChallenge + ClientChallenge))
|
||||||
|
LmChallengeResponse = ClientChallenge + Z(16)
|
||||||
|
else:
|
||||||
|
NtChallengeResponse = DESL(ResponseKeyNT, serverChallenge)
|
||||||
|
LmChallengeResponse = DESL(ResponseKeyLM, serverChallenge)
|
||||||
|
|
||||||
|
SessionBaseKey = MD4(ResponseKeyNT)
|
||||||
|
|
||||||
|
return NtChallengeResponse, LmChallengeResponse, SessionBaseKey
|
||||||
|
|
||||||
|
def HMAC_MD5(key, data):
|
||||||
|
return hmac.new(key, data, hashlib.md5).digest()
|
||||||
|
|
||||||
|
def NTOWFv2(Passwd, User, UserDom):
|
||||||
|
return HMAC_MD5(MD4(UNICODE(Passwd)), UNICODE(User.upper() + UserDom))
|
||||||
|
|
||||||
|
def LMOWFv2(Passwd, User, UserDom):
|
||||||
|
return NTOWFv2(Passwd, User, UserDom)
|
||||||
|
|
||||||
|
def NTLMv2Authenticate(negFlag, domain, user, password, serverChallenge, serverName):
|
||||||
|
"""
|
||||||
|
@summary: process NTLMv2 Authenticate hash
|
||||||
|
@see: https://msdn.microsoft.com/en-us/library/cc236700.aspx
|
||||||
|
"""
|
||||||
|
ResponseKeyNT = NTOWFv2(password, user, domain)
|
||||||
|
ResponseKeyLM = LMOWFv2(password, user, domain)
|
||||||
|
|
||||||
|
Responserversion = "\0x01"
|
||||||
|
HiResponserversion = "0x01"
|
||||||
|
Time = "\x00" * 8
|
||||||
|
ClientChallenge = random(64)
|
||||||
|
|
||||||
|
temp = Responserversion + HiResponserversion + Z(6) + Time + ClientChallenge + Z(4) + serverName + Z(4)
|
||||||
|
NTProofStr = HMAC_MD5(ResponseKeyNT, serverChallenge + temp)
|
||||||
|
NtChallengeResponse = NTProofStr + temp
|
||||||
|
LmChallengeResponse = HMAC_MD5(ResponseKeyLM, serverChallenge + ClientChallenge) + ClientChallenge
|
||||||
|
|
||||||
|
SessionBaseKey = HMAC_MD5(ResponseKeyNT, NTProofStr)
|
||||||
|
|
||||||
|
return NtChallengeResponse, LmChallengeResponse, SessionBaseKey
|
||||||
|
|
||||||
|
def createAuthenticationMessage(method, challengeResponse, domain, user, password):
|
||||||
|
#extract target info
|
||||||
|
NtChallengeResponse, LmChallengeResponse, SessionBaseKey = method(challengeResponse.NegotiateFlags.value,
|
||||||
|
domain, user, password,
|
||||||
|
challengeResponse.ServerChallenge.value,
|
||||||
|
challengeResponse.getTargetInfo())
|
||||||
|
|
||||||
|
if challengeResponse.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_UNICODE:
|
||||||
|
domain, user = UNICODE(domain), UNICODE(user)
|
||||||
|
|
||||||
|
message = AuthenticateMessage()
|
||||||
|
#fill message
|
||||||
|
offset = sizeof(message)
|
||||||
|
|
||||||
|
message.NegotiateFlags.value = challengeResponse.NegotiateFlags.value
|
||||||
|
message.NegotiateFlags.value &= ~Negotiate.NTLMSSP_NEGOTIATE_VERSION
|
||||||
|
|
||||||
|
message.LmChallengeResponseLen.value = len(LmChallengeResponse)
|
||||||
|
message.LmChallengeResponseBufferOffset.value = offset
|
||||||
|
message.Payload.value += LmChallengeResponse
|
||||||
|
offset += len(LmChallengeResponse)
|
||||||
|
|
||||||
|
message.NtChallengeResponseLen.value = len(NtChallengeResponse)
|
||||||
|
message.NtChallengeResponseBufferOffset.value = offset
|
||||||
|
message.Payload.value += NtChallengeResponse
|
||||||
|
offset += len(NtChallengeResponse)
|
||||||
|
|
||||||
|
message.DomainNameLen.value = len(domain)
|
||||||
|
message.DomainNameBufferOffset.value = offset
|
||||||
|
message.Payload.value += domain
|
||||||
|
offset += len(domain)
|
||||||
|
|
||||||
|
message.UserNameLen.value = len(user)
|
||||||
|
message.UserNameBufferOffset.value = offset
|
||||||
|
message.Payload.value += user
|
||||||
|
offset += len(user)
|
||||||
|
|
||||||
|
message.EncryptedRandomSessionLen.value = len(SessionBaseKey)
|
||||||
|
message.EncryptedRandomSessionBufferOffset.value = offset
|
||||||
|
message.Payload.value += SessionBaseKey
|
||||||
|
offset += len(SessionBaseKey)
|
||||||
|
|
||||||
|
return message
|
||||||
@@ -235,4 +235,5 @@ class TPKT(RawLayer, IFastPathSender):
|
|||||||
s = cssp.decodeDERTRequest(data)
|
s = cssp.decodeDERTRequest(data)
|
||||||
challenge = ntlm.ChallengeMessage()
|
challenge = ntlm.ChallengeMessage()
|
||||||
s[0].readType(challenge)
|
s[0].readType(challenge)
|
||||||
print challenge.ServerChallenge.value
|
RawLayer.send(self, cssp.encodeDERTRequest( [ ntlm.createAuthenticationMessage(ntlm.NTLMv2Authenticate, challenge, "toto", "toto", "toto") ] ))
|
||||||
|
self.restartAutomata()
|
||||||
Reference in New Issue
Block a user