not handle correctly the ntlmv2 auth message

This commit is contained in:
speyrefitte
2015-03-02 18:37:18 +01:00
parent 1e2f284e97
commit a7058f1c54
2 changed files with 256 additions and 7 deletions

View File

@@ -22,7 +22,10 @@
@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):
"""
@@ -99,12 +102,12 @@ class NegotiateMessage(CompositeType):
self.NegotiateFlags = UInt32Le(Negotiate.NTLMSSP_NEGOTIATE_KEY_EXCH |
Negotiate.NTLMSSP_NEGOTIATE_128 |
Negotiate.NTLMSSP_NEGOTIATE_56 |
Negotiate.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY |
Negotiate.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
Negotiate.NTLMSSP_NEGOTIATE_NTLM |
Negotiate.NTLMSSP_NEGOTIATE_SIGN |
Negotiate.NTLMSSP_NEGOTIATE_SEAL |
Negotiate.NTLMSSP_REQUEST_TARGET |
Negotiate.NTLMSSP_NEGOTIATE_TARGET_INFO |
Negotiate.NTLMSSP_NEGOTIATE_VERSION |
Negotiate.NTLMSSP_NEGOTIATE_UNICODE)
@@ -116,7 +119,7 @@ class NegotiateMessage(CompositeType):
self.WorkstationMaxLen = UInt16Le(lambda:self.WorkstationLen.value)
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()
@@ -143,5 +146,250 @@ class ChallengeMessage(CompositeType):
self.TargetInfoMaxLen = UInt16Le(lambda:self.TargetInfoLen.value)
self.TargetInfoBufferOffset = UInt32Le()
self.Version = Version(conditional = lambda:(self.NegotiateFlags & Negotiate.NTLMSSP_NEGOTIATE_VERSION))
self.Payload = String()
self.Version = Version(conditional = lambda:(self.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_VERSION))
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

View File

@@ -235,4 +235,5 @@ class TPKT(RawLayer, IFastPathSender):
s = cssp.decodeDERTRequest(data)
challenge = ntlm.ChallengeMessage()
s[0].readType(challenge)
print challenge.ServerChallenge.value
RawLayer.send(self, cssp.encodeDERTRequest( [ ntlm.createAuthenticationMessage(ntlm.NTLMv2Authenticate, challenge, "toto", "toto", "toto") ] ))
self.restartAutomata()