def __init__(self, parser: BasicSecurityParser): """ :param parser: the parser to use for security traffic. :type parser: Parser """ Layer.__init__(self, parser, hasNext=True) self.mainParser = parser self.clientInfoParser = ClientInfoParser()
def onClientInfo(self, pdu: PlayerPDU): parser = ClientInfoParser() clientInfoPDU = parser.parse(pdu.payload) info = self.json[JSON_KEY_INFO] info["date"] = pdu.timestamp info["username"] = clientInfoPDU.username.replace("\x00", "") info["password"] = clientInfoPDU.password.replace("\x00", "") info["domain"] = clientInfoPDU.domain.replace("\x00", "")
def onClientInfo(self, pdu: PlayerPDU): parser = ClientInfoParser() clientInfoPDU = parser.parse(pdu.payload) self.writeSeparator() self.writeText("USERNAME: {}\nPASSWORD: {}\nDOMAIN: {}\n".format( clientInfoPDU.username.replace("\x00", ""), clientInfoPDU.password.replace("\x00", ""), clientInfoPDU.domain.replace("\x00", ""))) self.writeSeparator()
def __init__(self, viewer, text): super().__init__() self.viewer = viewer self.text = text self.writeInCaps = False self.inputParser = BasicFastPathParser(ParserMode.SERVER) self.outputParser = BasicFastPathParser(ParserMode.CLIENT) self.clientInfoParser = ClientInfoParser() self.dataParser = SlowPathParser() self.clipboardParser = ClipboardParser() self.outputEventParser = FastPathOutputParser() self.clientConnectionParser = ClientConnectionParser() self.buffer = b""
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 onClientInfo(self, data: bytes): """ Log the client connection information and replace the username and password if applicable. :param data: the client info data """ pdu = ClientInfoParser().parse(data) clientAddress = None if pdu.extraInfo: clientAddress = decodeUTF16LE(pdu.extraInfo.clientAddress) self.log.info( "Client Info: username = %(username)r, password = %(password)r, domain = %(domain)r, clientAddress = %(clientAddress)r", { "username": pdu.username, "password": pdu.password, "domain": pdu.domain, "clientAddress": clientAddress }) self.recorder.record(pdu, PlayerPDUType.CLIENT_INFO) # If set, replace the provided username and password to connect the user regardless of # the credentials they entered. if self.config.replacementUsername is not None: pdu.username = self.config.replacementUsername if self.config.replacementPassword is not None: pdu.password = self.config.replacementPassword if self.config.replacementUsername is not None and self.config.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 %(pdu)s", {"pdu": pdu}) self.server.sendClientInfo(pdu)
def __init__(self, transports: List[LayerChainItem]): self.parsers: Dict[PlayerPDUType, Parser] = { PlayerPDUType.FAST_PATH_INPUT: BasicFastPathParser(ParserMode.CLIENT), PlayerPDUType.FAST_PATH_OUTPUT: BasicFastPathParser(ParserMode.SERVER), PlayerPDUType.CLIENT_INFO: ClientInfoParser(), PlayerPDUType.SLOW_PATH_PDU: SlowPathParser(), PlayerPDUType.CLIPBOARD_DATA: ClipboardParser(), PlayerPDUType.CLIENT_DATA: ClientConnectionParser(), } self.topLayers = [] self.recordFilename = None for transport in transports: self.addTransport(transport)
def __init__(self, transportLayers: List[Layer]): self.parsers: Dict[PlayerMessageType, Parser] = { PlayerMessageType.FAST_PATH_INPUT: BasicFastPathParser(ParserMode.CLIENT), PlayerMessageType.FAST_PATH_OUTPUT: BasicFastPathParser(ParserMode.SERVER), PlayerMessageType.CLIENT_INFO: ClientInfoParser(), PlayerMessageType.SLOW_PATH_PDU: SlowPathParser(), PlayerMessageType.CLIPBOARD_DATA: ClipboardParser(), PlayerMessageType.CLIENT_DATA: ClientConnectionParser(), } self.topLayers = [] for transportLayer in transportLayers: self.addTransportLayer(transportLayer)
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 PlayerMessageHandler(PlayerMessageObserver): """ Class to manage the display of the RDP player when reading events. """ def __init__(self, viewer, text): super().__init__() self.viewer = viewer self.text = text self.writeInCaps = False self.inputParser = BasicFastPathParser(ParserMode.SERVER) self.outputParser = BasicFastPathParser(ParserMode.CLIENT) self.clientInfoParser = ClientInfoParser() self.dataParser = SlowPathParser() self.clipboardParser = ClipboardParser() self.outputEventParser = FastPathOutputParser() self.clientConnectionParser = ClientConnectionParser() self.buffer = b"" def onConnectionClose(self, pdu: PlayerMessagePDU): self.text.moveCursor(QTextCursor.End) self.text.insertPlainText("\n<Connection closed>") def onOutput(self, pdu: PlayerMessagePDU): pdu = self.outputParser.parse(pdu.payload) for event in pdu.events: reassembledEvent = self.reassembleEvent(event) if reassembledEvent is not None: if isinstance(reassembledEvent, FastPathOrdersEvent): log.debug("Not handling orders event, not coded :)") elif isinstance(reassembledEvent, FastPathBitmapEvent): log.debug("Handling bitmap event %(arg1)s", {"arg1": type(reassembledEvent)}) self.onBitmap(reassembledEvent) else: log.debug("Can't handle output event: %(arg1)s", {"arg1": type(reassembledEvent)}) else: log.debug("Reassembling output event...") def onInput(self, pdu: PlayerMessagePDU): pdu = self.inputParser.parse(pdu.payload) for event in pdu.events: if isinstance(event, FastPathScanCodeEvent): log.debug("handling %(arg1)s", {"arg1": event}) self.onScanCode(event.scancode, not event.isReleased) elif isinstance(event, FastPathMouseEvent): self.onMousePosition(event.mouseX, event.mouseY) else: log.debug("Can't handle input event: %(arg1)s", {"arg1": event}) def onScanCode(self, code: int, isPressed: bool): """ Handle scan code. """ log.debug("Reading scancode %(arg1)s", {"arg1": code}) if code in [0x2A, 0x36]: self.text.moveCursor(QTextCursor.End) self.text.insertPlainText( "\n<LSHIFT PRESSED>" if isPressed else "\n<LSHIFT RELEASED>") self.writeInCaps = not self.writeInCaps elif code == 0x3A and isPressed: self.text.moveCursor(QTextCursor.End) self.text.insertPlainText("\n<CAPSLOCK>") self.writeInCaps = not self.writeInCaps elif isPressed: char = scancodeToChar(code) self.text.moveCursor(QTextCursor.End) self.text.insertPlainText( char if self.writeInCaps else char.lower()) def onMousePosition(self, x: int, y: int): self.viewer.setMousePosition(x, y) def onBitmap(self, event: FastPathBitmapEvent): parsedEvent = self.outputEventParser.parseBitmapEvent(event) for bitmapData in parsedEvent.bitmapUpdateData: self.handleBitmap(bitmapData) def handleBitmap(self, bitmapData: BitmapUpdateData): image = RDPBitmapToQtImage( bitmapData.width, bitmapData.heigth, bitmapData.bitsPerPixel, bitmapData.flags & BitmapFlags.BITMAP_COMPRESSION != 0, bitmapData.bitmapData) self.viewer.notifyImage(bitmapData.destLeft, bitmapData.destTop, image, bitmapData.destRight - bitmapData.destLeft + 1, bitmapData.destBottom - bitmapData.destTop + 1) def onClientInfo(self, pdu: PlayerMessagePDU): clientInfoPDU = self.clientInfoParser.parse(pdu.payload) self.text.insertPlainText( "USERNAME: {}\nPASSWORD: {}\nDOMAIN: {}\n".format( clientInfoPDU.username.replace("\0", ""), clientInfoPDU.password.replace("\0", ""), clientInfoPDU.domain.replace("\0", ""))) self.text.insertPlainText("--------------------\n") def onSlowPathPDU(self, pdu: PlayerMessagePDU): pdu = self.dataParser.parse(pdu.payload) if isinstance(pdu, ConfirmActivePDU): self.viewer.resize( pdu.parsedCapabilitySets[ CapabilityType.CAPSTYPE_BITMAP].desktopWidth, pdu.parsedCapabilitySets[ CapabilityType.CAPSTYPE_BITMAP].desktopHeight) elif isinstance( pdu, UpdatePDU ) and pdu.updateType == SlowPathUpdateType.SLOWPATH_UPDATETYPE_BITMAP: updates = BitmapParser().parseBitmapUpdateData(pdu.updateData) for bitmap in updates: self.handleBitmap(bitmap) elif isinstance(pdu, InputPDU): for event in pdu.events: if isinstance(event, MouseEvent): self.onMousePosition(event.x, event.y) elif isinstance(event, KeyboardEvent): self.onScanCode( event.keyCode, event.flags & KeyboardFlag.KBDFLAGS_DOWN != 0) def onClipboardData(self, pdu: PlayerMessagePDU): formatDataResponsePDU: FormatDataResponsePDU = self.clipboardParser.parse( pdu.payload) self.text.moveCursor(QTextCursor.End) self.text.insertPlainText("\n=============\n") self.text.insertPlainText("CLIPBOARD DATA: {}".format( decodeUTF16LE(formatDataResponsePDU.requestedFormatData))) self.text.insertPlainText("\n=============\n") def onClientData(self, pdu: PlayerMessagePDU): """ Prints the clientName on the screen """ clientDataPDU = self.clientConnectionParser.parse(pdu.payload) self.text.moveCursor(QTextCursor.End) self.text.insertPlainText("--------------------\n") self.text.insertPlainText( f"HOST: {clientDataPDU.coreData.clientName.strip(chr(0))}\n") def reassembleEvent( self, event: FastPathOutputEvent ) -> Optional[Union[FastPathBitmapEvent, FastPathOutputEvent]]: """ Handles FastPath event reassembly as described in https://msdn.microsoft.com/en-us/library/cc240622.aspx :param event: A potentially segmented fastpath output event :return: a FastPathBitmapEvent if a complete PDU has been reassembled, otherwise None. If the event is not fragmented, returns the original event. """ fragmentationFlag = FastPathFragmentation((event.header & 0b00110000) >> 4) if fragmentationFlag == FastPathFragmentation.FASTPATH_FRAGMENT_SINGLE: return event elif fragmentationFlag == FastPathFragmentation.FASTPATH_FRAGMENT_FIRST: self.buffer = event.payload elif fragmentationFlag == FastPathFragmentation.FASTPATH_FRAGMENT_NEXT: self.buffer += event.payload elif fragmentationFlag == FastPathFragmentation.FASTPATH_FRAGMENT_LAST: self.buffer += event.payload event.payload = self.buffer return self.outputEventParser.parseBitmapEvent(event) return None
class SecurityLayer(Layer): """ Layer for security related traffic. """ def __init__(self, parser: BasicSecurityParser): """ :param parser: the parser to use for security traffic. :type parser: Parser """ Layer.__init__(self, parser, hasNext=True) self.mainParser = parser self.clientInfoParser = ClientInfoParser() @staticmethod def create(encryptionMethod, crypter): """ Create a security layer using the chosen encryption method and crypter. :type encryptionMethod: EncryptionMethod :type crypter: RC4Crypter | RC4CrypterProxy :return: RDPSecurityLayer """ if encryptionMethod in [EncryptionMethod.ENCRYPTION_40BIT, EncryptionMethod.ENCRYPTION_56BIT, EncryptionMethod.ENCRYPTION_128BIT]: parser = SignedSecurityParser(crypter) return SecurityLayer(parser) elif encryptionMethod == EncryptionMethod.ENCRYPTION_FIPS: parser = FIPSSecurityParser(crypter) return SecurityLayer(parser) def recv(self, data): pdu = self.mainParser.parse(data) try: self.dispatchPDU(pdu) except KeyboardInterrupt: raise except Exception: if isinstance(pdu, SecurityExchangePDU): log.error("Exception occurred when receiving Security Exchange. Data: %(securityExchangeData)s", {"securityExchangeData": hexlify(data)}) else: log.error("Exception occurred when receiving: %(data)s", {"data": hexlify(pdu.payload).decode()}) raise def dispatchPDU(self, pdu): """ Send the PDU to the proper object depending on its type. :param pdu: the pdu. :type pdu: PDU. """ if pdu.header & SecurityFlags.SEC_EXCHANGE_PKT != 0: if self.observer: self.observer.onSecurityExchangeReceived(pdu) elif pdu.header & SecurityFlags.SEC_INFO_PKT != 0: if self.observer: self.observer.onClientInfoReceived(pdu.payload) elif pdu.header & SecurityFlags.SEC_LICENSE_PKT != 0: if self.observer: self.observer.onLicensingDataReceived(pdu.payload) else: self.pduReceived(pdu, self.hasNext) def send(self, data: bytes, header=0): pdu = SecurityPDU(header, data) data = self.mainParser.write(pdu) self.previous.send(data) def sendSecurityExchange(self, clientRandom): """ Send a security exchange PDU through the layer. :param clientRandom: the client random data. :type clientRandom: bytes """ pdu = SecurityExchangePDU(SecurityFlags.SEC_EXCHANGE_PKT, clientRandom + b"\x00" * 8) data = self.mainParser.writeSecurityExchange(pdu) self.previous.send(data) def sendClientInfo(self, pdu): """ Send a client info PDU. :type pdu: ClientInfoPDU """ data = self.clientInfoParser.write(pdu) pdu = SecurityPDU(SecurityFlags.SEC_INFO_PKT, data) data = self.mainParser.write(pdu) self.previous.send(data) def sendLicensing(self, data): """ Send raw licensing data. :type data: bytes """ pdu = SecurityPDU(SecurityFlags.SEC_LICENSE_PKT, data) self.previous.send(self.mainParser.write(pdu))
def onClientInfoReceived(self, data: bytes): pdu = ClientInfoParser().parse(data) self.logPDU(pdu)
def __init__(self, parser: BasicSecurityParser): """ :param parser: the parser to use for security traffic. """ super().__init__(parser) self.clientInfoParser = ClientInfoParser()