NLA Security Layer is AVAILABLE

This commit is contained in:
speyrefitte
2015-03-06 18:19:42 +01:00
parent b57b3d7398
commit 0abf18d130
10 changed files with 274 additions and 264 deletions

View File

@@ -115,7 +115,7 @@ class RDPClientQtFactory(rdp.ClientFactory):
self._nego = security == "nego"
self._recodedPath = recodedPath
if self._nego:
self._security = "nla"
self._security = rdp.SecurityLevel.RDP_LEVEL_NLA
else:
self._security = security
self._w = None
@@ -163,7 +163,7 @@ class RDPClientQtFactory(rdp.ClientFactory):
#stop nego
log.info("due to security nego error back to standard RDP security layer")
self._nego = False
self._security = "rdp"
self._security = rdp.SecurityLevel.RDP_LEVEL_RDP
self._client._widget.hide()
connector.connect()
return

View File

@@ -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.INFO
log._LOG_LEVEL = log.Level.DEBUG
class ProxyServer(rdp.RDPServerObserver):
"""
@@ -265,7 +265,7 @@ if __name__ == '__main__':
privateKeyFilePath = None
certificateFilePath = None
ouputDirectory = None
clientSecurity = "ssl"
clientSecurity = rdp.SecurityLevel.RDP_LEVEL_SSL
try:
opts, args = getopt.getopt(sys.argv[1:], "hl:k:c:o:r")
@@ -284,7 +284,7 @@ if __name__ == '__main__':
elif opt == "-o":
ouputDirectory = arg
elif opt == "-r":
clientSecurity = "rdp"
clientSecurity = rdp.SecurityLevel.RDP_LEVEL_RDP
if ouputDirectory is None or not os.path.dirname(ouputDirectory):
log.error("%s is an invalid output directory"%ouputDirectory)

View File

@@ -222,13 +222,19 @@ class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender):
"""
self._factory.connectionLost(self, reason)
def getDescriptor(self):
"""
@return: the twited file descriptor
"""
return self.transport
def close(self):
"""
@summary: Close raw layer
Use File descriptor directly to not use TLS close
Because is bugged
"""
FileDescriptor.loseConnection(self.transport)
FileDescriptor.loseConnection(self.getDescriptor())
def expect(self, expectedLen, callback = None):
"""

View File

@@ -23,11 +23,16 @@
"""
from pyasn1.type import namedtype, univ, tag
from pyasn1.codec.der import encoder, decoder
import pyasn1.codec.der.encoder as der_encoder
import pyasn1.codec.der.decoder as der_decoder
import pyasn1.codec.ber.encoder as ber_encoder
from rdpy.core.type import Stream
from rdpy.security import x509
from twisted.internet import protocol
from OpenSSL import crypto
from Crypto.Util import asn1
from rdpy.security import x509
from rdpy.core import error
class NegoToken(univ.Sequence):
componentType = namedtype.NamedTypes(
@@ -100,11 +105,13 @@ class TSSmartCardCreds(univ.Sequence):
namedtype.OptionalNamedType('domainHint', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)))
)
def encodeDERTRequest(negoTypes = [], pubKeyAuth = None):
def encodeDERTRequest(negoTypes = [], authInfo = None, pubKeyAuth = None):
"""
@summary: create TSRequest from list of Type
@param negoTypes: {list(Type)}
@return: {str}
@param authInfo: {str} authentication info TSCredentials encrypted with authentication protocol
@param pubKeyAuth: {str} public key encrypted with authentication protocol
@return: {str} TRequest der encoded
"""
negoData = NegoData().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))
@@ -120,18 +127,24 @@ def encodeDERTRequest(negoTypes = [], pubKeyAuth = None):
request = TSRequest()
request.setComponentByName("version", univ.Integer(2).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
request.setComponentByName("negoTokens", negoData)
if i > 0:
request.setComponentByName("negoTokens", negoData)
if not authInfo is None:
request.setComponentByName("authInfo", univ.OctetString(authInfo).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2)))
if not pubKeyAuth is None:
request.setComponentByName("pubKeyAuth", univ.OctetString(pubKeyAuth).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)))
request.setComponentByName("pubKeyAuth", univ.OctetString(pubKeyAuth).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)))
return encoder.encode(request)
return der_encoder.encode(request)
def decodeDERTRequest(s):
"""
@summary: Decode the stream as
@param s: {str}
"""
return decoder.decode(s, asn1Spec=TSRequest())[0]
return der_decoder.decode(s, asn1Spec=TSRequest())[0]
def getNegoTokens(tRequest):
negoData = tRequest.getComponentByName("negoTokens")
@@ -140,6 +153,18 @@ def getNegoTokens(tRequest):
def getPubKeyAuth(tRequest):
return tRequest.getComponentByName("pubKeyAuth").asOctets()
def encodeDERTCredentials(domain, username, password):
passwordCred = TSPasswordCreds()
passwordCred.setComponentByName("domainName", univ.OctetString(domain).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
passwordCred.setComponentByName("userName", univ.OctetString(username).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
passwordCred.setComponentByName("password", univ.OctetString(password).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2)))
credentials = TSCredentials()
credentials.setComponentByName("credType", univ.Integer(1).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
credentials.setComponentByName("credentials", univ.OctetString(der_encoder.encode(passwordCred)).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
return der_encoder.encode(credentials)
class CSSP(protocol.Protocol):
"""
@summary: Handle CSSP connection
@@ -152,6 +177,10 @@ class CSSP(protocol.Protocol):
"""
self._layer = layer
self._authenticationProtocol = authenticationProtocol
#IGenericSecurityService
self._interface = None
#function call at the end of nego
self._callback = None
def setFactory(self, factory):
"""
@@ -173,6 +202,7 @@ class CSSP(protocol.Protocol):
@summary: install proxy
"""
self._layer.transport = self
self._layer.getDescriptor = lambda:self.transport
self._layer.connectionMade()
def write(self, data):
@@ -189,10 +219,14 @@ class CSSP(protocol.Protocol):
"""
self.transport.startTLS(sslContext)
def startNLA(self):
def startNLA(self, sslContext, callback = None):
"""
@summary: start NLA authentication
@param sslContext: {ssl.ClientContextFactory | ssl.DefaultOpenSSLContextFactory} context use for TLS protocol
@param callback: {function} function call when cssp layer is read
"""
self._callback = callback
self.startTLS(sslContext)
#send negotiate message
self.transport.write(encodeDERTRequest( negoTypes = [ self._authenticationProtocol.getNegotiateMessage() ] ))
#next state is receive a challenge
@@ -201,17 +235,41 @@ class CSSP(protocol.Protocol):
def recvChallenge(self, data):
"""
@summary: second state in cssp automata
@param {str}: all data available on buffer
@param data : {str} all data available on buffer
"""
#get back public key
toto = self.transport.protocol._tlsConnection.get_peer_certificate().get_pubkey()
lolo = crypto.dump_privatekey(crypto.FILETYPE_ASN1, toto)
modulus, exponent = x509.extractRSAKey2(lolo)
import hexdump
print lolo
hexdump.hexdump(lolo)
request = decodeDERTRequest(data)
message, interface = self._authenticationProtocol.getAuthenticateMessage(getNegoTokens(request)[0])
message, self._interface = self._authenticationProtocol.getAuthenticateMessage(getNegoTokens(request)[0])
#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)
rsa = x509.RSAPublicKey()
rsa.setComponentByName("modulus", univ.Integer(pubKey[1]))
rsa.setComponentByName("publicExponent", univ.Integer(pubKey[2]))
self._pubKeyBer = ber_encoder.encode(rsa)
#send authenticate message with public key encoded
import ntlm
self.transport.write(encodeDERTRequest( negoTypes = [ message ], pubKeyAuth = interface.GSS_WrapEx("".join([chr(i) for i in ntlm.pubKeyHex]))))
self.transport.write(encodeDERTRequest( negoTypes = [ message ], pubKeyAuth = self._interface.GSS_WrapEx(self._pubKeyBer)))
#next step is received public key incremented by one
self.dataReceived = self.recvPubKeyInc
def recvPubKeyInc(self, data):
"""
@summary: the server send the pubKeyBer + 1
@param data : {str} all data available on buffer
"""
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]:
raise error.InvalidExpectedDataException("CSSP : Invalid public key increment")
domain, user, password = self._authenticationProtocol.getEncodedCredentials()
#send credentials
self.transport.write(encodeDERTRequest( authInfo = self._interface.GSS_WrapEx(encodeDERTCredentials(domain, user, password))))
#reset state back to normal state
self.dataReceived = lambda x: self.__class__.dataReceived(self, x)
if not self._callback is None:
self._callback()

View File

@@ -28,7 +28,7 @@ import rdpy.security.pyDes as pyDes
import rdpy.security.rc4 as rc4
from rdpy.security.rsa_wrapper import random
from rdpy.core.type import CompositeType, CallableValue, String, UInt8, UInt16Le, UInt24Le, UInt32Le, sizeof, Stream
from rdpy.core import filetimes
from rdpy.core import filetimes, error
class MajorVersion(object):
"""
@@ -80,6 +80,22 @@ class Negotiate(object):
NTLM_NEGOTIATE_OEM = 0x00000002
NTLMSSP_NEGOTIATE_UNICODE = 0x00000001
class AvId(object):
"""
@see: https://msdn.microsoft.com/en-us/library/cc236646.aspx
"""
MsvAvEOL = 0x0000
MsvAvNbComputerName = 0x0001
MsvAvNbDomainName = 0x0002
MsvAvDnsComputerName = 0x0003
MsvAvDnsDomainName = 0x0004
MsvAvDnsTreeName = 0x0005
MsvAvFlags = 0x0006
MsvAvTimestamp = 0x0007
MsvAvSingleHost = 0x0008
MsvAvTargetName = 0x0009
MsvChannelBindings = 0x000A
def getPayLoadField(message, length, bufferOffset):
if length == 0:
return None
@@ -101,6 +117,16 @@ class Version(CompositeType):
self.Reserved = UInt24Le()
self.NTLMRevisionCurrent = UInt8(NTLMRevision.NTLMSSP_REVISION_W2K3)
class AvPair(CompositeType):
"""
@see: https://msdn.microsoft.com/en-us/library/cc236646.aspx
"""
def __init__(self):
CompositeType.__init__(self)
self.AvId = UInt16Le()
self.AvLen = UInt16Le(lambda:sizeof(self.Value))
self.Value = String(readLen = self.AvLen)
class MessageSignatureEx(CompositeType):
"""
@summary: Signature for message
@@ -109,7 +135,7 @@ class MessageSignatureEx(CompositeType):
def __init__(self):
CompositeType.__init__(self)
self.Version = UInt32Le(0x00000001, constant = True)
self.Checksum = String(readLen = CallableValue(16))
self.Checksum = String(readLen = CallableValue(8))
self.SeqNum = UInt32Le()
class NegotiateMessage(CompositeType):
@@ -166,7 +192,22 @@ class ChallengeMessage(CompositeType):
return getPayLoadField(self, self.TargetNameLen.value, self.TargetNameBufferOffset.value)
def getTargetInfo(self):
return getPayLoadField(self, self.TargetInfoLen.value - 4, self.TargetInfoBufferOffset.value)
return getPayLoadField(self, self.TargetInfoLen.value, self.TargetInfoBufferOffset.value)
def getTargetInfoAsAvPairArray(self):
"""
@summary: Parse Target info field to retrieve array of AvPair
@return: {map(AvId, str)}
"""
result = {}
s = Stream(self.getTargetInfo())
while(True):
avPair = AvPair()
s.readType(avPair)
if avPair.AvId.value == AvId.MsvAvEOL:
return result
result[avPair.AvId.value] = avPair.Value.value
class AuthenticateMessage(CompositeType):
"""
@@ -205,7 +246,7 @@ class AuthenticateMessage(CompositeType):
self.NegotiateFlags = UInt32Le()
self.Version = Version(conditional = lambda:(self.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_VERSION))
#self.MIC = String("\x00" * 16, readLen = CallableValue(16))
self.MIC = String("\x00" * 16, readLen = CallableValue(16))
self.Payload = String()
def getUserName(self):
@@ -223,7 +264,7 @@ class AuthenticateMessage(CompositeType):
def getEncryptedRandomSession(self):
return getPayLoadField(self, self.EncryptedRandomSessionLen.value, self.EncryptedRandomSessionBufferOffset.value)
def createAuthenticationMessage(domain, user, NtChallengeResponse, LmChallengeResponse, EncryptedRandomSessionKey):
def createAuthenticationMessage(NegFlag, domain, user, NtChallengeResponse, LmChallengeResponse, EncryptedRandomSessionKey, Workstation):
"""
@summary: Create an Authenticate Message
@param domain: {str} domain microsoft
@@ -233,6 +274,7 @@ def createAuthenticationMessage(domain, user, NtChallengeResponse, LmChallengeRe
@param EncryptedRandomSessionKey: {str} EncryptedRandomSessionKey
"""
message = AuthenticateMessage()
message.NegotiateFlags.value = NegFlag
#fill message
offset = sizeof(message)
@@ -246,6 +288,11 @@ def createAuthenticationMessage(domain, user, NtChallengeResponse, LmChallengeRe
message.Payload.value += user
offset += len(user)
message.WorkstationLen.value = len(Workstation)
message.WorkstationBufferOffset.value = offset
message.Payload.value += Workstation
offset += len(Workstation)
message.LmChallengeResponseLen.value = len(LmChallengeResponse)
message.LmChallengeResponseBufferOffset.value = offset
message.Payload.value += LmChallengeResponse
@@ -399,7 +446,7 @@ def ComputeResponsev2(ResponseKeyNT, ResponseKeyLM, ServerChallenge, ClientChall
Responserversion = "\x01"
HiResponserversion = "\x01"
temp = Responserversion + HiResponserversion + Z(6) + Time + ClientChallenge + Z(4) + ServerName + Z(4)
temp = Responserversion + HiResponserversion + Z(6) + Time + ClientChallenge + Z(4) + ServerName
NTProofStr = HMAC_MD5(ResponseKeyNT, ServerChallenge + temp)
NtChallengeResponse = NTProofStr + temp
LmChallengeResponse = HMAC_MD5(ResponseKeyLM, ServerChallenge + ClientChallenge) + ClientChallenge
@@ -427,6 +474,19 @@ def MAC(handle, SigningKey, SeqNum, Message):
signature.Checksum.value = rc4.crypt(handle, HMAC_MD5(SigningKey, s.getvalue() + Message)[:8])
return signature
def MIC(ExportedSessionKey, negotiateMessage, challengeMessage, authenticateMessage):
"""
@summary: Compute MIC signature
@param negotiateMessage: {NegotiateMessage}
@param challengeMessage: {ChallengeMessage}
@param authenticateMessage: {AuthenticateMessage}
@return: {str} signature
@see: https://msdn.microsoft.com/en-us/library/cc236676.aspx
"""
s = Stream()
s.writeType((negotiateMessage, challengeMessage, authenticateMessage))
return HMAC_MD5(ExportedSessionKey, s.getvalue())
class NTLMv2(sspi.IAuthenticationProtocol):
"""
@@ -435,16 +495,23 @@ class NTLMv2(sspi.IAuthenticationProtocol):
def __init__(self, domain, user, password):
self._domain = domain
self._user = user
self._password = password
self._enableUnicode = False
#https://msdn.microsoft.com/en-us/library/cc236700.aspx
self._ResponseKeyNT = NTOWFv2(password, user, domain)
self._ResponseKeyLM = LMOWFv2(password, user, domain)
#For MIC computation
self._negotiateMessage = None
self._challengeMessage = None
self._authenticateMessage = None
def getNegotiateMessage(self):
"""
@summary: generate first handshake messgae
"""
message = NegotiateMessage()
message.NegotiateFlags.value = (Negotiate.NTLMSSP_NEGOTIATE_KEY_EXCH |
self._negotiateMessage = NegotiateMessage()
self._negotiateMessage.NegotiateFlags.value = (Negotiate.NTLMSSP_NEGOTIATE_KEY_EXCH |
Negotiate.NTLMSSP_NEGOTIATE_128 |
Negotiate.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY |
Negotiate.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
@@ -453,7 +520,7 @@ class NTLMv2(sspi.IAuthenticationProtocol):
Negotiate.NTLMSSP_NEGOTIATE_SIGN |
Negotiate.NTLMSSP_REQUEST_TARGET |
Negotiate.NTLMSSP_NEGOTIATE_UNICODE)
return message
return self._negotiateMessage
def getAuthenticateMessage(self, s):
"""
@@ -462,13 +529,21 @@ class NTLMv2(sspi.IAuthenticationProtocol):
@return: {(AuthenticateMessage, NTLMv2SecurityInterface)} Last handshake message and security interface use to encrypt
@see: https://msdn.microsoft.com/en-us/library/cc236676.aspx
"""
challenge = ChallengeMessage()
s.readType(challenge)
self._challengeMessage = ChallengeMessage()
s.readType(self._challengeMessage)
ServerChallenge = challenge.ServerChallenge.value
ServerChallenge = self._challengeMessage.ServerChallenge.value
ClientChallenge = random(64)
Timestamp = CurrentFileTimes()
ServerName = challenge.getTargetInfo()
computeMIC = False
ServerName = self._challengeMessage.getTargetInfo()
infos = self._challengeMessage.getTargetInfoAsAvPairArray()
if infos.has_key(AvId.MsvAvTimestamp):
Timestamp = infos[AvId.MsvAvTimestamp]
computeMIC = True
else:
Timestamp = CurrentFileTimes()
NtChallengeResponse, LmChallengeResponse, SessionBaseKey = ComputeResponsev2(self._ResponseKeyNT, self._ResponseKeyLM, ServerChallenge, ClientChallenge, Timestamp, ServerName)
KeyExchangeKey = KXKEYv2(SessionBaseKey, LmChallengeResponse, ServerChallenge)
@@ -476,9 +551,15 @@ class NTLMv2(sspi.IAuthenticationProtocol):
EncryptedRandomSessionKey = RC4K(KeyExchangeKey, ExportedSessionKey)
domain, user = self._domain, self._user
if challenge.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_UNICODE:
if self._challengeMessage.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_UNICODE:
self._enableUnicode = True
domain, user = UNICODE(domain), UNICODE(user)
message = createAuthenticationMessage(domain, user, NtChallengeResponse, LmChallengeResponse, EncryptedRandomSessionKey)
self._authenticateMessage = createAuthenticationMessage(self._challengeMessage.NegotiateFlags.value, domain, user, NtChallengeResponse, LmChallengeResponse, EncryptedRandomSessionKey, "")
if computeMIC:
self._authenticateMessage.MIC.value = MIC(ExportedSessionKey, self._negotiateMessage, self._challengeMessage, self._authenticateMessage)
else:
self._authenticateMessage.MIC._conditional = lambda:False
ClientSigningKey = SIGNKEY(ExportedSessionKey, True)
ServerSigningKey = SIGNKEY(ExportedSessionKey, False)
@@ -487,7 +568,17 @@ class NTLMv2(sspi.IAuthenticationProtocol):
interface = NTLMv2SecurityInterface(rc4.RC4Key(ClientSealingKey), rc4.RC4Key(ServerSealingKey), ClientSigningKey, ServerSigningKey)
return message, interface
return self._authenticateMessage, interface
def getEncodedCredentials(self):
"""
@summary: return encoded credentials accorded with authentication protocol nego
@return: (domain, username, password)
"""
if self._enableUnicode:
return UNICODE(self._domain), UNICODE(self._user), UNICODE(self._password)
else:
return self._domain, self._user, self._password
class NTLMv2SecurityInterface(sspi.IGenericSecurityService):
@@ -519,196 +610,26 @@ class NTLMv2SecurityInterface(sspi.IGenericSecurityService):
s = Stream()
s.writeType(signature)
return s.getvalue() + encryptedData
pubKeyHex = [
0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0x9E,
0x95, 0xB5, 0x41, 0x03, 0xC5, 0x33, 0xEA, 0x29,
0x65, 0x2B, 0x65, 0xEF, 0x30, 0x71, 0xDD, 0x73,
0xBB, 0x30, 0x3B, 0xEC, 0xCA, 0x72, 0xCF, 0xBD,
0xE0, 0xF8, 0x21, 0xFF, 0xA6, 0x97, 0x76, 0xA1,
0x08, 0xB5, 0xD2, 0xC6, 0x95, 0x81, 0xD2, 0xBA,
0x71, 0x10, 0x4A, 0xAC, 0x25, 0x34, 0x37, 0xA0,
0xC3, 0x57, 0xF0, 0xEA, 0x1F, 0x8C, 0x84, 0xEB,
0x7B, 0xE6, 0x6C, 0x50, 0x26, 0x1F, 0xB7, 0x41,
0x0A, 0x58, 0xD3, 0x80, 0x87, 0x3D, 0x0B, 0x41,
0xD9, 0xBC, 0x54, 0x3A, 0x0F, 0x77, 0x14, 0x79,
0xF5, 0xB9, 0xA4, 0x38, 0xEB, 0x13, 0x08, 0x35,
0xAE, 0xBF, 0xB3, 0x17, 0x5A, 0xE2, 0x58, 0x89,
0x39, 0xC4, 0x22, 0x7F, 0x16, 0x57, 0x90, 0x08,
0xAF, 0x91, 0x3B, 0x95, 0xC8, 0x53, 0xD0, 0xC0,
0x8E, 0x19, 0x8A, 0xF3, 0x10, 0xBC, 0xC8, 0xC7,
0x42, 0xFB, 0x12, 0xDE, 0x2D, 0x5E, 0x83, 0x02,
0x03, 0x01, 0x00, 0x01 ]
peer0_0 = [
0x30, 0x2f, 0xa0, 0x03, 0x02, 0x01, 0x02, 0xa1,
0x28, 0x30, 0x26, 0x30, 0x24, 0xa0, 0x22, 0x04,
0x20, 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50,
0x00, 0x01, 0x00, 0x00, 0x00, 0x35, 0x82, 0x08,
0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00 ]
peer1_0 = [
0x30, 0x82, 0x01, 0x09, 0xa0, 0x03, 0x02, 0x01,
0x02, 0xa1, 0x82, 0x01, 0x00, 0x30, 0x81, 0xfd,
0x30, 0x81, 0xfa, 0xa0, 0x81, 0xf7, 0x04, 0x81,
0xf4, 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50,
0x00, 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x0e,
0x00, 0x38, 0x00, 0x00, 0x00, 0x35, 0x82, 0x89,
0x62, 0x0a, 0xee, 0xd7, 0xc3, 0xeb, 0x8e, 0x34,
0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xae, 0x00, 0xae, 0x00, 0x46, 0x00, 0x00,
0x00, 0x06, 0x01, 0xb1, 0x1d, 0x00, 0x00, 0x00,
0x0f, 0x53, 0x00, 0x49, 0x00, 0x52, 0x00, 0x41,
0x00, 0x44, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x02,
0x00, 0x0e, 0x00, 0x53, 0x00, 0x49, 0x00, 0x52,
0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00, 0x4c,
0x00, 0x01, 0x00, 0x16, 0x00, 0x57, 0x00, 0x41,
0x00, 0x56, 0x00, 0x2d, 0x00, 0x47, 0x00, 0x4c,
0x00, 0x57, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x30,
0x00, 0x39, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x53,
0x00, 0x69, 0x00, 0x72, 0x00, 0x61, 0x00, 0x64,
0x00, 0x65, 0x00, 0x6c, 0x00, 0x2e, 0x00, 0x6c,
0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c,
0x00, 0x03, 0x00, 0x32, 0x00, 0x77, 0x00, 0x61,
0x00, 0x76, 0x00, 0x2d, 0x00, 0x67, 0x00, 0x6c,
0x00, 0x77, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x30,
0x00, 0x39, 0x00, 0x2e, 0x00, 0x53, 0x00, 0x69,
0x00, 0x72, 0x00, 0x61, 0x00, 0x64, 0x00, 0x65,
0x00, 0x6c, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f,
0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x05,
0x00, 0x1a, 0x00, 0x53, 0x00, 0x69, 0x00, 0x72,
0x00, 0x61, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6c,
0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63,
0x00, 0x61, 0x00, 0x6c, 0x00, 0x07, 0x00, 0x08,
0x00, 0xe5, 0x40, 0x3c, 0xa6, 0x68, 0x57, 0xd0,
0x01, 0x00, 0x00, 0x00, 0x00 ]
peer0_1 = [
0x30, 0x82, 0x02, 0x21, 0xa0, 0x03, 0x02, 0x01,
0x02, 0xa1, 0x82, 0x01, 0x76, 0x30, 0x82, 0x01,
0x72, 0x30, 0x82, 0x01, 0x6e, 0xa0, 0x82, 0x01,
0x6a, 0x04, 0x82, 0x01, 0x66, 0x4e, 0x54, 0x4c,
0x4d, 0x53, 0x53, 0x50, 0x00, 0x03, 0x00, 0x00,
0x00, 0x18, 0x00, 0x18, 0x00, 0x64, 0x00, 0x00,
0x00, 0xda, 0x00, 0xda, 0x00, 0x7c, 0x00, 0x00,
0x00, 0x0e, 0x00, 0x0e, 0x00, 0x40, 0x00, 0x00,
0x00, 0x16, 0x00, 0x16, 0x00, 0x4e, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x10, 0x00, 0x56, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x00, 0x69,
0x00, 0x72, 0x00, 0x61, 0x00, 0x64, 0x00, 0x65,
0x00, 0x6c, 0x00, 0x73, 0x00, 0x70, 0x00, 0x65,
0x00, 0x79, 0x00, 0x72, 0x00, 0x65, 0x00, 0x66,
0x00, 0x69, 0x00, 0x74, 0x00, 0x74, 0x00, 0x65,
0x00, 0x8a, 0x01, 0x34, 0xd8, 0x57, 0x6e, 0x14,
0x2b, 0xda, 0xc6, 0x91, 0x02, 0x49, 0xbb, 0xc4,
0x00, 0x19, 0x6c, 0x60, 0x26, 0x16, 0xdb, 0x37,
0x8f, 0x98, 0xe1, 0x04, 0xf8, 0x36, 0x6a, 0x96,
0xa2, 0xa1, 0x9a, 0xf9, 0x5f, 0x1f, 0x04, 0x63,
0x69, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x38, 0x8d, 0x10, 0x08, 0x71, 0x57, 0xd0,
0x01, 0x19, 0x6c, 0x60, 0x26, 0x16, 0xdb, 0x37,
0x8f, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0e,
0x00, 0x53, 0x00, 0x49, 0x00, 0x52, 0x00, 0x41,
0x00, 0x44, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x01,
0x00, 0x16, 0x00, 0x57, 0x00, 0x41, 0x00, 0x56,
0x00, 0x2d, 0x00, 0x47, 0x00, 0x4c, 0x00, 0x57,
0x00, 0x2d, 0x00, 0x30, 0x00, 0x30, 0x00, 0x39,
0x00, 0x04, 0x00, 0x1a, 0x00, 0x53, 0x00, 0x69,
0x00, 0x72, 0x00, 0x61, 0x00, 0x64, 0x00, 0x65,
0x00, 0x6c, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f,
0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x03,
0x00, 0x32, 0x00, 0x77, 0x00, 0x61, 0x00, 0x76,
0x00, 0x2d, 0x00, 0x67, 0x00, 0x6c, 0x00, 0x77,
0x00, 0x2d, 0x00, 0x30, 0x00, 0x30, 0x00, 0x39,
0x00, 0x2e, 0x00, 0x53, 0x00, 0x69, 0x00, 0x72,
0x00, 0x61, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6c,
0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63,
0x00, 0x61, 0x00, 0x6c, 0x00, 0x05, 0x00, 0x1a,
0x00, 0x53, 0x00, 0x69, 0x00, 0x72, 0x00, 0x61,
0x00, 0x64, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x2e,
0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61,
0x00, 0x6c, 0x00, 0x07, 0x00, 0x08, 0x00, 0xe5,
0x40, 0x3c, 0xa6, 0x68, 0x57, 0xd0, 0x01, 0x00,
0x00, 0x00, 0x00, 0x59, 0x49, 0x84, 0x63, 0xf2,
0x84, 0x53, 0x18, 0xea, 0xa4, 0xc3, 0xb6, 0x97,
0x0d, 0x3e, 0x38, 0xa3, 0x81, 0x9f, 0x04, 0x81,
0x9c, 0x01, 0x00, 0x00, 0x00, 0xc9, 0x56, 0x22,
0x84, 0x7d, 0xba, 0xa2, 0xe6, 0x00, 0x00, 0x00,
0x00, 0x1c, 0x6d, 0x39, 0xe1, 0x5a, 0x31, 0x5d,
0xf5, 0x01, 0xa6, 0xea, 0x4b, 0xaf, 0x83, 0x13,
0xdc, 0x8a, 0x45, 0xb3, 0x76, 0xc6, 0x3d, 0xbf,
0x73, 0x4c, 0x93, 0xe6, 0x75, 0x8b, 0x42, 0x21,
0xea, 0xe6, 0x0c, 0xfa, 0x3c, 0xd0, 0x7c, 0x8d,
0xd6, 0x2a, 0x97, 0x7a, 0x49, 0xb5, 0x7d, 0xeb,
0xc2, 0x94, 0xc0, 0x84, 0xb4, 0xef, 0x7f, 0x1e,
0xa3, 0xa3, 0x3f, 0x61, 0x7c, 0x1c, 0xd9, 0x82,
0xc6, 0x0b, 0x6c, 0x85, 0x15, 0xb0, 0x47, 0x25,
0xe9, 0x0a, 0x88, 0x58, 0x3c, 0x6d, 0x8e, 0x60,
0x2a, 0xbc, 0x04, 0x57, 0x7f, 0x5b, 0x03, 0x7c,
0x7a, 0x8f, 0x1b, 0x7b, 0xe3, 0x67, 0xb6, 0x02,
0xa4, 0xc0, 0xdd, 0x9e, 0x97, 0x4c, 0xd8, 0x86,
0x5c, 0x9a, 0x45, 0x0d, 0x85, 0x4b, 0x46, 0x87,
0xde, 0xcf, 0x31, 0x72, 0xe3, 0xd7, 0x5d, 0x0b,
0x67, 0x1b, 0xa1, 0xde, 0x24, 0x87, 0xdf, 0xd9,
0xb2, 0x18, 0xfd, 0x5a, 0x29, 0xbb, 0x35, 0xe0,
0x3d, 0x9f, 0x85, 0xf7, 0x36 ]
if __name__ == "__main__":
import cssp, hexdump
negotiate_data_request = cssp.decodeDERTRequest(Stream("".join([chr(i) for i in peer0_0])))
challenge_data_request = cssp.decodeDERTRequest(Stream("".join([chr(i) for i in peer1_0])))
authenticate_data_request = cssp.decodeDERTRequest(Stream("".join([chr(i) for i in peer0_1])))
negotiate_data = cssp.getNegoTokens(negotiate_data_request)[0]
challenge_data = cssp.getNegoTokens(challenge_data_request)[0]
authenticate_data = cssp.getNegoTokens(authenticate_data_request)[0]
negotiate = NegotiateMessage()
negotiate_data.readType(negotiate)
challenge = ChallengeMessage()
challenge_data.readType(challenge)
ServerChallenge = challenge.ServerChallenge.value
ServerName = challenge.getTargetInfo()
authenticate = AuthenticateMessage()
authenticate_data.readType(authenticate)
NtChallengeResponseTemp = authenticate.getNtChallengeResponse()
NTProofStr = NtChallengeResponseTemp[:16]
temp = NtChallengeResponseTemp[16:]
Timestamp = temp[8:16]
ClientChallenge = temp[16:24]
EncryptedRandomSessionKey = authenticate.getEncryptedRandomSession()
domain = ""
user = ""
password = ""
ResponseKeyNT = NTOWFv2(password, user, domain)
ResponseKeyLM = LMOWFv2(password, user, domain)
def GSS_UnWrapEx(self, data):
"""
@summary: decrypt data with key exchange in Authentication protocol
@param data: {str}
"""
signature = MessageSignatureEx()
message = String()
s = Stream(data)
s.readType((signature, message))
NtChallengeResponse, LmChallengeResponse, SessionBaseKey = ComputeResponsev2(ResponseKeyNT, ResponseKeyLM, ServerChallenge, ClientChallenge, Timestamp, ServerName)
KeyExchangeKey = KXKEYv2(SessionBaseKey, LmChallengeResponse, ServerChallenge)
ExportedSessionKey = RC4K(KeyExchangeKey, EncryptedRandomSessionKey)
domain, user = domain, user
if challenge.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_UNICODE:
domain, user = UNICODE(domain), UNICODE(user)
message = createAuthenticationMessage(domain, user, NtChallengeResponse, LmChallengeResponse, EncryptedRandomSessionKey)
ClientSigningKey = SIGNKEY(ExportedSessionKey, True)
ServerSigningKey = SIGNKEY(ExportedSessionKey, False)
ClientSealingKey = SEALKEY(ExportedSessionKey, True)
ServerSealingKey = SEALKEY(ExportedSessionKey, False)
interface = NTLMv2SecurityInterface(rc4.RC4Key(ClientSealingKey), rc4.RC4Key(ServerSealingKey), ClientSigningKey, ServerSigningKey)
EncryptedPubKeySrc = cssp.getPubKeyAuth(authenticate_data_request)
EncryptedPubKeyDst = interface.GSS_WrapEx("".join([chr(i) for i in pubKeyHex]))
print "EncryptedPubKeySrc"
hexdump.hexdump(EncryptedPubKeySrc)
print "EncryptedPubKeyDst"
hexdump.hexdump(EncryptedPubKeyDst)
#decrypt message
plaintextMessage = rc4.crypt(self._decryptHandle, message.value)
checksum = rc4.crypt(self._decryptHandle, signature.Checksum.value)
#recompute checksum
t = Stream()
t.writeType(signature.SeqNum)
verify = HMAC_MD5(self._verifyKey, t.getvalue() + plaintextMessage)[:8]
if verify != checksum:
raise error.InvalidExpectedDataException("NTLMv2SecurityInterface : Invalid checksum")
return plaintextMessage

View File

@@ -42,6 +42,13 @@ class IAuthenticationProtocol(object):
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getAuthenticateMessage", "IAuthenticationProtocol"))
def getEncodedCredentials(self):
"""
@summary: return encoded credentials accorded with authentication protocol nego
@return: (domain, username, password)
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getEncodedCredentials", "IAuthenticationProtocol"))
class IGenericSecurityService(object):
"""
@summary: use by application from authentification protocol
@@ -49,7 +56,14 @@ class IGenericSecurityService(object):
"""
def GSS_WrapEx(self, data):
"""
@summary: encrypt data with key exchage in Authentication protocol
@summary: encrypt data with key exchange in Authentication protocol
@param data: {str}
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "GSS_WrapEx", "IGenericSecurityService"))
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "GSS_WrapEx", "IGenericSecurityService"))
def GSS_UnWrapEx(self, data):
"""
@summary: decrypt data with key exchange in Authentication protocol
@param data: {str}
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "GSS_UnWrapEx", "IGenericSecurityService"))

View File

@@ -31,6 +31,14 @@ import tpkt, x224, sec
from t125 import mcs, gcc
from nla import cssp, ntlm
class SecurityLevel(object):
"""
@summary: RDP security level
"""
RDP_LEVEL_RDP = 0
RDP_LEVEL_SSL = 1
RDP_LEVEL_NLA = 2
class RDPClientController(pdu.layer.PDUClientListener):
"""
Manage RDP stack as client
@@ -59,7 +67,8 @@ class RDPClientController(pdu.layer.PDUClientListener):
@return: return Protocol layer for twisted
In case of RDP TPKT is the Raw layer
"""
return self._tpktLayer
#build a cssp wrapper in case of nla authentication
return cssp.CSSP(self._tpktLayer, ntlm.NTLMv2(self._secLayer._info.domain.value, self._secLayer._info.userName.value, self._secLayer._info.password.value))
def getColorDepth(self):
"""
@@ -139,13 +148,13 @@ class RDPClientController(pdu.layer.PDUClientListener):
def setSecurityLevel(self, level):
"""
@summary: Request basic security
@param level: {str} (ssl | rdp | nla)
@param level: {SecurityLevel}
"""
if level == "rdp":
if level == SecurityLevel.RDP_LEVEL_RDP:
self._x224Layer._requestedProtocol = x224.Protocols.PROTOCOL_RDP
elif level == "ssl":
elif level == SecurityLevel.RDP_LEVEL_SSL:
self._x224Layer._requestedProtocol = x224.Protocols.PROTOCOL_SSL
elif level == "nla":
elif level == SecurityLevel.RDP_LEVEL_NLA:
self._x224Layer._requestedProtocol = x224.Protocols.PROTOCOL_SSL | x224.Protocols.PROTOCOL_HYBRID
def addClientObserver(self, observer):
@@ -351,6 +360,7 @@ class RDPServerController(pdu.layer.PDUServerListener):
self._x224Layer = x224.Server(self._mcsLayer, privateKeyFileName, certificateFileName, False)
#transport packet (protocol layer)
self._tpktLayer = tpkt.TPKT(self._x224Layer)
#fastpath stack
self._pduLayer.initFastPath(self._secLayer)
self._secLayer.initFastPath(self._tpktLayer)
@@ -527,8 +537,7 @@ class ClientFactory(layer.RawLayerClientFactory):
"""
controller = RDPClientController()
self.buildObserver(controller, addr)
#Add cssp proxy in case of nla protocol
return cssp.CSSP(controller.getProtocol(), ntlm.NTLMv2("toto", "coco", "lolo"))
return controller.getProtocol()
def buildObserver(self, controller, addr):
"""

View File

@@ -26,8 +26,6 @@ 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
@@ -218,9 +216,9 @@ class TPKT(RawLayer, IFastPathSender):
"""
self.transport.startTLS(sslContext)
def startNLA(self):
def startNLA(self, sslContext, callback):
"""
@summary: use to start NLA (NTLM over SSL) protocol
must be called after startTLS function
"""
self.transport.startNLA()
self.transport.startNLA(sslContext, callback)

View File

@@ -208,19 +208,29 @@ class Client(X224Layer):
if self._selectedProtocol in [ Protocols.PROTOCOL_HYBRID_EX ]:
raise InvalidExpectedDataException("RDPY doesn't support PROTOCOL_HYBRID_EX security Layer")
if self._selectedProtocol in [ Protocols.PROTOCOL_SSL, Protocols.PROTOCOL_HYBRID ]:
log.debug("*" * 10 + " select SSL layer " + "*" * 10)
self._transport.startTLS(ClientTLSContext())
#now i'm ready to receive data
self.setNextState(self.recvData)
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)
if self._selectedProtocol == Protocols.PROTOCOL_RDP:
log.warning("*" * 43)
log.warning("*" * 10 + " RDP Security selected " + "*" * 10)
log.warning("*" * 43)
#connection is done send to presentation
self._presentation.connect()
elif self._selectedProtocol == Protocols.PROTOCOL_SSL:
log.info("*" * 43)
log.info("*" * 10 + " SSL Security selected " + "*" * 10)
log.info("*" * 43)
self._transport.startTLS(ClientTLSContext())
#connection is done send to presentation
self._presentation.connect()
elif self._selectedProtocol == Protocols.PROTOCOL_HYBRID:
log.info("*" * 43)
log.info("*" + " " * 10 + "NLA Security selected" + " " * 10 + "*")
log.info("*" * 43)
self._transport.startNLA(ClientTLSContext(), lambda:self._presentation.connect())
class Server(X224Layer):
"""

View File

@@ -154,10 +154,4 @@ def extractRSAKey(certificate):
def extractRSAKeyFromASN1(subjectPublicKey):
rsaKey = decoder.decode(subjectPublicKey, asn1Spec=RSAPublicKey())[0]
return rsaKey.getComponentByName('modulus')._value , rsaKey.getComponentByName('publicExponent')._value
def extractRSAKey2(key):
binaryTuple = decoder.decode(key, asn1Spec=univ.BitString())[0]
l = int("".join([str(i) for i in binaryTuple]), 2)
return extractRSAKeyFromASN1(hex(l)[2:-1].decode('hex'))
return rsaKey.getComponentByName('modulus')._value , rsaKey.getComponentByName('publicExponent')._value