finish RDP basic security layer on server side except signature and fastpath

This commit is contained in:
speyrefitte
2014-12-23 18:24:41 +01:00
parent f9b30a668a
commit 28f50b1f0e
13 changed files with 162 additions and 63 deletions

View File

@@ -29,7 +29,7 @@ from rdpy.protocol.rdp import rdp
from rdpy.core.error import RDPSecurityNegoFail
import rdpy.core.log as log
log._LOG_LEVEL = log.Level.INFO
#log._LOG_LEVEL = log.Level.INFO
class RDPClientQtFactory(rdp.ClientFactory):
"""

View File

@@ -25,7 +25,9 @@ http://msdn.microsoft.com/en-us/library/cc240508.aspx
from rdpy.core.type import UInt8, UInt16Le, UInt32Le, CompositeType, String, Stream, sizeof, FactoryType, ArrayType
import per, mcs
from rdpy.core.error import InvalidExpectedDataException
from rdpy.core import log, x509
from rdpy.core import log
from rdpy.security import x509
import rdpy.security.rsa_wrapper as rsa
t124_02_98_oid = ( 0, 0, 20, 124, 0, 1 )
@@ -312,7 +314,7 @@ class ServerCertificate(CompositeType):
"""
def __init__(self, certData = None, readLen = None, conditional = lambda:True):
CompositeType.__init__(self, readLen = readLen, conditional = conditional)
self.dwVersion = UInt32Le()
self.dwVersion = UInt32Le(lambda:self.certData.__class__._TYPE_)
def CertificateFactory():
"""
@@ -328,18 +330,7 @@ class ServerCertificate(CompositeType):
elif not "_TYPE_" in certData.__class__.__dict__:
raise InvalidExpectedDataException("Try to send an invalid Certificate")
self.certData = FactoryType(CertificateFactory)
def bin2bn(b):
"""
@summary: convert binary string to bignum
@param b: {str} binary string
@return: {long} bignum
"""
l = 0L
for ch in b:
l = (l<<8) | ord(ch)
return l
self.certData = FactoryType(CertificateFactory)
class ProprietaryServerCertificate(CompositeType):
"""
@@ -363,8 +354,9 @@ class ProprietaryServerCertificate(CompositeType):
"""
@return: {Tuple} (modulus, publicExponent)
"""
log.debug("read RSA public key from proprietary certificate")
#reverse because bignum in little endian
return bin2bn(self.PublicKeyBlob.modulus.value[::-1]), self.PublicKeyBlob.pubExp.value
return rsa.PublicKey(self.PublicKeyBlob.pubExp.value, self.PublicKeyBlob.modulus.value[::-1])
class CertBlob(CompositeType):
"""
@@ -393,8 +385,10 @@ class X509CertificateChain(CompositeType):
"""
@return: {Tuple} (modulus, publicExponent)
"""
#last certifcate contain publi key
return x509.extractRSAKey(x509.load(self.CertBlobArray[-1].abCert.value))
log.debug("read RSA public key from x509 certificate")
#last certifcate contain public key
n, e = x509.extractRSAKey(x509.load(self.CertBlobArray[-1].abCert.value))
return rsa.PublicKey(e, n)
class RSAPublicKey(CompositeType):
"""

View File

@@ -25,8 +25,8 @@
from rdpy.core.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType, Stream
from rdpy.core.error import InvalidExpectedDataException
import rdpy.core.log as log
import sec, gcc
from rdpy.core import x509, rc4
import sec
from rdpy.security import rc4
class MessageType(object):
"""

View File

@@ -31,7 +31,7 @@ from rdpy.protocol.rdp.ber import writeLength
import rdpy.core.log as log
import ber, gcc, per
import rsa
import rdpy.security.rsa_wrapper as rsa
class Message(object):
"""
@@ -491,7 +491,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.randnum.read_random_bits(256)
self._serverSettings.SC_SECURITY.serverRandom.value = rsa.random(256)
self._serverSettings.SC_SECURITY.serverCertificate.certData = self._presentation.getCertificate()
self._serverSettings.SC_CORE.clientRequestedProtocol.value = self._transport._requestedProtocol

View File

@@ -21,12 +21,14 @@
Some use full methods for security in RDP
"""
import sha, md5, rsa
import sha, md5
import gcc, lic, tpkt, mcs
from rdpy.core.type import CompositeType, Stream, UInt32Le, UInt16Le, String, sizeof, UInt8
from rdpy.core.layer import LayerAutomata, IStreamSender
from rdpy.core.error import InvalidExpectedDataException
from rdpy.core import log, rc4
from rdpy.core import log
from rdpy.security import rc4
import rdpy.security.rsa_wrapper as rsa
class SecurityFlag(object):
"""
@@ -94,33 +96,39 @@ class AfInet(object):
AF_INET = 0x00002
AF_INET6 = 0x0017
def terminalServicesSigningKey():
"""
@summary: security is security ;-) Generate RSA private key
@see: http://msdn.microsoft.com/en-us/library/cc240776.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"
return rsa.PublicKey(e = gcc.bin2bn(privateExponent[::-1]), n = gcc.bin2bn(modulus[::-1]))
def terminalServicesSign(certificate):
"""
@summary: sign proprietary certificate
@param certificate: {gcc.ProprietaryServerCertificate}
@see: http://msdn.microsoft.com/en-us/library/cc240778.aspx
"""
s = Stream()
s.writeType(UInt32Le(gcc.ProprietaryServerCertificate._TYPE_))
s.writeType(certificate.dwSigAlgId)
s.writeType(certificate.dwKeyAlgId)
s.writeType(certificate.wPublicKeyBlobType)
s.writeType(certificate.wPublicKeyBlobLen)
s.writeType(certificate.PublicKeyBlob)
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(s.getvalue())
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.encrypt(message, terminalServicesSigningKey())
return rsa.sign(message[::-1], rsa.PrivateKey(d = privateExponent[::-1], n = modulus[::-1]))
def saltedHash(inputData, salt, salt1, salt2):
"""
@@ -179,7 +187,6 @@ def sessionKeyBlob(secret, random1, random2):
"""
return saltedHash("X", secret, random1, random2) + saltedHash("YY", secret, random1, random2) + saltedHash("ZZZ", secret, random1, random2)
def macData(macSaltKey, data):
"""
@see: http://msdn.microsoft.com/en-us/library/cc241995.aspx
@@ -573,7 +580,7 @@ class Client(SecLayer):
@summary: generate and send client random and init session keys
"""
#generate client random
clientRandom = rsa.randnum.read_random_bits(256)
clientRandom = rsa.random(256)
self._macKey, self._initialDecrytKey, self._initialEncryptKey = generateKeys( clientRandom,
self.getGCCServerSettings().SC_SECURITY.serverRandom.value,
self.getGCCServerSettings().SC_SECURITY.encryptionMethod.value)
@@ -584,8 +591,7 @@ class Client(SecLayer):
self._encryptRc4 = rc4.RC4Key(self._currentEncryptKey)
#send client random encrypted with
modulus, publicExponent = self.getGCCServerSettings().SC_SECURITY.serverCertificate.certData.getPublicKey()
serverPublicKey = rsa.PublicKey(modulus, publicExponent)
serverPublicKey = self.getGCCServerSettings().SC_SECURITY.serverCertificate.certData.getPublicKey()
message = ClientSecurityExchangePDU()
#reverse because bignum in little endian
message.encryptedClientRandom.value = rsa.encrypt(clientRandom[::-1], serverPublicKey)[::-1]
@@ -619,7 +625,7 @@ class Server(SecLayer):
@param presentation: {Layer}
"""
SecLayer.__init__(self, presentation)
self._rsaPublicKey, self._rsaPrivaterKey = rsa.newkeys(512)
self._rsaPublicKey, self._rsaPrivateKey = rsa.newkeys(512)
def connect(self):
"""
@@ -636,9 +642,9 @@ class Server(SecLayer):
@summary: generate proprietary certificate from rsa public key
"""
certificate = gcc.ProprietaryServerCertificate()
certificate.PublicKeyBlob.modulus.value = hex(self._rsaPublicKey.n)[2:-1].decode('hex')[::-1]
certificate.PublicKeyBlob.modulus.value = rsa.int2bytes(self._rsaPublicKey.n)[::-1]
certificate.PublicKeyBlob.pubExp.value = self._rsaPublicKey.e
certificate.SignatureBlob.value = hex(terminalServicesSign(certificate))[2:-1].decode('hex')[::-1]
certificate.SignatureBlob.value = terminalServicesSign(certificate)[::-1] + "\x00" * 8
return certificate
def recvClientRandom(self, s):
@@ -646,11 +652,19 @@ class Server(SecLayer):
@summary: receive client random and generate session keys
@param s: {Stream}
"""
#packet preambule
securityFlag = UInt16Le()
securityFlagHi = UInt16Le()
s.readType((securityFlag, securityFlagHi))
if not (securityFlag.value & SecurityFlag.SEC_EXCHANGE_PKT):
raise InvalidExpectedDataException("waiting client random")
message = ClientSecurityExchangePDU()
s.readType(message)
clientRandom = rsa.decrypt(message.encryptedClientRandom.value, self._rsaPrivateKey)
clientRandom = rsa.decrypt(message.encryptedClientRandom.value[::-1], self._rsaPrivateKey)[::-1]
self._macKey, self._initialDecrytKey, self._initialEncryptKey = generateKeys( clientRandom,
self._macKey, self._initialEncryptKey, self._initialDecrytKey = generateKeys( clientRandom,
self.getGCCServerSettings().SC_SECURITY.serverRandom.value,
self.getGCCServerSettings().SC_SECURITY.encryptionMethod.value)
#initialize keys
@@ -661,7 +675,6 @@ class Server(SecLayer):
self.setNextState(self.recvInfoPkt)
def recvInfoPkt(self, s):
"""
@summary: receive info packet from client
@@ -678,6 +691,9 @@ class Server(SecLayer):
if not (securityFlag.value & SecurityFlag.SEC_INFO_PKT):
raise InvalidExpectedDataException("Waiting info packet")
if securityFlag.value & SecurityFlag.SEC_ENCRYPT:
s = self.readEncryptedPayload(s)
s.readType(self._info)
#next state send error license
self.sendLicensingErrorMessage()

View File

@@ -147,7 +147,7 @@ class TPKT(RawLayer, IFastPathSender):
self.expect(2, self.readExtendedHeader)
else:
#is fast path packet
self._secFlag = version.value >> 6
self._secFlag = ((version.value >> 6) & 0x3)
data.readType(self._lastShortLength)
if self._lastShortLength.value & 0x80:
#size is 1 byte more
@@ -207,4 +207,4 @@ class TPKT(RawLayer, IFastPathSender):
@param fastPathS: type transform to stream and send as fastpath
@param secFlag: {integer} Security flag for fastpath packet
"""
RawLayer.send(self, (UInt8(Action.FASTPATH_ACTION_FASTPATH | secFlag), UInt16Be((sizeof(fastPathS) + 3) | 0x8000), fastPathS))
RawLayer.send(self, (UInt8(Action.FASTPATH_ACTION_FASTPATH | ((secFlag & 0x3) << 6)), UInt16Be((sizeof(fastPathS) + 3) | 0x8000), fastPathS))

View File

@@ -23,6 +23,7 @@ Implement transport PDU layer
This layer have main goal to negociate SSL transport
RDP basic security is supported only on client side
"""
from rdpy.core import log
from rdpy.core.layer import LayerAutomata, IStreamSender
from rdpy.core.type import UInt8, UInt16Le, UInt16Be, UInt32Le, CompositeType, sizeof, String
@@ -131,7 +132,7 @@ class X224Layer(LayerAutomata, IStreamSender):
"""
LayerAutomata.__init__(self, presentation)
#client requested selectedProtocol
self._requestedProtocol = Protocols.PROTOCOL_RDP | Protocols.PROTOCOL_SSL
self._requestedProtocol = Protocols.PROTOCOL_RDP
#server selected selectedProtocol
self._selectedProtocol = Protocols.PROTOCOL_SSL
@@ -207,6 +208,7 @@ class Client(X224Layer):
raise InvalidExpectedDataException("RDPY doesn't support NLA security Layer")
if self._selectedProtocol == Protocols.PROTOCOL_SSL:
log.debug("*" * 10 + " select SSL layer" + "*" * 10)
#_transport is TPKT and transport is TCP layer of twisted
self._transport.transport.startTLS(ClientTLSContext())
@@ -262,6 +264,7 @@ class Server(X224Layer):
#if force ssl is enable
if not self._selectedProtocol & Protocols.PROTOCOL_SSL and self._forceSSL:
log.warning("server reject client because doesn't support SSL")
#send error message and quit
message = ServerConnectionConfirm()
message.protocolNeg.code.value = NegociationType.TYPE_RDP_NEG_FAILURE
@@ -284,6 +287,7 @@ class Server(X224Layer):
message.protocolNeg.selectedProtocol.value = self._selectedProtocol
self._transport.send(message)
if self._selectedProtocol == Protocols.PROTOCOL_SSL:
log.debug("*" * 5 + " select SSL layer")
#_transport is TPKT and transport is TCP layer of twisted
self._transport.transport.startTLS(ServerTLSContext(self._serverPrivateKeyFileName, self._serverCertificateFileName))

View File

@@ -28,7 +28,7 @@ Implement Remote FrameBuffer protocol use in VNC client and server
from rdpy.core.layer import RawLayer, RawLayerClientFactory
from rdpy.core.type import UInt8, UInt16Be, UInt32Be, SInt32Be, String, CompositeType
from rdpy.core.error import InvalidValue, CallPureVirtualFuntion
from rdpy.protocol.rfb.pyDes import des
from rdpy.security.pyDes import des
import rdpy.core.log as log
class ProtocolVersion(object):

View File

View File

@@ -0,0 +1,90 @@
#
# Copyright (c) 2014 Sylvain Peyrefitte
#
# This file is part of rdpy.
#
# rdpy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
Wrapper around RSA library
"""
import rsa
def newkeys(size):
"""
@summary: wrapper around rsa.newkeys function
@param size: {integer} size of key
"""
return rsa.newkeys(size)
def PublicKey(e, n):
"""
@param e: {long | str}public exponent
@param n: {long | str}modulus
"""
if isinstance(e, str):
e = rsa.transform.bytes2int(e)
if isinstance(n, str):
n = rsa.transform.bytes2int(n)
return { 'e' : e, 'n' : n }
def PrivateKey(d, n):
"""
@param d: {long | str}private exponent
@param n: {long | str}modulus
"""
if isinstance(d, str):
d = rsa.transform.bytes2int(d)
if isinstance(n, str):
n = rsa.transform.bytes2int(n)
return { 'd' : d, 'n' : n }
def int2bytes(i):
"""
@summary: wrapper of rsa.transform.int2bytes
"""
return rsa.transform.int2bytes(i)
def random(size):
"""
@summary: wrapper around rsa.randnum.read_random_bits function
@return: {str} random bytes array
"""
return rsa.randnum.read_random_bits(size)
def encrypt(message, publicKey):
"""
@summary: wrapper around rsa.core.encrypt_int function
@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']))
def decrypt(message, privateKey):
"""
@summary: wrapper around rsa.core.decrypt_int function
@param message: {str} source message
@param publicKey: {rsa.PrivateKey}
"""
return rsa.transform.int2bytes(rsa.core.decrypt_int(rsa.transform.bytes2int(message), privateKey['d'], privateKey['n']))
def sign(message, privateKey):
"""
@summary: sign message with private key
@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']))

View File

@@ -22,9 +22,7 @@
"""
from pyasn1.type import tag, namedtype, namedval, univ, constraint, char, useful
from pyasn1.codec.ber import decoder, encoder
from pyasn1 import error
from error import InvalidExpectedDataException
from pyasn1.codec.ber import decoder
MAX = 64
@@ -149,9 +147,6 @@ def extractRSAKey(certificate):
@return: (modulus, public exponent)
"""
#http://www.alvestrand.no/objectid/1.2.840.113549.1.1.1.html
#disable check because nobody respect
#if certificate.getComponentByName('tbsCertificate').getComponentByName('subjectPublicKeyInfo').getComponentByName('algorithm').getComponentByName('algorithm')._value != (1, 3, 14, 3, 2, 15):
# raise InvalidExpectedDataException("Certificate doesn't contain RSA public key")
#extract binary data
l = 0L