diff --git a/pyrdp/layer/tcp.py b/pyrdp/layer/tcp.py index 154dfcf29..172a8f403 100644 --- a/pyrdp/layer/tcp.py +++ b/pyrdp/layer/tcp.py @@ -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): """ @@ -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): """ @@ -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. @@ -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() @@ -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): @@ -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. diff --git a/pyrdp/mitm/PlayerLayerSet.py b/pyrdp/mitm/PlayerLayerSet.py index a7587b638..16c4cac14 100644 --- a/pyrdp/mitm/PlayerLayerSet.py +++ b/pyrdp/mitm/PlayerLayerSet.py @@ -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) @@ -18,4 +19,4 @@ class AsyncIOPlayerLayerSet: def __init__(self): self.tcp = AsyncIOTCPLayer() self.player = PlayerLayer() - LayerChainItem.chain(self.tcp, self.player) \ No newline at end of file + LayerChainItem.chain(self.tcp, self.player) diff --git a/pyrdp/mitm/RDPMITM.py b/pyrdp/mitm/RDPMITM.py index 54b7e0ae9..9fcb9f9cd 100644 --- a/pyrdp/mitm/RDPMITM.py +++ b/pyrdp/mitm/RDPMITM.py @@ -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. # @@ -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) diff --git a/pyrdp/mitm/layerset.py b/pyrdp/mitm/layerset.py index e4ba7640a..bc3409b59 100644 --- a/pyrdp/mitm/layerset.py +++ b/pyrdp/mitm/layerset.py @@ -1,12 +1,13 @@ # # 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: @@ -14,8 +15,8 @@ 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()