Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Log cred attempts when NLA is not used #303

Merged
merged 3 commits into from
Mar 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions pyrdp/mitm/BasePathMITM.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
# Copyright (C) 2019-2020 GoSecure Inc.
obilodeau marked this conversation as resolved.
Show resolved Hide resolved
# 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
Expand All @@ -16,18 +17,38 @@ 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")

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.
Expand All @@ -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 += "<ctrl-a>"
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:
Expand Down
9 changes: 6 additions & 3 deletions pyrdp/mitm/FastPathMITM.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
# Copyright (C) 2019 GoSecure Inc.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copyright bump

# 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

Expand All @@ -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,
Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions pyrdp/mitm/MCSMITM.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions pyrdp/mitm/RDPMITM.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
9 changes: 6 additions & 3 deletions pyrdp/mitm/SlowPathMITM.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -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,
Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions pyrdp/mitm/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -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])

Expand Down