From d6bb21565d1ffdfec21f4a2b2a1fa2fbcb033ac1 Mon Sep 17 00:00:00 2001 From: speyrefitte Date: Thu, 19 Feb 2015 18:26:50 +0100 Subject: [PATCH] start spneg --- bin/rdpy-rdpclient.py | 2 +- rdpy/protocol/rdp/nla/__init__.py | 0 rdpy/protocol/rdp/{credssp.py => nla/cssp.py} | 25 +++++++++- rdpy/{security => protocol/rdp/nla}/ntlm.py | 49 ++++++++++++++++--- rdpy/protocol/rdp/nla/spng.py | 38 ++++++++++++++ rdpy/protocol/rdp/rdp.py | 4 +- rdpy/protocol/rdp/tpkt.py | 9 ++-- rdpy/protocol/rdp/x224.py | 24 +++++---- 8 files changed, 128 insertions(+), 23 deletions(-) create mode 100644 rdpy/protocol/rdp/nla/__init__.py rename rdpy/protocol/rdp/{credssp.py => nla/cssp.py} (83%) rename rdpy/{security => protocol/rdp/nla}/ntlm.py (55%) create mode 100644 rdpy/protocol/rdp/nla/spng.py diff --git a/bin/rdpy-rdpclient.py b/bin/rdpy-rdpclient.py index 557ad24..dbe979a 100755 --- a/bin/rdpy-rdpclient.py +++ b/bin/rdpy-rdpclient.py @@ -115,7 +115,7 @@ class RDPClientQtFactory(rdp.ClientFactory): self._nego = security == "nego" self._recodedPath = recodedPath if self._nego: - self._security = "ssl" + self._security = "nla" else: self._security = security self._w = None diff --git a/rdpy/protocol/rdp/nla/__init__.py b/rdpy/protocol/rdp/nla/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rdpy/protocol/rdp/credssp.py b/rdpy/protocol/rdp/nla/cssp.py similarity index 83% rename from rdpy/protocol/rdp/credssp.py rename to rdpy/protocol/rdp/nla/cssp.py index bfc3eb5..3108229 100644 --- a/rdpy/protocol/rdp/credssp.py +++ b/rdpy/protocol/rdp/nla/cssp.py @@ -23,7 +23,8 @@ """ from pyasn1.type import namedtype, univ -from pyasn1.codec.ber import decoder +from pyasn1.codec.ber import encoder +from rdpy.core.type import Stream class NegoData(univ.SequenceOf): """ @@ -90,3 +91,25 @@ class TSSmartCardCreds(univ.Sequence): namedtype.OptionalNamedType('userHint', univ.OctetString()), namedtype.OptionalNamedType('domainHint', univ.OctetString()) ) + +def createBERRequest(negoTokens): + """ + @summary: create TSRequest from list of Type + @param negoTokens: {list(Type)} + @return: {str} + """ + negoData = NegoData() + + #fill nego data tokens + i = 0 + for negoToken in negoTokens: + s = Stream() + s.writeType(negoToken) + negoData.setComponentByPosition(i, s.getvalue()) + i += 1 + + request = TSRequest() + request.setComponentByName("version", univ.Integer(2)) + request.setComponentByName("negoTokens", negoData) + return encoder.encode(request) + \ No newline at end of file diff --git a/rdpy/security/ntlm.py b/rdpy/protocol/rdp/nla/ntlm.py similarity index 55% rename from rdpy/security/ntlm.py rename to rdpy/protocol/rdp/nla/ntlm.py index 1bf83dc..03440c2 100644 --- a/rdpy/security/ntlm.py +++ b/rdpy/protocol/rdp/nla/ntlm.py @@ -47,16 +47,43 @@ class NTLMRevision(object): @see: https://msdn.microsoft.com/en-us/library/cc236654.aspx """ NTLMSSP_REVISION_W2K3 = 0x0F + +class Negotiate(object): + """ + @see: https://msdn.microsoft.com/en-us/library/cc236650.aspx + """ + NTLMSSP_NEGOTIATE_56 = 0x80000000 + NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000 + NTLMSSP_NEGOTIATE_128 = 0x20000000 + NTLMSSP_NEGOTIATE_VERSION = 0x02000000 + NTLMSSP_NEGOTIATE_TARGET_INFO = 0x00800000 + NTLMSSP_REQUEST_NON_NT_SESSION_KEY = 0x00400000 + NTLMSSP_NEGOTIATE_IDENTIFY = 0x00100000 + NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000 + NTLMSSP_TARGET_TYPE_SERVER = 0x00020000 + NTLMSSP_TARGET_TYPE_DOMAIN = 0x00010000 + NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x00008000 + NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000 + NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED = 0x00001000 + NTLMSSP_NEGOTIATE_NTLM = 0x00000200 + NTLMSSP_NEGOTIATE_LM_KEY = 0x00000080 + NTLMSSP_NEGOTIATE_DATAGRAM = 0x00000040 + NTLMSSP_NEGOTIATE_SEAL = 0x00000020 + NTLMSSP_NEGOTIATE_SIGN = 0x00000010 + NTLMSSP_REQUEST_TARGET = 0x00000004 + NTLM_NEGOTIATE_OEM = 0x00000002 + NTLMSSP_NEGOTIATE_UNICODE = 0x00000001 class Version(CompositeType): """ @summary: Version structure as describe in NTLM spec @see: https://msdn.microsoft.com/en-us/library/cc236654.aspx """ - def __init__(self): + def __init__(self, conditional): + CompositeType.__init__(self, conditional = conditional) self.ProductMajorVersion = UInt8(MajorVersion.WINDOWS_MAJOR_VERSION_6) - self.ProductMinorVersion = UInt8(MinorVersion.WINDOWS_MINOR_VERSION_2) - self.ProductBuild = UInt16Le() + self.ProductMinorVersion = UInt8(MinorVersion.WINDOWS_MINOR_VERSION_0) + self.ProductBuild = UInt16Le(6002) self.Reserved = UInt24Le() self.NTLMRevisionCurrent = UInt8(NTLMRevision.NTLMSSP_REVISION_W2K3) @@ -69,13 +96,21 @@ class NegotiateMessage(CompositeType): CompositeType.__init__(self) self.Signature = String("NTLMSSP\x00", constant = True) self.MessageType = UInt32Le(0x00000001) - self.NegotiateFlags = UInt32Le() + self.NegotiateFlags = UInt32Le(Negotiate.NTLMSSP_NEGOTIATE_KEY_EXCH | + Negotiate.NTLMSSP_NEGOTIATE_128 | + 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_UNICODE) self.DomainNameLen = UInt16Le() - self.DomainNameMaxLen = UInt16Le(self.DomainNameLen) + self.DomainNameMaxLen = UInt16Le(lambda:self.DomainNameLen.value) self.DomainNameBufferOffset = UInt32Le() self.WorkstationLen = UInt16Le() - self.WorkstationMaxLen = UInt16Le(self.WorkstationLen) + self.WorkstationMaxLen = UInt16Le(lambda:self.WorkstationLen.value) self.WorkstationBufferOffset = UInt32Le() - self.Version = Version() + self.Version = Version(conditional = lambda:(self.NegotiateFlags & Negotiate.NTLMSSP_NEGOTIATE_VERSION)) self.Payload = String() \ No newline at end of file diff --git a/rdpy/protocol/rdp/nla/spng.py b/rdpy/protocol/rdp/nla/spng.py new file mode 100644 index 0000000..043b89d --- /dev/null +++ b/rdpy/protocol/rdp/nla/spng.py @@ -0,0 +1,38 @@ +# +# Copyright (c) 2014-2015 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 . +# + +""" +@summary: Simple and protected GSS-API Negotiation Mechanism +@see: https://msdn.microsoft.com/en-us/library/cc247021.aspx +""" + +from pyasn1.type import namedtype, univ + +class NegTokenInit2(univ.Sequence): + """ + @summary: main structure + @see: https://msdn.microsoft.com/en-us/library/cc247039.aspx + """ + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('mechTypes', univ.Integer()), + namedtype.OptionalNamedType('reqFlags', NegoData()), + namedtype.OptionalNamedType('mechToken', univ.OctetString()), + namedtype.OptionalNamedType('negHints', univ.OctetString()), + namedtype.OptionalNamedType('mechListMIC', univ.Integer()) + ) \ No newline at end of file diff --git a/rdpy/protocol/rdp/rdp.py b/rdpy/protocol/rdp/rdp.py index 5d03c04..3babf6c 100644 --- a/rdpy/protocol/rdp/rdp.py +++ b/rdpy/protocol/rdp/rdp.py @@ -137,12 +137,14 @@ class RDPClientController(pdu.layer.PDUClientListener): def setSecurityLevel(self, level): """ @summary: Request basic security - @param level: {str} (ssl | rdp) + @param level: {str} (ssl | rdp | nla) """ if level == "rdp": self._x224Layer._requestedProtocol = x224.Protocols.PROTOCOL_RDP elif level == "ssl": self._x224Layer._requestedProtocol = x224.Protocols.PROTOCOL_SSL + elif level == "nla": + self._x224Layer._requestedProtocol = x224.Protocols.PROTOCOL_SSL | x224.Protocols.PROTOCOL_HYBRID def addClientObserver(self, observer): """ diff --git a/rdpy/protocol/rdp/tpkt.py b/rdpy/protocol/rdp/tpkt.py index 14d71cc..c2b083e 100644 --- a/rdpy/protocol/rdp/tpkt.py +++ b/rdpy/protocol/rdp/tpkt.py @@ -26,6 +26,8 @@ from rdpy.core.layer import RawLayer from rdpy.core.type import UInt8, UInt16Be, sizeof from rdpy.core.error import CallPureVirtualFuntion +from nla import cssp, ntlm + class Action(object): """ @see: http://msdn.microsoft.com/en-us/library/cc240621.aspx @@ -216,9 +218,10 @@ class TPKT(RawLayer, IFastPathSender): """ self.transport.startTLS(sslContext) - def startNLA(self, sslContext): + def startNLA(self): """ @summary: use to start NLA (NTLM over SSL) protocol - @param sslContext: {ssl.ClientContextFactory | ssl.DefaultOpenSSLContextFactory} context use for NLA protocol + must be called after startTLS function """ - self.transport.startTLS(sslContext) \ No newline at end of file + #send first NTLM packet + self.transport.write(cssp.createBERRequest( [ ntlm.NegotiateMessage() ] )) \ No newline at end of file diff --git a/rdpy/protocol/rdp/x224.py b/rdpy/protocol/rdp/x224.py index 8a581dd..5e469b6 100644 --- a/rdpy/protocol/rdp/x224.py +++ b/rdpy/protocol/rdp/x224.py @@ -50,6 +50,7 @@ class NegociationType(object): class Protocols(object): """ @summary: Protocols available for x224 layer + @see: https://msdn.microsoft.com/en-us/library/cc240500.aspx """ PROTOCOL_RDP = 0x00000000 PROTOCOL_SSL = 0x00000001 @@ -132,7 +133,7 @@ class X224Layer(LayerAutomata, IStreamSender): """ LayerAutomata.__init__(self, presentation) #client requested selectedProtocol - self._requestedProtocol = Protocols.PROTOCOL_SSL + self._requestedProtocol = Protocols.PROTOCOL_SSL | Protocols.PROTOCOL_HYBRID #server selected selectedProtocol self._selectedProtocol = Protocols.PROTOCOL_SSL @@ -204,19 +205,22 @@ class Client(X224Layer): self._selectedProtocol = Protocols.PROTOCOL_RDP #NLA protocol doesn't support in actual version of RDPY - if self._selectedProtocol in [ Protocols.PROTOCOL_HYBRID, Protocols.PROTOCOL_HYBRID_EX ]: - raise InvalidExpectedDataException("RDPY doesn't support NLA security Layer") + if self._selectedProtocol in [ Protocols.PROTOCOL_HYBRID_EX ]: + raise InvalidExpectedDataException("RDPY doesn't support PROTOCOL_HYBRID_EX security Layer") - if self._selectedProtocol == Protocols.PROTOCOL_SSL: + if self._selectedProtocol in [ Protocols.PROTOCOL_SSL, Protocols.PROTOCOL_HYBRID ]: log.debug("*" * 10 + " select SSL layer " + "*" * 10) - #_transport is TPKT and transport is TCP layer of twisted self._transport.startTLS(ClientTLSContext()) - #now i'm ready to receive data - self.setNextState(self.recvData) - - #connection is done send to presentation - self._presentation.connect() + if self._selectedProtocol == Protocols.PROTOCOL_HYBRID: + log.debug("*" * 10 + " select NLA layer " + "*" * 10) + self._transport.startNLA() + else: + #now i'm ready to receive data + self.setNextState(self.recvData) + + #connection is done send to presentation + self._presentation.connect() class Server(X224Layer): """