From 9dcac862ff66ee1a536d199dc234b359c40ea771 Mon Sep 17 00:00:00 2001 From: speyrefitte Date: Wed, 14 Jan 2015 15:49:00 +0100 Subject: [PATCH] finish honeypot tools --- README.md | 4 +- bin/rdpy-rdpclient.py | 93 +++++++++++++++++++++++++++++++++++------ bin/rdpy-rdphoneypot.py | 40 +++++++++++++----- bin/rdpy-rdpmitm.py | 14 +++++-- bin/rdpy-rssplayer.py | 36 +++++++++------- rdpy/core/rss.py | 23 ++++++++-- rdpy/ui/qt4.py | 26 ++++++------ 7 files changed, 174 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 949193f..f1283f2 100644 --- a/README.md +++ b/README.md @@ -61,9 +61,11 @@ RDPY comes with some very useful binaries; These binaries are linux and windows rdpy-rdpclient is a simple RDP Qt4 client . ``` -$ rdpy-rdpclient.py [-u username] [-p password] [-d domain] [...] XXX.XXX.XXX.XXX[:3389] +$ rdpy-rdpclient.py [-u username] [-p password] [-d domain] [-r rss_ouput_file] [...] XXX.XXX.XXX.XXX[:3389] ``` +You can use rdpy-rdpclient as Recorder Session Scenarion, use in rdpy-rdphoneypot. + ### rdpy-vncclient rdpy-vncclient is a simple VNC Qt4 client . diff --git a/bin/rdpy-rdpclient.py b/bin/rdpy-rdpclient.py index 5f37ed0..8f5b8f1 100755 --- a/bin/rdpy-rdpclient.py +++ b/bin/rdpy-rdpclient.py @@ -27,15 +27,71 @@ from PyQt4 import QtGui, QtCore from rdpy.ui.qt4 import RDPClientQt from rdpy.protocol.rdp import rdp from rdpy.core.error import RDPSecurityNegoFail +from rdpy.core import rss import rdpy.core.log as log log._LOG_LEVEL = log.Level.INFO + +class RDPClientQtRecorder(RDPClientQt): + """ + @summary: Widget with record session + """ + def __init__(self, controller, width, height, rssRecorder): + """ + @param controller: {RDPClientController} RDP controller + @param width: {int} width of widget + @param height: {int} height of widget + @param rssRecorder: {rss.FileRecorder} + """ + RDPClientQt.__init__(self, controller, width, height) + self._screensize = width, height + self._rssRecorder = rssRecorder + + def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): + """ + @summary: Notify bitmap update + @param destLeft: {int} xmin position + @param destTop: {int} ymin position + @param destRight: {int} xmax position because RDP can send bitmap with padding + @param destBottom: {int} ymax position because RDP can send bitmap with padding + @param width: {int} width of bitmap + @param height: {int} height of bitmap + @param bitsPerPixel: {int} number of bit per pixel + @param isCompress: {bool} use RLE compression + @param data: {str} bitmap data + """ + #record update + self._rssRecorder.update(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, rss.UpdateFormat.BMP if isCompress else rss.UpdateFormat.RAW, data) + RDPClientQt.onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data) + + def onReady(self): + """ + @summary: Call when stack is ready + """ + self._rssRecorder.screen(self._screensize[0], self._screensize[1], self._controller.getColorDepth()) + RDPClientQt.onReady(self) + + def onClose(self): + """ + @summary: Call when stack is close + """ + self._rssRecorder.close() + RDPClientQt.onClose(self) + + def closeEvent(self, e): + """ + @summary: Convert Qt close widget event into close stack event + @param e: QCloseEvent + """ + self._rssRecorder.close() + RDPClientQt.closeEvent(self, e) + class RDPClientQtFactory(rdp.ClientFactory): """ @summary: Factory create a RDP GUI client """ - def __init__(self, width, height, username, password, domain, fullscreen, keyboardLayout, optimized, security): + def __init__(self, width, height, username, password, domain, fullscreen, keyboardLayout, optimized, security, recodedPath): """ @param width: {integer} width of client @param heigth: {integer} heigth of client @@ -46,6 +102,7 @@ class RDPClientQtFactory(rdp.ClientFactory): @param keyboardLayout: {str} (fr|en) keyboard layout @param optimized: {bool} enable optimized session orders @param security: {str} (ssl | rdp | nego) + @param recodedPath: {str | None} Rss file Path """ self._width = width self._height = height @@ -56,6 +113,7 @@ class RDPClientQtFactory(rdp.ClientFactory): self._keyboardLayout = keyboardLayout self._optimized = optimized self._nego = security == "nego" + self._recodedPath = recodedPath if self._nego: self._security = "ssl" else: @@ -71,7 +129,10 @@ class RDPClientQtFactory(rdp.ClientFactory): @return: RDPClientQt """ #create client observer - self._client = RDPClientQt(controller, self._width, self._height) + if self._recodedPath is None: + self._client = RDPClientQt(controller, self._width, self._height) + else: + self._client = RDPClientQtRecorder(controller, self._width, self._height, rss.createRecorder(self._recodedPath)) #create qt widget self._w = self._client.getWidget() self._w.setWindowTitle('rdpy-rdpclient') @@ -146,15 +207,18 @@ def autoDetectKeyboardLayout(): return "en" def help(): - print "Usage: rdpy-rdpclient [options] ip[:port]" - print "\t-u: user name" - print "\t-p: password" - print "\t-d: domain" - print "\t-w: width of screen [default : 1024]" - print "\t-l: height of screen [default : 800]" - print "\t-f: enable full screen mode [default : False]" - print "\t-k: keyboard layout [en|fr] [default : en]" - print "\t-o: optimized session (disable costly effect) [default : False]" + print """ + Usage: rdpy-rdpclient [options] ip[:port]" + \t-u: user name + \t-p: password + \t-d: domain + \t-w: width of screen [default : 1024] + \t-l: height of screen [default : 800] + \t-f: enable full screen mode [default : False] + \t-k: keyboard layout [en|fr] [default : en] + \t-o: optimized session (disable costly effect) [default : False] + \t-r: rss_filepath Recorded Session Scenario [default : None] + """ if __name__ == '__main__': @@ -166,10 +230,11 @@ if __name__ == '__main__': height = 800 fullscreen = False optimized = False + recodedPath = None keyboardLayout = autoDetectKeyboardLayout() try: - opts, args = getopt.getopt(sys.argv[1:], "hfou:p:d:w:l:k:") + opts, args = getopt.getopt(sys.argv[1:], "hfou:p:d:w:l:k:r:") except getopt.GetoptError: help() for opt, arg in opts: @@ -192,6 +257,8 @@ if __name__ == '__main__': optimized = True elif opt == "-k": keyboardLayout = arg + elif opt == "-r": + recodedPath = arg if ':' in args[0]: ip, port = args[0].split(':') @@ -212,6 +279,6 @@ if __name__ == '__main__': log.info("keyboard layout set to %s"%keyboardLayout) from twisted.internet import reactor - reactor.connectTCP(ip, int(port), RDPClientQtFactory(width, height, username, password, domain, fullscreen, keyboardLayout, optimized, "nego")) + reactor.connectTCP(ip, int(port), RDPClientQtFactory(width, height, username, password, domain, fullscreen, keyboardLayout, optimized, "nego", recodedPath)) reactor.runReturn() app.exec_() \ No newline at end of file diff --git a/bin/rdpy-rdphoneypot.py b/bin/rdpy-rdphoneypot.py index 64b3f58..e4639fc 100755 --- a/bin/rdpy-rdphoneypot.py +++ b/bin/rdpy-rdphoneypot.py @@ -37,6 +37,7 @@ class HoneyPotServer(rdp.RDPServerObserver): """ rdp.RDPServerObserver.__init__(self, controller) self._rssFile = rssFile + self._dx, self._dy = 0, 0 def onReady(self): """ @@ -46,7 +47,15 @@ class HoneyPotServer(rdp.RDPServerObserver): restart a connection sequence @see: rdp.RDPServerObserver.onReady """ - self.loopScenario() + domain, username, password = self._controller.getCredentials() + hostname = self._controller.getHostname() + log.info("""Credentials: + \tdomain : %s + \tusername : %s + \tpassword : %s + \thostname : %s + """%(domain, username, password, hostname)); + self.start() def onClose(self): """ HoneyPot """ @@ -60,22 +69,32 @@ class HoneyPotServer(rdp.RDPServerObserver): def onPointerEvent(self, x, y, button, isPressed): """ HoneyPot """ - def loopScenario(self): + def start(self): + self.loopScenario(self._rssFile.nextEvent()) + + def loopScenario(self, nextEvent): """ @summary: main loop event """ - e = self._rssFile.nextEvent() - if e is None: + if nextEvent.type.value == rss.EventType.UPDATE: + self._controller.sendUpdate(nextEvent.event.destLeft.value + self._dx, nextEvent.event.destTop.value + self._dy, nextEvent.event.destRight.value + self._dx, nextEvent.event.destBottom.value + self._dy, nextEvent.event.width.value, nextEvent.event.height.value, nextEvent.event.bpp.value, nextEvent.event.format.value == rss.UpdateFormat.BMP, nextEvent.event.data.value) + + elif nextEvent.type.value == rss.EventType.CLOSE: self._controller.close() return - - if e.type.value == rss.EventType.UPDATE: - self._controller.sendUpdate(e.event.destLeft.value, e.event.destTop.value, e.event.destRight.value, e.event.destBottom.value, e.event.width.value, e.event.height.value, e.event.bpp.value, e.event.format.value == rss.UpdateFormat.BMP, e.event.data.value) - elif e.type.value == rss.EventType.SCREEN: - self._controller.setColorDepth(e.event.colorDepth.value) + + elif nextEvent.type.value == rss.EventType.SCREEN: + self._controller.setColorDepth(nextEvent.event.colorDepth.value) + #compute centering because we cannot resize client + clientSize = nextEvent.event.width.value, nextEvent.event.height.value + serverSize = self._controller.getScreen() + + self._dx, self._dy = (serverSize[0] - clientSize[0]) / 2, (serverSize[1] - clientSize[1]) / 2 #restart connection sequence return - reactor.callLater(float(e.timestamp.value) / 1000.0, self.loopScenario) + + e = self._rssFile.nextEvent() + reactor.callLater(float(e.timestamp.value) / 1000.0, lambda:self.loopScenario(e)) class HoneyPotServerFactory(rdp.ServerFactory): """ @@ -96,6 +115,7 @@ class HoneyPotServerFactory(rdp.ServerFactory): @param addr: destination address @see: rdp.ServerFactory.buildObserver """ + log.info("Connection from %s:%s"%(addr.host, addr.port)) return HoneyPotServer(controller, rss.createReader(self._rssFilePath)) def help(): diff --git a/bin/rdpy-rdpmitm.py b/bin/rdpy-rdpmitm.py index 10f4471..b33722b 100755 --- a/bin/rdpy-rdpmitm.py +++ b/bin/rdpy-rdpmitm.py @@ -35,7 +35,7 @@ from rdpy.core import log, error, rss from rdpy.protocol.rdp import rdp from twisted.internet import reactor -log._LOG_LEVEL = log.Level.INFO +log._LOG_LEVEL = log.Level.DEBUG class ProxyServer(rdp.RDPServerObserver): """ @@ -71,10 +71,10 @@ class ProxyServer(rdp.RDPServerObserver): if self._client is None: #try a connection domain, username, password = self._controller.getCredentials() - self._rss.recInfo(username, password, domain, self._controller.getHostname()) + self._rss.credentials(username, password, domain, self._controller.getHostname()) width, height = self._controller.getScreen() - self._rss.recScreen(width, height, self._controller.getColorDepth()) + self._rss.screen(width, height, self._controller.getColorDepth()) reactor.connectTCP(self._target[0], int(self._target[1]), ProxyClientFactory(self, width, height, domain, username, password,self._clientSecurityLevel)) @@ -84,6 +84,10 @@ class ProxyServer(rdp.RDPServerObserver): @summary: Call when human client close connection @see: rdp.RDPServerObserver.onClose """ + #end scenario + self._rss.close() + + #close network stack if self._client is None: return self._client._controller.close() @@ -177,6 +181,8 @@ class ProxyClient(rdp.RDPClientObserver): @summary: Event inform that stack is close @see: rdp.RDPClientObserver.onClose """ + #end scenario + self._server._rss.close() self._server._controller.close() def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): @@ -193,7 +199,7 @@ class ProxyClient(rdp.RDPClientObserver): @param data: {str} bitmap data @see: rdp.RDPClientObserver.onUpdate """ - self._server._rss.recUpdate(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, rss.UpdateFormat.BMP if isCompress else rss.UpdateFormat.RAW, data) + self._server._rss.update(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, rss.UpdateFormat.BMP if isCompress else rss.UpdateFormat.RAW, data) self._server._controller.sendUpdate(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data) class ProxyClientFactory(rdp.ClientFactory): diff --git a/bin/rdpy-rssplayer.py b/bin/rdpy-rssplayer.py index d7b59e2..09daddb 100755 --- a/bin/rdpy-rssplayer.py +++ b/bin/rdpy-rssplayer.py @@ -51,29 +51,33 @@ class RssPlayerWidget(QRemoteDesktop): def help(): print "Usage: rdpy-rssplayer [-h] rss_filepath" - -def loop(widget, rssFile): + +def start(widget, rssFile): + loop(widget, rssFile, rssFile.nextEvent()) + +def loop(widget, rssFile, nextEvent): """ @summary: timer function @param widget: {QRemoteDesktop} @param rssFile: {rss.FileReader} """ - e = rssFile.nextEvent() - if e is None: + + if nextEvent.type.value == rss.EventType.UPDATE: + image = RDPBitmapToQtImage(nextEvent.event.width.value, nextEvent.event.height.value, nextEvent.event.bpp.value, nextEvent.event.format.value == rss.UpdateFormat.BMP, nextEvent.event.data.value); + widget.notifyImage(nextEvent.event.destLeft.value, nextEvent.event.destTop.value, image, nextEvent.event.destRight.value - nextEvent.event.destLeft.value + 1, nextEvent.event.destBottom.value - nextEvent.event.destTop.value + 1) + + elif nextEvent.type.value == rss.EventType.SCREEN: + widget.resize(nextEvent.event.width.value, nextEvent.event.height.value) + + elif nextEvent.type.value == rss.EventType.INFO: + widget.drawInfos(nextEvent.event.domain.value, nextEvent.event.username.value, nextEvent.event.password.value, nextEvent.event.hostname.value) + + elif nextEvent.type.value == rss.EventType.CLOSE: widget.close() return - if e.type.value == rss.EventType.UPDATE: - image = RDPBitmapToQtImage(e.event.width.value, e.event.height.value, e.event.bpp.value, e.event.format.value == rss.UpdateFormat.BMP, e.event.data.value); - widget.notifyImage(e.event.destLeft.value, e.event.destTop.value, image, e.event.destRight.value - e.event.destLeft.value + 1, e.event.destBottom.value - e.event.destTop.value + 1) - - elif e.type.value == rss.EventType.SCREEN: - widget.resize(e.event.width.value, e.event.height.value) - - elif e.type.value == rss.EventType.INFO: - widget.drawInfos(e.event.domain.value, e.event.username.value, e.event.password.value, e.event.hostname.value) - - QtCore.QTimer.singleShot(e.timestamp.value,lambda:loop(widget, rssFile)) + e = rssFile.nextEvent() + QtCore.QTimer.singleShot(e.timestamp.value,lambda:loop(widget, rssFile, e)) if __name__ == '__main__': try: @@ -91,5 +95,5 @@ if __name__ == '__main__': widget = RssPlayerWidget(800, 600) widget.show() rssFile = rss.createReader(filepath) - loop(widget, rssFile) + start(widget, rssFile) sys.exit(app.exec_()) \ No newline at end of file diff --git a/rdpy/core/rss.py b/rdpy/core/rss.py index 3ed98c1..6e5223a 100644 --- a/rdpy/core/rss.py +++ b/rdpy/core/rss.py @@ -33,6 +33,7 @@ class EventType(object): UPDATE = 0x0001 SCREEN = 0x0002 INFO = 0x0003 + CLOSE = 0x0004 class UpdateFormat(object): """ @@ -55,7 +56,7 @@ class Event(CompositeType): """ @summary: Closure for event factory """ - for c in [UpdateEvent, ScreenEvent, InfoEvent]: + for c in [UpdateEvent, ScreenEvent, InfoEvent, CloseEvent]: if self.type.value == c._TYPE_: return c(readLen = self.length) log.debug("unknown event type : %s"%hex(self.type.value)) @@ -114,6 +115,14 @@ class ScreenEvent(CompositeType): self.height = UInt16Le() self.colorDepth = UInt8() +class CloseEvent(CompositeType): + """ + @summary: end of session event + """ + _TYPE_ = EventType.CLOSE + def __init__(self, readLen = None): + CompositeType.__init__(self, readLen = readLen) + def timeMs(): """ @return: {int} time stamp in milliseconds @@ -150,7 +159,7 @@ class FileRecorder(object): self._file.write(s.getvalue()) - def recUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bpp, upateFormat, data): + def update(self, destLeft, destTop, destRight, destBottom, width, height, bpp, upateFormat, data): """ @summary: record update event @param destLeft: {int} xmin position @@ -175,7 +184,7 @@ class FileRecorder(object): updateEvent.data.value = data self.rec(updateEvent) - def recScreen(self, width, height, colorDepth): + def screen(self, width, height, colorDepth): """ @summary: record resize event of screen (maybe first event) @param width: {int} width of screen @@ -188,7 +197,7 @@ class FileRecorder(object): screenEvent.colorDepth.value = colorDepth self.rec(screenEvent) - def recInfo(self, username, password, domain = "", hostname = ""): + def credentials(self, username, password, domain = "", hostname = ""): """ @summary: Record informations event @param username: {str} username of session @@ -202,6 +211,12 @@ class FileRecorder(object): infoEvent.domain.value = domain infoEvent.hostname.value = hostname self.rec(infoEvent) + + def close(self): + """ + @summary: end of scenario + """ + self.rec(CloseEvent()) class FileReader(object): """ diff --git a/rdpy/ui/qt4.py b/rdpy/ui/qt4.py index eaf9624..59723a6 100644 --- a/rdpy/ui/qt4.py +++ b/rdpy/ui/qt4.py @@ -233,9 +233,9 @@ class RDPClientQt(RDPClientObserver, QAdaptor): """ def __init__(self, controller, width, height): """ - @param controller: RDP controller - @param width: width of widget - @param height: height of widget + @param controller: {RDPClientController} RDP controller + @param width: {int} width of widget + @param height: {int} height of widget """ RDPClientObserver.__init__(self, controller) self._widget = QRemoteDesktop(width, height, self) @@ -293,15 +293,15 @@ class RDPClientQt(RDPClientObserver, QAdaptor): def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): """ @summary: Notify bitmap update - @param destLeft: xmin position - @param destTop: ymin position - @param destRight: xmax position because RDP can send bitmap with padding - @param destBottom: ymax position because RDP can send bitmap with padding - @param width: width of bitmap - @param height: height of bitmap - @param bitsPerPixel: number of bit per pixel - @param isCompress: use RLE compression - @param data: bitmap data + @param destLeft: {int} xmin position + @param destTop: {int} ymin position + @param destRight: {int} xmax position because RDP can send bitmap with padding + @param destBottom: {int} ymax position because RDP can send bitmap with padding + @param width: {int} width of bitmap + @param height: {int} height of bitmap + @param bitsPerPixel: {int} number of bit per pixel + @param isCompress: {bool} use RLE compression + @param data: {str} bitmap data """ image = RDPBitmapToQtImage(width, height, bitsPerPixel, isCompress, data); #if image need to be cut @@ -313,14 +313,12 @@ class RDPClientQt(RDPClientObserver, QAdaptor): @summary: Call when stack is ready """ #do something maybe a loader - pass def onClose(self): """ @summary: Call when stack is close """ #do something maybe a message - pass class QRemoteDesktop(QtGui.QWidget):