diff --git a/rdpy/protocol/rdp/pdu/data.py b/rdpy/protocol/rdp/pdu/data.py index fab5bd6..91ebbab 100644 --- a/rdpy/protocol/rdp/pdu/data.py +++ b/rdpy/protocol/rdp/pdu/data.py @@ -26,50 +26,6 @@ from rdpy.network.type import CompositeType, String, UInt8, UInt16Le, UInt32Le, from rdpy.base.error import InvalidExpectedDataException import rdpy.base.log as log import caps, order - -class InfoFlag(object): - """ - Client capabilities informations - """ - INFO_MOUSE = 0x00000001 - INFO_DISABLECTRLALTDEL = 0x00000002 - INFO_AUTOLOGON = 0x00000008 - INFO_UNICODE = 0x00000010 - INFO_MAXIMIZESHELL = 0x00000020 - INFO_LOGONNOTIFY = 0x00000040 - INFO_COMPRESSION = 0x00000080 - INFO_ENABLEWINDOWSKEY = 0x00000100 - INFO_REMOTECONSOLEAUDIO = 0x00002000 - INFO_FORCE_ENCRYPTED_CS_PDU = 0x00004000 - INFO_RAIL = 0x00008000 - INFO_LOGONERRORS = 0x00010000 - INFO_MOUSE_HAS_WHEEL = 0x00020000 - INFO_PASSWORD_IS_SC_PIN = 0x00040000 - INFO_NOAUDIOPLAYBACK = 0x00080000 - INFO_USING_SAVED_CREDS = 0x00100000 - INFO_AUDIOCAPTURE = 0x00200000 - INFO_VIDEO_DISABLE = 0x00400000 - INFO_CompressionTypeMask = 0x00001E00 - -class PerfFlag(object): - """ - Network performances flag - """ - PERF_DISABLE_WALLPAPER = 0x00000001 - PERF_DISABLE_FULLWINDOWDRAG = 0x00000002 - PERF_DISABLE_MENUANIMATIONS = 0x00000004 - PERF_DISABLE_THEMING = 0x00000008 - PERF_DISABLE_CURSOR_SHADOW = 0x00000020 - PERF_DISABLE_CURSORSETTINGS = 0x00000040 - PERF_ENABLE_FONT_SMOOTHING = 0x00000080 - PERF_ENABLE_DESKTOP_COMPOSITION = 0x00000100 - -class AfInet(object): - """ - IPv4 or IPv6 adress style - """ - AF_INET = 0x00002 - AF_INET6 = 0x0017 class PDUType(object): """ @@ -457,48 +413,7 @@ class ErrorInfo(object): ERRINFO_VCDATATOOLONG : "The size of a received Virtual Channel PDU (section 2.2.6.1) exceeds the chunking size specified in the Virtual Channel Capability Set (section 2.2.7.1.10).", } -class RDPInfo(CompositeType): - """ - Client informations - Contains credentials (very important packet) - @see: http://msdn.microsoft.com/en-us/library/cc240475.aspx - """ - def __init__(self, extendedInfoConditional): - CompositeType.__init__(self) - #code page - self.codePage = UInt32Le() - #support flag - self.flag = UInt32Le(InfoFlag.INFO_MOUSE | InfoFlag.INFO_UNICODE | InfoFlag.INFO_LOGONNOTIFY | InfoFlag.INFO_LOGONERRORS) - self.cbDomain = UInt16Le(lambda:sizeof(self.domain) - 2) - self.cbUserName = UInt16Le(lambda:sizeof(self.userName) - 2) - self.cbPassword = UInt16Le(lambda:sizeof(self.password) - 2) - self.cbAlternateShell = UInt16Le(lambda:sizeof(self.alternateShell) - 2) - self.cbWorkingDir = UInt16Le(lambda:sizeof(self.workingDir) - 2) - #microsoft domain - self.domain = String(readLen = UInt16Le(lambda:self.cbDomain.value + 2), unicode = True) - self.userName = String(readLen = UInt16Le(lambda:self.cbUserName.value + 2), unicode = True) - self.password = String(readLen = UInt16Le(lambda:self.cbPassword.value + 2), unicode = True) - #shell execute at start of session - self.alternateShell = String(readLen = UInt16Le(lambda:self.cbAlternateShell.value + 2), unicode = True) - #working directory for session - self.workingDir = String(readLen = UInt16Le(lambda:self.cbWorkingDir.value + 2), unicode = True) - self.extendedInfo = RDPExtendedInfo(conditional = extendedInfoConditional) - -class RDPExtendedInfo(CompositeType): - """ - Add more client informations - """ - def __init__(self, conditional): - CompositeType.__init__(self, conditional = conditional) - self.clientAddressFamily = UInt16Le(AfInet.AF_INET) - self.cbClientAddress = UInt16Le(lambda:sizeof(self.clientAddress)) - self.clientAddress = String(readLen = self.cbClientAddress, unicode = True) - self.cbClientDir = UInt16Le(lambda:sizeof(self.clientDir)) - self.clientDir = String(readLen = self.cbClientDir, unicode = True) - #TODO make tiomezone - self.clientTimeZone = String("\x00" * 172) - self.clientSessionId = UInt32Le() - self.performanceFlags = UInt32Le() + class ShareControlHeader(CompositeType): """ diff --git a/rdpy/protocol/rdp/pdu/layer.py b/rdpy/protocol/rdp/pdu/layer.py index 8ce6799..0ed39aa 100644 --- a/rdpy/protocol/rdp/pdu/layer.py +++ b/rdpy/protocol/rdp/pdu/layer.py @@ -78,9 +78,6 @@ class PDULayer(LayerAutomata): """ def __init__(self): LayerAutomata.__init__(self, None) - - #logon info send from client to server - self._info = data.RDPInfo(extendedInfoConditional = lambda:(self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_CORE).rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS)) #server capabilities self._serverCapabilities = { caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.GeneralCapability()), @@ -328,13 +325,6 @@ class Client(PDULayer, tpkt.IFastPathListener): if updateDataPDU.updateType.value == data.UpdateType.UPDATETYPE_BITMAP: self._listener.onUpdate(updateDataPDU.updateData.rectangles._array) - def sendInfoPkt(self): - """ - @summary: Send a logon info packet - client automata data - """ - self._transport.send((UInt16Le(data.SecurityFlag.SEC_INFO_PKT), UInt16Le(), self._info)) - def sendLicensePacket(self, licPkt): """ @summary: send license packet diff --git a/rdpy/protocol/rdp/sec.py b/rdpy/protocol/rdp/sec.py index 9de75ec..571d718 100644 --- a/rdpy/protocol/rdp/sec.py +++ b/rdpy/protocol/rdp/sec.py @@ -22,7 +22,7 @@ Some use full methods for security in RDP """ import sha, md5, rsa, gcc, rc4 -from rdpy.network.type import CompositeType, Stream, UInt32Le, String, sizeof +from rdpy.network.type import CompositeType, Stream, UInt32Le, UInt16Le, String, sizeof from rdpy.network.layer import LayerAutomata, IStreamSender class SecurityFlag(object): @@ -46,6 +46,50 @@ class SecurityFlag(object): SEC_AUTODETECT_RSP = 0x2000 SEC_HEARTBEAT = 0x4000 SEC_FLAGSHI_VALID = 0x8000 + +class InfoFlag(object): + """ + Client capabilities informations + """ + INFO_MOUSE = 0x00000001 + INFO_DISABLECTRLALTDEL = 0x00000002 + INFO_AUTOLOGON = 0x00000008 + INFO_UNICODE = 0x00000010 + INFO_MAXIMIZESHELL = 0x00000020 + INFO_LOGONNOTIFY = 0x00000040 + INFO_COMPRESSION = 0x00000080 + INFO_ENABLEWINDOWSKEY = 0x00000100 + INFO_REMOTECONSOLEAUDIO = 0x00002000 + INFO_FORCE_ENCRYPTED_CS_PDU = 0x00004000 + INFO_RAIL = 0x00008000 + INFO_LOGONERRORS = 0x00010000 + INFO_MOUSE_HAS_WHEEL = 0x00020000 + INFO_PASSWORD_IS_SC_PIN = 0x00040000 + INFO_NOAUDIOPLAYBACK = 0x00080000 + INFO_USING_SAVED_CREDS = 0x00100000 + INFO_AUDIOCAPTURE = 0x00200000 + INFO_VIDEO_DISABLE = 0x00400000 + INFO_CompressionTypeMask = 0x00001E00 + +class PerfFlag(object): + """ + Network performances flag + """ + PERF_DISABLE_WALLPAPER = 0x00000001 + PERF_DISABLE_FULLWINDOWDRAG = 0x00000002 + PERF_DISABLE_MENUANIMATIONS = 0x00000004 + PERF_DISABLE_THEMING = 0x00000008 + PERF_DISABLE_CURSOR_SHADOW = 0x00000020 + PERF_DISABLE_CURSORSETTINGS = 0x00000040 + PERF_ENABLE_FONT_SMOOTHING = 0x00000080 + PERF_ENABLE_DESKTOP_COMPOSITION = 0x00000100 + +class AfInet(object): + """ + IPv4 or IPv6 address style + """ + AF_INET = 0x00002 + AF_INET6 = 0x0017 def saltedHash(inputData, salt, salt1, salt2): """ @@ -140,6 +184,11 @@ def bin2bn(b): return l def bn2bin(b): + """ + @summary: convert Big number to binary + @param b: bignumber (long) + @return: binary stream + """ s = bytearray() i = (b.bit_length() + 7) / 8 while i > 0: @@ -147,7 +196,6 @@ def bn2bin(b): i -= 1 return s - class ClientSecurityExchangePDU(CompositeType): """ @summary: contain client random for basic security @@ -157,6 +205,49 @@ class ClientSecurityExchangePDU(CompositeType): CompositeType.__init__(self) self.length = UInt32Le(lambda:(sizeof(self) - 4)) self.encryptedClientRandom = String(readLen = self.length) + +class RDPInfo(CompositeType): + """ + Client informations + Contains credentials (very important packet) + @see: http://msdn.microsoft.com/en-us/library/cc240475.aspx + """ + def __init__(self, extendedInfoConditional): + CompositeType.__init__(self) + #code page + self.codePage = UInt32Le() + #support flag + self.flag = UInt32Le(InfoFlag.INFO_MOUSE | InfoFlag.INFO_UNICODE | InfoFlag.INFO_LOGONNOTIFY | InfoFlag.INFO_LOGONERRORS) + self.cbDomain = UInt16Le(lambda:sizeof(self.domain) - 2) + self.cbUserName = UInt16Le(lambda:sizeof(self.userName) - 2) + self.cbPassword = UInt16Le(lambda:sizeof(self.password) - 2) + self.cbAlternateShell = UInt16Le(lambda:sizeof(self.alternateShell) - 2) + self.cbWorkingDir = UInt16Le(lambda:sizeof(self.workingDir) - 2) + #microsoft domain + self.domain = String(readLen = UInt16Le(lambda:self.cbDomain.value + 2), unicode = True) + self.userName = String(readLen = UInt16Le(lambda:self.cbUserName.value + 2), unicode = True) + self.password = String(readLen = UInt16Le(lambda:self.cbPassword.value + 2), unicode = True) + #shell execute at start of session + self.alternateShell = String(readLen = UInt16Le(lambda:self.cbAlternateShell.value + 2), unicode = True) + #working directory for session + self.workingDir = String(readLen = UInt16Le(lambda:self.cbWorkingDir.value + 2), unicode = True) + self.extendedInfo = RDPExtendedInfo(conditional = extendedInfoConditional) + +class RDPExtendedInfo(CompositeType): + """ + Add more client informations + """ + def __init__(self, conditional): + CompositeType.__init__(self, conditional = conditional) + self.clientAddressFamily = UInt16Le(AfInet.AF_INET) + self.cbClientAddress = UInt16Le(lambda:sizeof(self.clientAddress)) + self.clientAddress = String(readLen = self.cbClientAddress, unicode = True) + self.cbClientDir = UInt16Le(lambda:sizeof(self.clientDir)) + self.clientDir = String(readLen = self.cbClientDir, unicode = True) + #TODO make tiomezone + self.clientTimeZone = String("\x00" * 172) + self.clientSessionId = UInt32Le() + self.performanceFlags = UInt32Le() class SecLayer(LayerAutomata, IStreamSender): """ @@ -165,30 +256,10 @@ class SecLayer(LayerAutomata, IStreamSender): """ def __init__(self, presentation): LayerAutomata.__init__(self, presentation) + self._info = RDPInfo(extendedInfoConditional = lambda:(self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_CORE).rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS)) self._enableEncryption = False - - def connect(self): - """ - @summary: send client random - """ - self._enableEncryption = (self._transport.getGCCClientSettings.getBlock(gcc.MessageType.CS_CORE).serverSelectedProtocol == 0) - if not self._enableEncryption: - self._presentation.connect() - return - - #generate client random - self._clientRandom = rsa.randnum.read_random_bits(128) - self._serverRandom = self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_SECURITY).serverRandom.value - self.generateKeys() - - #send client random encrypted with - certificate = self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_SECURITY).serverCertificate.certData - serverPublicKey = rsa.PublicKey(bin2bn(certificate.PublicKeyBlob.modulus.value), certificate.PublicKeyBlob.pubExp.value) - - message = ClientSecurityExchangePDU() - message.encryptedClientRandom.value = rsa.encrypt(self._clientRandom, serverPublicKey) - self._transport.send(message) - self._presentation.connect() + self._decryt = None + self._encrypt = None def generateKeys(self): """ @@ -197,10 +268,7 @@ class SecLayer(LayerAutomata, IStreamSender): preMasterSecret = self._clientRandom[:24] + self._serverRandom[:24] masterSecret = generateMicrosoftKeyABBCCC(preMasterSecret, self._clientRandom, self._serverRandom) self._sessionKey = generateMicrosoftKeyXYYZZZ(masterSecret, self._clientRandom, self._serverRandom) - self._macKey128 = self._sessionKey[:16] - self._decrypt = finalHash(self._sessionKey[16:32], self._clientRandom, self._serverRandom) - self._encrypt = finalHash(self._sessionKey[32:48], self._clientRandom, self._serverRandom) def recv(self, data): if not self._enableEncryption: @@ -211,6 +279,34 @@ class SecLayer(LayerAutomata, IStreamSender): if not self._enableEncryption: self._presentation.recv(data) return + + def sendFlag(self, flag, data): + if self._enableEncryption: + flag |= SecurityFlag.SEC_ENCRYPT + self._transport.send((UInt16Le(flag), UInt16Le(), data)) - def sendInfoPkt(self, data): - self._transport.send() \ No newline at end of file +class SecClient(SecLayer): + def connect(self): + """ + @summary: send client random + """ + if self._transport.getGCCClientSettings.getBlock(gcc.MessageType.CS_CORE).serverSelectedProtocol == 0: + #generate client random + self._clientRandom = rsa.randnum.read_random_bits(128) + self._serverRandom = self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_SECURITY).serverRandom.value + self.generateKeys() + + self._decrypt = finalHash(self._sessionKey[16:32], self._clientRandom, self._serverRandom) + self._encrypt = finalHash(self._sessionKey[32:48], self._clientRandom, self._serverRandom) + + #send client random encrypted with + certificate = self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_SECURITY).serverCertificate.certData + serverPublicKey = rsa.PublicKey(bin2bn(certificate.PublicKeyBlob.modulus.value), certificate.PublicKeyBlob.pubExp.value) + + message = ClientSecurityExchangePDU() + message.encryptedClientRandom.value = rsa.encrypt(self._clientRandom, serverPublicKey) + self.send(SecurityFlag.SEC_EXCHANGE_PKT, message) + self._enableEncryption = True + + self.send(SecurityFlag.SEC_INFO_PKT, self._info) + self._presentation.connect() \ No newline at end of file