Skip to content

Commit

Permalink
refactor(#94): Changed timing of StartTLS.
Browse files Browse the repository at this point in the history
  • Loading branch information
alxbl committed Aug 21, 2020
1 parent 240c023 commit e434b7f
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 13 deletions.
52 changes: 42 additions & 10 deletions pyrdp/mitm/RDPMITM.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#
# 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.
#

import asyncio
import datetime
import typing

from twisted.internet import reactor
from twisted.internet.protocol import Protocol
Expand Down Expand Up @@ -137,9 +138,11 @@ def __init__(self, mainLogger: SessionLogger, crawlerLogger: SessionLogger, conf

self.player.player.addObserver(LayerLogger(self.attackerLog))

self.config.outDir.mkdir(parents=True, exist_ok=True)
self.config.replayDir.mkdir(exist_ok=True)
self.config.fileDir.mkdir(exist_ok=True)
self.ensureOutDir()
# if config.certificateFileName == "auto":
# self.certCache: CertificateCache = CertificateCache(self.config.certDir)
# else:
# self.certCache = None

self.state.securitySettings.addObserver(RC4LoggingObserver(self.rc4Log))

Expand Down Expand Up @@ -213,17 +216,40 @@ async def connectToServer(self):
except asyncio.TimeoutError:
self.log.error("Failed to connect to recording host: timeout expired")

def startTLS(self):
def doClientTls(self):
cert = self.server.tcp.transport.getPeerCertificate()
if not cert:
# Wait for server certificate
reactor.callLater(1, self.doClientTls)

# Clone certificate if necessary.
if self.certs:
contextForClient = ServerTLSContext(self.config.privateKeyFileName, self.config.certificateFileName)
else:
# No automated certificate cloning. Use the specified certificate.
contextForClient = ServerTLSContext(self.config.privateKeyFileName, self.config.certificateFileName)

# Establish TLS tunnel with the client
self.onTlsReady()
self.client.tcp.startTLS(contextForClient)
self.onTlsReady = None

# Add unknown packet handlers.
self.client.segmentation.addObserver(PacketForwarder(self.server.tcp))
self.server.segmentation.addObserver(PacketForwarder(self.client.tcp))

def startTLS(self, onTlsReady: typing.Callable[[], None]):
"""
Execute a startTLS on both the client and server side.
"""
contextForClient = ServerTLSContext(self.config.privateKeyFileName, self.config.certificateFileName)
contextForServer = ClientTLSContext()
self.onTlsReady = onTlsReady

self.client.tcp.startTLS(contextForClient)
# Establish TLS tunnel with target server...
contextForServer = ClientTLSContext()
self.server.tcp.startTLS(contextForServer)
self.client.segmentation.addObserver(PacketForwarder(self.server.tcp))
self.server.segmentation.addObserver(PacketForwarder(self.client.tcp))

# Establish TLS tunnel with client.
reactor.callLater(1, self.doClientTls)

def buildChannel(self, client: MCSServerChannel, server: MCSClientChannel):
"""
Expand Down Expand Up @@ -420,3 +446,9 @@ def enableForwarding():
enableForwarding
])
sequencer.run()

def ensureOutDir(self):
self.config.outDir.mkdir(parents=True, exist_ok=True)
self.config.replayDir.mkdir(exist_ok=True)
self.config.fileDir.mkdir(exist_ok=True)
self.config.certDir.mkdir(exist_ok=True)
9 changes: 6 additions & 3 deletions pyrdp/mitm/X224MITM.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@


class X224MITM:
def __init__(self, client: X224Layer, server: X224Layer, log: LoggerAdapter, state: RDPMITMState, connector: typing.Coroutine, startTLSCallback: typing.Callable[[], None]):
def __init__(self, client: X224Layer, server: X224Layer, log: LoggerAdapter, state: RDPMITMState, connector: typing.Coroutine, startTLSCallback: typing.Callable[[typing.Callable[[], None]], None]):
"""
:param client: X224 layer for the client side
Expand Down Expand Up @@ -112,13 +112,16 @@ def onConnectionConfirm(self, pdu: X224ConnectionConfirmPDU):
payload = pdu.payload
else:
payload = parser.write(NegotiationResponsePDU(NegotiationType.TYPE_RDP_NEG_RSP, 0x00, response.selectedProtocols))
self.client.sendConnectionConfirm(payload, source=0x1234)

# FIXME: This should be done based on what authentication method the server selected, not on what
# the client supports.
if self.originalRequest.tlsSupported:
self.startTLSCallback()
# If a TLS tunnel is requested, then we establish the server-side tunnel before
# replying to the client, so that we can clone the certificate if needed.
self.startTLSCallback(lambda: self.client.sendConnectionConfirm(payload, source=0x1234))
self.state.useTLS = True
else:
self.client.sendConnectionConfirm(payload, source=0x1234)

def onClientDisconnectRequest(self, pdu: X224DisconnectRequestPDU):
self.server.sendPDU(pdu)
Expand Down
7 changes: 7 additions & 0 deletions pyrdp/mitm/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ def fileDir(self) -> Path:
"""
return self.outDir / "files"

@property
def certDir(self) -> Path:
"""
Get the directory for dynamically generated certificates.
"""
return self.outDir / "certs"


"""
The default MITM configuration.
Expand Down

0 comments on commit e434b7f

Please sign in to comment.