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

Add support for Enhanced Security Direct Connection #212

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
32 changes: 27 additions & 5 deletions pyrdp/layer/tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@
from twisted.internet.protocol import connectionDone, Protocol

from pyrdp.core import ObservedBy
from pyrdp.core.ssl import ServerTLSContext
from pyrdp.layer.layer import IntermediateLayer, LayerObserver
from pyrdp.logging import LOGGER_NAMES, getSSLLogger
from pyrdp.parser.tcp import TCPParser
from pyrdp.pdu import PDU


TLS_RECORD = 0x16


class TCPObserver(LayerObserver):
def onConnection(self):
"""
Expand All @@ -41,17 +45,21 @@ class TwistedTCPLayer(IntermediateLayer, Protocol):
TCP observers are notified when a connection is made.
"""

def __init__(self):
def __init__(self, config):
self.log = logging.getLogger(LOGGER_NAMES.PYRDP)
super().__init__(TCPParser())
self.connectedEvent = asyncio.Event()
self.logSSLRequired = False

self.new = True
self.config = config

def logSSLParameters(self):
"""
Log the SSL parameters of the connection in a format suitable for decryption by Wireshark.
"""
getSSLLogger().info(self.transport.protocol._tlsConnection.client_random(), self.transport.protocol._tlsConnection.master_key())
getSSLLogger().info(self.transport.protocol._tlsConnection.client_random(),
self.transport.protocol._tlsConnection.master_key())

def connectionMade(self):
"""
Expand All @@ -66,7 +74,7 @@ def connectionLost(self, reason=connectionDone):
"""
self.observer.onDisconnection(reason)

def disconnect(self, abort = False):
def disconnect(self, abort=False):
"""
Close the TCP connection.
:param abort: True to force close the connection, False to end gracefully.
Expand All @@ -83,6 +91,20 @@ def dataReceived(self, data: bytes):
Called whenever data is received.
:param data: bytes received.
"""

# Check if the client is sending us a TLS record immediately.
# and start the TLS handshake early.
if self.new and data[0] == TLS_RECORD:
self.startTLS(ServerTLSContext(self.config.privateKeyFileName,
self.config.certificateFileName))
# Resend the ClientHello to Twisted for handshake processing.
# WARNING: This is using a private Twisted API which could change in the future.
self.transport._dataReceived(data)
self.new = False
return

self.new = False # First packet was not a TLS record.

try:
if self.logSSLRequired:
self.logSSLParameters()
Expand All @@ -93,7 +115,7 @@ def dataReceived(self, data: bytes):
raise
except Exception as e:
self.log.exception(e)
self.log.error("Exception occurred when receiving: %(data)s" , {"data": hexlify(data).decode()})
self.log.error("Exception occurred when receiving: %(data)s", {"data": hexlify(data).decode()})
raise

def sendBytes(self, data: bytes):
Expand Down Expand Up @@ -143,7 +165,7 @@ def connection_lost(self, exception=connectionDone):
"""
self.observer.onDisconnection(exception)

def disconnect(self, abort = False):
def disconnect(self, abort=False):
"""
Close the TCP connection.
:param abort: True to force close the connection, False to end gracefully.
Expand Down
9 changes: 5 additions & 4 deletions pyrdp/mitm/PlayerLayerSet.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
#
# This file is part of the PyRDP project.
# Copyright (C) 2019 GoSecure Inc.
# Copyright (C) 2019-2020 GoSecure Inc.
# Licensed under the GPLv3 or later.
#

from pyrdp.layer import AsyncIOTCPLayer, LayerChainItem, PlayerLayer, TwistedTCPLayer
from pyrdp.mitm import MITMConfig


class TwistedPlayerLayerSet:
def __init__(self):
self.tcp = TwistedTCPLayer()
def __init__(self, config: MITMConfig):
self.tcp = TwistedTCPLayer(config)
self.player = PlayerLayer()
LayerChainItem.chain(self.tcp, self.player)

Expand All @@ -18,4 +19,4 @@ class AsyncIOPlayerLayerSet:
def __init__(self):
self.tcp = AsyncIOTCPLayer()
self.player = PlayerLayer()
LayerChainItem.chain(self.tcp, self.player)
LayerChainItem.chain(self.tcp, self.player)
8 changes: 4 additions & 4 deletions pyrdp/mitm/RDPMITM.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#
# This file is part of the PyRDP project.
# Copyright (C) 2019 GoSecure Inc.
# Copyright (C) 2019-2020 GoSecure Inc.
# Licensed under the GPLv3 or later.
#

Expand Down Expand Up @@ -76,13 +76,13 @@ def __init__(self, mainLogger: SessionLogger, crawlerLogger: SessionLogger, conf
self.state = state if state is not None else RDPMITMState(self.config)
"""The MITM state"""

self.client = RDPLayerSet()
self.client = RDPLayerSet(self.config)
"""Layers on the client side"""

self.server = RDPLayerSet()
self.server = RDPLayerSet(self.config)
"""Layers on the server side"""

self.player = TwistedPlayerLayerSet()
self.player = TwistedPlayerLayerSet(self.config)
"""Layers on the attacker side"""

self.recorder = recorder if recorder is not None else MITMRecorder([], self.state)
Expand Down
7 changes: 4 additions & 3 deletions pyrdp/mitm/layerset.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
#
# This file is part of the PyRDP project.
# Copyright (C) 2019 GoSecure Inc.
# Copyright (C) 2019-2020 GoSecure Inc.
# Licensed under the GPLv3 or later.
#

from pyrdp.enum import SegmentationPDUType
from pyrdp.layer import FastPathLayer, LayerChainItem, MCSLayer, SecurityLayer, SegmentationLayer, SlowPathLayer, \
TPKTLayer, TwistedTCPLayer, X224Layer
# from pyrdp.mitm.config import MITMConfig


class RDPLayerSet:
"""
Class that handles initialization of regular (non-virtual channel) RDP layers.
"""

def __init__(self):
self.tcp = TwistedTCPLayer()
def __init__(self, config):
self.tcp = TwistedTCPLayer(config)
self.segmentation = SegmentationLayer()
self.tpkt = TPKTLayer()
self.x224 = X224Layer()
Expand Down