GoSecure / pyrdp

RDP monster-in-the-middle (mitm) and library for Python with the ability to watch connections live or after the fact
https://www.gosecure.net/blog/2020/10/20/announcing-pyrdp-1/
GNU General Public License v3.0
1.5k stars 242 forks source link

Keep clipboard file transfers in replay files #442

Closed obilodeau closed 10 months ago

obilodeau commented 1 year ago

Right now our replay files don't have a copy of the clipboard files. I think it should be stored or at least the event recorded.

I started but can't finish right now due to other priorities. Started when doing #441. Here's a patch of where my debugging was:

diff --git a/pyrdp/convert/JSONEventHandler.py b/pyrdp/convert/JSONEventHandler.py
index 1968e4d..9227cfc 100644
--- a/pyrdp/convert/JSONEventHandler.py
+++ b/pyrdp/convert/JSONEventHandler.py
@@ -118,7 +118,9 @@ class JSONEventHandler(BaseEventHandler):
         parser = ClipboardParser()
         pdu = parser.parse(pdu.payload)

+        import ipdb; ipdb.set_trace()
         if not isinstance(pdu, FormatDataResponsePDU):
+            import ipdb; ipdb.set_trace()
             # TODO: Handle file PDUs.
             return

diff --git a/pyrdp/mitm/ClipboardMITM.py b/pyrdp/mitm/ClipboardMITM.py
index 3e00bdf..f9f596a 100644
--- a/pyrdp/mitm/ClipboardMITM.py
+++ b/pyrdp/mitm/ClipboardMITM.py
@@ -175,15 +175,21 @@ class PassiveClipboardStealer:

     def onFormatDataResponse(self, pdu: FormatDataResponsePDU):
         if pdu.msgFlags == ClipboardMessageFlags.CB_RESPONSE_OK:
+            self.log.info("onFormatDataResponse with CB_RESPONSE_OK. PDU: %(pdu)r", {"pdu": pdu})
             # Keep the file list if there is one.
             # FIXME: There is currently no concept of transfer direction.
             if len(pdu.files) > 0:
+                self.log.info("-> XXX hit on files")
                 self.files = pdu.files
+                import ipdb; ipdb.set_trace()
+                self.recorder.record(pdu, PlayerPDUType.CLIPBOARD_DATA)
+                #import ipdb; ipdb.set_trace()

             if pdu.formatId == ClipboardFormatNumber.GENERIC.value:
                 clipboardData = self.decodeClipboardData(pdu.requestedFormatData)
                 self.log.info("Clipboard data: %(clipboardData)r", {"clipboardData": clipboardData})
                 # FIXME: Record all clipboard related messages?
+                self.log.info("-> XXX hit on record")
                 self.recorder.record(pdu, PlayerPDUType.CLIPBOARD_DATA)

             if self.forwardNextDataResponse:
diff --git a/pyrdp/parser/rdp/virtual_channel/clipboard.py b/pyrdp/parser/rdp/virtual_channel/clipboard.py
index 18db8e1..e344bd8 100644
--- a/pyrdp/parser/rdp/virtual_channel/clipboard.py
+++ b/pyrdp/parser/rdp/virtual_channel/clipboard.py
@@ -133,6 +133,7 @@ class ClipboardParser(Parser):
         """
         Uint32LE.pack(len(pdu.requestedFormatData), stream)
         stream.write(pdu.requestedFormatData)
+        self.req = None

     def writeFormatList(self, stream, pdu):
         """
@@ -162,6 +163,7 @@ class ClipboardParser(Parser):
         :type stream: BytesIO
         :type pdu: FormatDataRequestPDU
         """
+        self.req = pdu
         Uint32LE.pack(4, stream)  # datalen
         Uint32LE.pack(pdu.requestedFormatId, stream)

diff --git a/pyrdp/player/BaseEventHandler.py b/pyrdp/player/BaseEventHandler.py
index 9786e54..5bf0dc9 100644
--- a/pyrdp/player/BaseEventHandler.py
+++ b/pyrdp/player/BaseEventHandler.py
@@ -105,6 +105,7 @@ class BaseEventHandler(Observer):
         parser = ClipboardParser()
         pdu = parser.parse(pdu.payload)

+        import ipdb; ipdb.set_trace()
         if not isinstance(pdu, FormatDataResponsePDU):
             return
obilodeau commented 10 months ago

After looking at many captures, I am no longer convinced we should keep clipboard files in the replay file. It would bloat the file drastically and reduce deduplication opportunities. Closing this.