From f4808d0ae262cfe609d6f2fea47db38294e9fbf6 Mon Sep 17 00:00:00 2001 From: speyrefitte Date: Wed, 24 Dec 2014 17:20:09 +0100 Subject: [PATCH] finish RDP basic security layer for server side but have bug on faspath + basic security layer... --- rdpy/protocol/rdp/gcc.py | 51 ++++++++++++++++++++++++++++++++-- rdpy/protocol/rdp/mcs.py | 2 +- rdpy/protocol/rdp/pdu/caps.py | 17 ++++++++++-- rdpy/protocol/rdp/pdu/layer.py | 10 +++---- rdpy/protocol/rdp/sec.py | 45 +++++------------------------- rdpy/protocol/rdp/x224.py | 4 +-- rdpy/security/rsa_wrapper.py | 16 ++++++++--- 7 files changed, 90 insertions(+), 55 deletions(-) diff --git a/rdpy/protocol/rdp/gcc.py b/rdpy/protocol/rdp/gcc.py index 328a28c..0ece6c8 100644 --- a/rdpy/protocol/rdp/gcc.py +++ b/rdpy/protocol/rdp/gcc.py @@ -22,6 +22,7 @@ Implement GCC structure use in RDP protocol http://msdn.microsoft.com/en-us/library/cc240508.aspx """ +import md5 from rdpy.core.type import UInt8, UInt16Le, UInt32Le, CompositeType, String, Stream, sizeof, FactoryType, ArrayType import per, mcs from rdpy.core.error import InvalidExpectedDataException @@ -314,7 +315,7 @@ class ServerCertificate(CompositeType): """ def __init__(self, certData = None, readLen = None, conditional = lambda:True): CompositeType.__init__(self, readLen = readLen, conditional = conditional) - self.dwVersion = UInt32Le(lambda:self.certData.__class__._TYPE_) + self.dwVersion = UInt32Le(lambda:(self.certData.__class__._TYPE_)) def CertificateFactory(): """ @@ -330,7 +331,7 @@ class ServerCertificate(CompositeType): elif not "_TYPE_" in certData.__class__.__dict__: raise InvalidExpectedDataException("Try to send an invalid Certificate") - self.certData = FactoryType(CertificateFactory) + self.certData = certData class ProprietaryServerCertificate(CompositeType): """ @@ -339,6 +340,11 @@ class ProprietaryServerCertificate(CompositeType): """ _TYPE_ = CertificateType.CERT_CHAIN_VERSION_1 + #http://msdn.microsoft.com/en-us/library/cc240776.aspx + _TERMINAL_SERVICES_MODULUS_ = "\x3d\x3a\x5e\xbd\x72\x43\x3e\xc9\x4d\xbb\xc1\x1e\x4a\xba\x5f\xcb\x3e\x88\x20\x87\xef\xf5\xc1\xe2\xd7\xb7\x6b\x9a\xf2\x52\x45\x95\xce\x63\x65\x6b\x58\x3a\xfe\xef\x7c\xe7\xbf\xfe\x3d\xf6\x5c\x7d\x6c\x5e\x06\x09\x1a\xf5\x61\xbb\x20\x93\x09\x5f\x05\x6d\xea\x87" + _TERMINAL_SERVICES_PRIVATE_EXPONENT_ = "\x87\xa7\x19\x32\xda\x11\x87\x55\x58\x00\x16\x16\x25\x65\x68\xf8\x24\x3e\xe6\xfa\xe9\x67\x49\x94\xcf\x92\xcc\x33\x99\xe8\x08\x60\x17\x9a\x12\x9f\x24\xdd\xb1\x24\x99\xc7\x3a\xb8\x0a\x7b\x0d\xdd\x35\x07\x79\x17\x0b\x51\x9b\xb3\xc7\x10\x01\x13\xe7\x3f\xf3\x5f" + _TERMINAL_SERVICES_PUBLIC_EXPONENT_ = "\x5b\x7b\x88\xc0" + def __init__(self): CompositeType.__init__(self) self.dwSigAlgId = UInt32Le(0x00000001, constant = True) @@ -347,8 +353,9 @@ class ProprietaryServerCertificate(CompositeType): self.wPublicKeyBlobLen = UInt16Le(lambda:sizeof(self.PublicKeyBlob)) self.PublicKeyBlob = RSAPublicKey(readLen = self.wPublicKeyBlobLen) self.wSignatureBlobType = UInt16Le(0x0008, constant = True) - self.wSignatureBlobLen = UInt16Le(lambda:sizeof(self.SignatureBlob)) + self.wSignatureBlobLen = UInt16Le(lambda:(sizeof(self.SignatureBlob) - 8)) self.SignatureBlob = String(readLen = self.wSignatureBlobLen) + self.padding = String("\x00" * 8, readLen = UInt8(8)) def getPublicKey(self): """ @@ -357,6 +364,37 @@ class ProprietaryServerCertificate(CompositeType): log.debug("read RSA public key from proprietary certificate") #reverse because bignum in little endian return rsa.PublicKey(self.PublicKeyBlob.pubExp.value, self.PublicKeyBlob.modulus.value[::-1]) + + def computeSignatureHash(self): + """ + @summary: compute hash + """ + s = Stream() + s.writeType(UInt32Le(self.__class__._TYPE_)) + s.writeType(self.dwSigAlgId) + s.writeType(self.dwKeyAlgId) + s.writeType(self.wPublicKeyBlobType) + s.writeType(self.wPublicKeyBlobLen) + s.writeType(self.PublicKeyBlob) + + md5Digest = md5.new() + md5Digest.update(s.getvalue()) + + return md5Digest.digest() + "\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01" + + def sign(self): + """ + @summary: sign proprietary certificate + @see: http://msdn.microsoft.com/en-us/library/cc240778.aspx + """ + self.SignatureBlob.value = rsa.sign(self.computeSignatureHash()[::-1], rsa.PrivateKey(d = ProprietaryServerCertificate._TERMINAL_SERVICES_PRIVATE_EXPONENT_[::-1], n = ProprietaryServerCertificate._TERMINAL_SERVICES_MODULUS_[::-1]))[::-1] + + def verify(self): + """ + @summary: verify certificate signature + """ + return rsa.verify(self.SignatureBlob.value[::-1], rsa.PublicKey(e = ProprietaryServerCertificate._TERMINAL_SERVICES_PUBLIC_EXPONENT_[::-1], n = ProprietaryServerCertificate._TERMINAL_SERVICES_MODULUS_[::-1]))[::-1] == self.computeSignatureHash() + class CertBlob(CompositeType): """ @@ -389,6 +427,12 @@ class X509CertificateChain(CompositeType): #last certifcate contain public key n, e = x509.extractRSAKey(x509.load(self.CertBlobArray[-1].abCert.value)) return rsa.PublicKey(e, n) + + def verify(self): + """ + @todo: verify x509 signature + """ + return True class RSAPublicKey(CompositeType): """ @@ -396,6 +440,7 @@ class RSAPublicKey(CompositeType): """ def __init__(self, readLen): CompositeType.__init__(self, readLen = readLen) + #magic is RSA1(0x31415352) self.magic = UInt32Le(0x31415352, constant = True) self.keylen = UInt32Le(lambda:(sizeof(self.modulus) + sizeof(self.padding))) self.bitlen = UInt32Le(lambda:((self.keylen.value - 8) * 8)) diff --git a/rdpy/protocol/rdp/mcs.py b/rdpy/protocol/rdp/mcs.py index eaefde0..6e38e46 100644 --- a/rdpy/protocol/rdp/mcs.py +++ b/rdpy/protocol/rdp/mcs.py @@ -492,7 +492,7 @@ class Server(MCSLayer): self._serverSettings.SC_SECURITY.encryptionMethod.value = gcc.EncryptionMethod.ENCRYPTION_FLAG_128BIT self._serverSettings.SC_SECURITY.encryptionLevel.value = gcc.EncryptionLevel.ENCRYPTION_LEVEL_HIGH self._serverSettings.SC_SECURITY.serverRandom.value = rsa.random(256) - self._serverSettings.SC_SECURITY.serverCertificate.certData = self._presentation.getCertificate() + self._serverSettings.SC_SECURITY.serverCertificate = self._presentation.getCertificate() self._serverSettings.SC_CORE.clientRequestedProtocol.value = self._transport._requestedProtocol self.setNextState(self.recvConnectInitial) diff --git a/rdpy/protocol/rdp/pdu/caps.py b/rdpy/protocol/rdp/pdu/caps.py index 35bffd6..044d5e6 100644 --- a/rdpy/protocol/rdp/pdu/caps.py +++ b/rdpy/protocol/rdp/pdu/caps.py @@ -235,7 +235,7 @@ class Capability(CompositeType): """ Closure for capability factory """ - for c in [GeneralCapability, BitmapCapability, OrderCapability, BitmapCacheCapability, PointerCapability, InputCapability, BrushCapability, GlyphCapability, OffscreenBitmapCacheCapability, VirtualChannelCapability, SoundCapability, ControlCapability, WindowActivationCapability, FontCapability, ColorCacheCapability, ShareCapability]: + for c in [GeneralCapability, BitmapCapability, OrderCapability, BitmapCacheCapability, PointerCapability, InputCapability, BrushCapability, GlyphCapability, OffscreenBitmapCacheCapability, VirtualChannelCapability, SoundCapability, ControlCapability, WindowActivationCapability, FontCapability, ColorCacheCapability, ShareCapability, MultiFragmentUpdate]: if self.capabilitySetType.value == c._TYPE_ and (self.lengthCapability.value - 4) > 0: return c(readLen = self.lengthCapability - 4) log.debug("unknown Capability type : %s"%hex(self.capabilitySetType.value)) @@ -526,4 +526,17 @@ class ShareCapability(CompositeType): def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.nodeId = UInt16Le() - self.pad2octets = UInt16Le() \ No newline at end of file + self.pad2octets = UInt16Le() + +class MultiFragmentUpdate(CompositeType): + """ + @summary: Use to advertise fast path max buffer to use + client -> server + server -> client + @see: http://msdn.microsoft.com/en-us/library/cc240649.aspx + """ + _TYPE_ = CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE + + def __init__(self, readLen = None): + CompositeType.__init__(self, readLen = readLen) + self.MaxRequestSize = UInt32Le(0xffffffff) \ No newline at end of file diff --git a/rdpy/protocol/rdp/pdu/layer.py b/rdpy/protocol/rdp/pdu/layer.py index 6f70d4f..25033a6 100644 --- a/rdpy/protocol/rdp/pdu/layer.py +++ b/rdpy/protocol/rdp/pdu/layer.py @@ -100,7 +100,8 @@ class PDULayer(LayerAutomata, tpkt.IFastPathListener): caps.CapsType.CAPSTYPE_GLYPHCACHE : caps.Capability(caps.GlyphCapability()), caps.CapsType.CAPSTYPE_OFFSCREENCACHE : caps.Capability(caps.OffscreenBitmapCacheCapability()), caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.VirtualChannelCapability()), - caps.CapsType.CAPSTYPE_SOUND : caps.Capability(caps.SoundCapability()) + caps.CapsType.CAPSTYPE_SOUND : caps.Capability(caps.SoundCapability()), + caps.CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE : caps.Capability(caps.MultiFragmentUpdate()) } #share id between client and server self._shareId = 0x103EA @@ -410,8 +411,8 @@ class Server(PDULayer): self._clientCapabilities[cap.capabilitySetType] = cap #find use full flag - self._clientFastPathSupported = self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability.extraFlags.value & caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED - + self._clientFastPathSupported = bool(self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability.extraFlags.value & (caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED | caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED)) + self.setNextState(self.recvClientSynchronizePDU) def recvClientSynchronizePDU(self, s): @@ -524,7 +525,7 @@ class Server(PDULayer): generalCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT - generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR + generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED inputCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_INPUT].capability inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX @@ -582,7 +583,6 @@ class Server(PDULayer): fastPathUpdateDataPDU = data.FastPathBitmapUpdateDataPDU() fastPathUpdateDataPDU.rectangles._array = bitmapDatas self._fastPathSender.sendFastPath(0, data.FastPathUpdatePDU(fastPathUpdateDataPDU)) - else: #slow path case updateDataPDU = data.BitmapUpdateDataPDU() diff --git a/rdpy/protocol/rdp/sec.py b/rdpy/protocol/rdp/sec.py index 27fe183..0d663d4 100644 --- a/rdpy/protocol/rdp/sec.py +++ b/rdpy/protocol/rdp/sec.py @@ -95,40 +95,6 @@ class AfInet(object): """ AF_INET = 0x00002 AF_INET6 = 0x0017 - -def terminalServicesSign(certificate): - """ - @summary: sign proprietary certificate - @param certificate: {gcc.ProprietaryServerCertificate} - @see: http://msdn.microsoft.com/en-us/library/cc240778.aspx - """ - modulus = "\x3d\x3a\x5e\xbd\x72\x43\x3e\xc9\x4d\xbb\xc1\x1e\x4a\xba\x5f\xcb\x3e\x88\x20\x87\xef\xf5\xc1\xe2\xd7\xb7\x6b\x9a\xf2\x52\x45\x95\xce\x63\x65\x6b\x58\x3a\xfe\xef\x7c\xe7\xbf\xfe\x3d\xf6\x5c\x7d\x6c\x5e\x06\x09\x1a\xf5\x61\xbb\x20\x93\x09\x5f\x05\x6d\xea\x87" - privateExponent = "\x87\xa7\x19\x32\xda\x11\x87\x55\x58\x00\x16\x16\x25\x65\x68\xf8\x24\x3e\xe6\xfa\xe9\x67\x49\x94\xcf\x92\xcc\x33\x99\xe8\x08\x60\x17\x9a\x12\x9f\x24\xdd\xb1\x24\x99\xc7\x3a\xb8\x0a\x7b\x0d\xdd\x35\x07\x79\x17\x0b\x51\x9b\xb3\xc7\x10\x01\x13\xe7\x3f\xf3\x5f" - publicExponent = "\x5b\x7b\x88\xc0" - - publicKeyBlob = Stream() - publicKeyBlob.writeType(certificate.wPublicKeyBlobType) - publicKeyBlob.writeType(certificate.wPublicKeyBlobLen) - publicKeyBlob.writeType(certificate.PublicKeyBlob) - - dwVersion = Stream() - dwVersion.writeType(UInt32Le(certificate.__class__._TYPE_)) - - dwSigAlgId = Stream() - dwSigAlgId.writeType(certificate.dwSigAlgId) - - dwKeyAlgId = Stream() - dwKeyAlgId.writeType(certificate.dwKeyAlgId) - - md5Digest = md5.new() - md5Digest.update(dwVersion.getvalue()) - md5Digest.update(dwSigAlgId.getvalue()) - md5Digest.update(dwKeyAlgId.getvalue()) - md5Digest.update(publicKeyBlob.getvalue()) - - message = md5Digest.digest() + "\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01" - - return rsa.sign(message[::-1], rsa.PrivateKey(d = privateExponent[::-1], n = modulus[::-1])) def saltedHash(inputData, salt, salt1, salt2): """ @@ -400,7 +366,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP """ #if update is needed if self._nbDecryptedPacket == 4096: - log.info("update decrypt key") + log.debug("update decrypt key") self._currentDecrytKey = updateKey( self._initialDecrytKey, self._currentDecrytKey, self.getGCCServerSettings().SC_SECURITY.encryptionMethod.value) self._decryptRc4 = rc4.RC4Key(self._currentDecrytKey) @@ -427,7 +393,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP @return: {Tuple} (signature, encryptedData) """ if self._nbEncryptedPacket == 4096: - log.info("update encrypt key") + log.debug("update encrypt key") self._currentEncryptKey = updateKey( self._initialEncryptKey, self._currentEncryptKey, self.getGCCServerSettings().SC_SECURITY.encryptionMethod.value) self._encryptRc4 = rc4.RC4Key(self._currentEncryptKey) @@ -590,6 +556,9 @@ class Client(SecLayer): self._decryptRc4 = rc4.RC4Key(self._currentDecrytKey) self._encryptRc4 = rc4.RC4Key(self._currentEncryptKey) + #verify certificate + if not self.getGCCServerSettings().SC_SECURITY.serverCertificate.certData.verify(): + log.warning("cannot verify server identity") #send client random encrypted with serverPublicKey = self.getGCCServerSettings().SC_SECURITY.serverCertificate.certData.getPublicKey() message = ClientSecurityExchangePDU() @@ -644,8 +613,8 @@ class Server(SecLayer): certificate = gcc.ProprietaryServerCertificate() certificate.PublicKeyBlob.modulus.value = rsa.int2bytes(self._rsaPublicKey.n)[::-1] certificate.PublicKeyBlob.pubExp.value = self._rsaPublicKey.e - certificate.SignatureBlob.value = terminalServicesSign(certificate)[::-1] + "\x00" * 8 - return certificate + certificate.sign() + return gcc.ServerCertificate(certificate) def recvClientRandom(self, s): """ diff --git a/rdpy/protocol/rdp/x224.py b/rdpy/protocol/rdp/x224.py index 1287c6b..1733630 100644 --- a/rdpy/protocol/rdp/x224.py +++ b/rdpy/protocol/rdp/x224.py @@ -132,7 +132,7 @@ class X224Layer(LayerAutomata, IStreamSender): """ LayerAutomata.__init__(self, presentation) #client requested selectedProtocol - self._requestedProtocol = Protocols.PROTOCOL_RDP + self._requestedProtocol = Protocols.PROTOCOL_SSL #server selected selectedProtocol self._selectedProtocol = Protocols.PROTOCOL_SSL @@ -258,7 +258,7 @@ class Server(X224Layer): #match best security layer available if not self._serverPrivateKeyFileName is None and not self._serverCertificateFileName is None: - self._selectedProtocol = self._requestedProtocol & Protocols.PROTOCOL_RDP + self._selectedProtocol = self._requestedProtocol & Protocols.PROTOCOL_SSL else: self._selectedProtocol = self._requestedProtocol & Protocols.PROTOCOL_RDP diff --git a/rdpy/security/rsa_wrapper.py b/rdpy/security/rsa_wrapper.py index 9472f8c..642bc11 100644 --- a/rdpy/security/rsa_wrapper.py +++ b/rdpy/security/rsa_wrapper.py @@ -52,11 +52,11 @@ def PrivateKey(d, n): n = rsa.transform.bytes2int(n) return { 'd' : d, 'n' : n } -def int2bytes(i): +def int2bytes(i, fill_size=None): """ @summary: wrapper of rsa.transform.int2bytes """ - return rsa.transform.int2bytes(i) + return rsa.transform.int2bytes(i,fill_size) def random(size): """ @@ -71,7 +71,7 @@ def encrypt(message, publicKey): @param message: {str} source message @param publicKey: {rsa.PublicKey} """ - return rsa.transform.int2bytes(rsa.core.encrypt_int(rsa.transform.bytes2int(message), publicKey['e'], publicKey['n'])) + return rsa.transform.int2bytes(rsa.core.encrypt_int(rsa.transform.bytes2int(message), publicKey['e'], publicKey['n']), rsa.common.byte_size(publicKey['n'])) def decrypt(message, privateKey): """ @@ -87,4 +87,12 @@ def sign(message, privateKey): @param message: {str} message to sign @param privateKey : {rsa.privateKey} key use to sugn """ - return rsa.transform.int2bytes(rsa.core.encrypt_int(rsa.transform.bytes2int(message), privateKey['d'], privateKey['n'])) \ No newline at end of file + return rsa.transform.int2bytes(rsa.core.encrypt_int(rsa.transform.bytes2int(message), privateKey['d'], privateKey['n']), rsa.common.byte_size(privateKey['n'])) + +def verify(message, publicKey): + """ + @summary: return hash + @param message: {str} message to verify + @param publicKey : {rsa.publicKey} key use to sugn + """ + return rsa.transform.int2bytes(rsa.core.decrypt_int(rsa.transform.bytes2int(message), publicKey['e'], publicKey['n'])) \ No newline at end of file