diff --git a/README.md b/README.md index 4502a54..bac3f54 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Remote Desktop Protocol in twisted python. -RDPY is a pure Python implementation of the Microsoft RDP (Remote Desktop Protocol) protocol (client and server side). RDPY is built over the event driven network engine Twisted. +RDPY is a pure Python implementation of the Microsoft RDP (Remote Desktop Protocol) protocol (client and server side). RDPY is built over the event driven network engine Twisted. RDPY support standard RDP security layer, RDP over SSL and NLA authentication (through ntlmv2 authentication protocol). RDPY provides the following RDP and VNC binaries : * RDP Man In The Middle proxy which record session @@ -107,7 +107,7 @@ $ rdpy-rdpmitm.py -o output_dir [-l listen_port] [-k private_key_file_path] [-c ``` Output directory is used to save the rss file with following format (YYYYMMDDHHMMSS_ip_index.rss) -The private key file and the certificate file are classic cryptographic files for SSL connections. The RDP protocol can negotiate its own security layer. The CredSSP security layer is planned for an upcoming release. If one of both parameters are omitted, the server use standard RDP as security layer. +The private key file and the certificate file are classic cryptographic files for SSL connections. The RDP protocol can negotiate its own security layer If one of both parameters are omitted, the server use standard RDP as security layer. ### rdpy-rdphoneypot @@ -117,7 +117,7 @@ rdpy-rdphoneypot is an RDP honey Pot. Use Recorded Session Scenario to replay sc $ rdpy-rdphoneypot.py [-l listen_port] [-k private_key_file_path] [-c certificate_file_path] rss_file_path_1 ... rss_file_path_N ``` -The private key file and the certificate file are classic cryptographic files for SSL connections. The RDP protocol can negotiate its own security layer. The CredSSP security layer is planned for an upcoming release. If one of both parameters are omitted, the server use standard RDP as security layer. +The private key file and the certificate file are classic cryptographic files for SSL connections. The RDP protocol can negotiate its own security layer. If one of both parameters are omitted, the server use standard RDP as security layer. You can specify more than one files to match more common screen size. ### rdpy-rssplayer diff --git a/bin/rdpy-rdpclient.py b/bin/rdpy-rdpclient.py index 5e88652..b78fc1b 100755 --- a/bin/rdpy-rdpclient.py +++ b/bin/rdpy-rdpclient.py @@ -30,7 +30,7 @@ from rdpy.core.error import RDPSecurityNegoFail from rdpy.core import rss import rdpy.core.log as log -log._LOG_LEVEL = log.Level.DEBUG +log._LOG_LEVEL = log.Level.INFO class RDPClientQtRecorder(RDPClientQt): diff --git a/bin/rdpy-rdpmitm.py b/bin/rdpy-rdpmitm.py index f13b8db..0042a37 100755 --- a/bin/rdpy-rdpmitm.py +++ b/bin/rdpy-rdpmitm.py @@ -35,7 +35,7 @@ from rdpy.core import log, error, rss from rdpy.protocol.rdp import rdp from twisted.internet import reactor -log._LOG_LEVEL = log.Level.DEBUG +log._LOG_LEVEL = log.Level.INFO class ProxyServer(rdp.RDPServerObserver): """ @@ -251,7 +251,9 @@ def help(): [-l listen_port default 3389] [-k private_key_file_path (mandatory for SSL)] [-c certificate_file_path (mandatory for SSL)] + [-o output directory for recoded files] [-r RDP standard security (XP or server 2003 client or older)] + [-n For NLA Client authentication (need to provide credentials)] """ def parseIpPort(interface, defaultPort = "3389"): @@ -265,6 +267,7 @@ if __name__ == '__main__': privateKeyFilePath = None certificateFilePath = None ouputDirectory = None + #for anonymous authentication clientSecurity = rdp.SecurityLevel.RDP_LEVEL_SSL try: @@ -285,6 +288,8 @@ if __name__ == '__main__': ouputDirectory = arg elif opt == "-r": clientSecurity = rdp.SecurityLevel.RDP_LEVEL_RDP + elif opt == "-n": + clientSecurity = rdp.SecurityLevel.RDP_LEVEL_NLA if ouputDirectory is None or not os.path.dirname(ouputDirectory): log.error("%s is an invalid output directory"%ouputDirectory) diff --git a/rdpy/protocol/rdp/nla/cssp.py b/rdpy/protocol/rdp/nla/cssp.py index f8c2382..c42ca40 100644 --- a/rdpy/protocol/rdp/nla/cssp.py +++ b/rdpy/protocol/rdp/nla/cssp.py @@ -30,7 +30,6 @@ import pyasn1.codec.ber.encoder as ber_encoder from rdpy.core.type import Stream from twisted.internet import protocol from OpenSSL import crypto -from Crypto.Util import asn1 from rdpy.security import x509 from rdpy.core import error @@ -105,6 +104,17 @@ class TSSmartCardCreds(univ.Sequence): namedtype.OptionalNamedType('domainHint', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))) ) +class OpenSSLRSAPublicKey(univ.Sequence): + """ + @summary: asn1 public rsa key + @see: https://tools.ietf.org/html/rfc3447 + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('unknow', univ.Integer()), + namedtype.NamedType('modulus', univ.Integer()), + namedtype.NamedType('publicExponent', univ.Integer()), + ) + def encodeDERTRequest(negoTypes = [], authInfo = None, pubKeyAuth = None): """ @summary: create TSRequest from list of Type @@ -242,12 +252,11 @@ class CSSP(protocol.Protocol): #get back public key #convert from der to ber... pubKeyDer = crypto.dump_privatekey(crypto.FILETYPE_ASN1, self.transport.protocol._tlsConnection.get_peer_certificate().get_pubkey()) - pubKey = asn1.DerSequence() - pubKey.decode(pubKeyDer) + pubKey = der_decoder.decode(pubKeyDer, asn1Spec=OpenSSLRSAPublicKey())[0] rsa = x509.RSAPublicKey() - rsa.setComponentByName("modulus", univ.Integer(pubKey[1])) - rsa.setComponentByName("publicExponent", univ.Integer(pubKey[2])) + rsa.setComponentByName("modulus", univ.Integer(pubKey.getComponentByName('modulus')._value)) + rsa.setComponentByName("publicExponent", univ.Integer(pubKey.getComponentByName('publicExponent')._value)) self._pubKeyBer = ber_encoder.encode(rsa) #send authenticate message with public key encoded @@ -263,7 +272,7 @@ class CSSP(protocol.Protocol): request = decodeDERTRequest(data) pubKeyInc = self._interface.GSS_UnWrapEx(getPubKeyAuth(request)) #check pubKeyInc = self._pubKeyBer + 1 - if not self._pubKeyBer[:-1] == pubKeyInc[:-1] and ord(self._pubKeyBer[-1]) + 1 == pubKeyInc[-1]: + if not (self._pubKeyBer[1:] == pubKeyInc[1:] and ord(self._pubKeyBer[0]) + 1 == ord(pubKeyInc[0])): raise error.InvalidExpectedDataException("CSSP : Invalid public key increment") domain, user, password = self._authenticationProtocol.getEncodedCredentials()