add honey pot binary
This commit is contained in:
25
README.md
25
README.md
@@ -2,7 +2,16 @@
|
||||
|
||||
Remote Desktop Protocol in twisted PYthon.
|
||||
|
||||
RDPY is a pure Python implementation of the Microsoft RDP (Remote Desktop Protocol) protocol. RDPY is built over the event driven network engine Twisted.
|
||||
RDPY is a pure Python implementation of the Microsoft RDP (Remote Desktop Protocol) protocol (Client and Server Side). RDPY is built over the event driven network engine Twisted.
|
||||
|
||||
RDPY provide RDP and VNC binaries :
|
||||
* RDP Man In The Middle proxy which record session
|
||||
* RDP Honeypot
|
||||
* RDP screen shooter
|
||||
* RDP client
|
||||
* VNC client
|
||||
* VNC screen shooter
|
||||
* RSS Player
|
||||
|
||||
## Build
|
||||
|
||||
@@ -81,22 +90,22 @@ $ rdpy-vncscreenshot.py [-p password] [-o output_file_path] XXX.XXX.XXX.XXX[:590
|
||||
|
||||
### rdpy-rdpmitm
|
||||
|
||||
rdpy-rdpmitm is a RDP proxy allows you to do a Man in the middle attack on RDP protocol.
|
||||
Record session bitmap and do a keylogger for RDP protocol.
|
||||
rdpy-rdpmitm is a RDP proxy allows you to do a Man In The Middle attack on RDP protocol.
|
||||
Record Session Scenario into rss file which can be replay by rdpy-rssplayer.
|
||||
|
||||
```
|
||||
$ rdpy-rdpmitm.py [-l listen_port] [-k private_key_file_path] [-c certificate_file_path] target_host[:target_port]
|
||||
$ rdpy-rdpmitm.py -o output_dir [-l listen_port] [-k private_key_file_path] [-c certificate_file_path] target_host[:target_port]
|
||||
```
|
||||
|
||||
Output directory is use to save rss file with following format (YYYYMMDDHHMMSS_ip_index.rss)
|
||||
The private key file and the certificate file are classic cryptographic files for SSL connections. The RDP protocol can negotiate its own security layer. The CredSSP security layer is planned for an upcoming release. If one of both parameters are omitted, the server use standard RDP as security layer.
|
||||
|
||||
### rdpy-rdpshare
|
||||
### rdpy-rdphoneypot
|
||||
|
||||
rdpy-rdpshare is a RDP proxy allows you to share your desktop through RDP clients (rdpy-rdpclient, mstsc, remmina, rdesktop).
|
||||
First connection is use as master connection (keep session control).
|
||||
rdpy-rdphoneypot is a RDP honey Pot. Use Recorded Session Scenario to replay scenario through RDP Protocol.
|
||||
|
||||
```
|
||||
$ rdpy-rdpshare.py [-l listen_port] [-k private_key_file_path] [-c certificate_file_path] target_host[:target_port]
|
||||
$ rdpy-rdphoneypot.py [-l listen_port] [-k private_key_file_path] [-c certificate_file_path] rss_file_path
|
||||
```
|
||||
|
||||
The private key file and the certificate file are classic cryptographic files for SSL connections. The RDP protocol can negotiate its own security layer. The CredSSP security layer is planned for an upcoming release. If one of both parameters are omitted, the server use standard RDP as security layer.
|
||||
|
||||
133
bin/rdpy-rdphoneypot.py
Executable file
133
bin/rdpy-rdphoneypot.py
Executable file
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
#
|
||||
# This file is part of rdpy.
|
||||
#
|
||||
# rdpy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
"""
|
||||
RDP Honey pot use Rss scenario file to simulate RDP server
|
||||
"""
|
||||
|
||||
import sys, os, getopt, time
|
||||
|
||||
from rdpy.core import log, error, rss
|
||||
from rdpy.protocol.rdp import rdp
|
||||
from twisted.internet import reactor
|
||||
|
||||
log._LOG_LEVEL = log.Level.INFO
|
||||
|
||||
class HoneyPotServer(rdp.RDPServerObserver):
|
||||
def __init__(self, controller, rssFile):
|
||||
"""
|
||||
@param controller: {RDPServerController}
|
||||
"""
|
||||
rdp.RDPServerObserver.__init__(self, controller)
|
||||
self._rssFile = rssFile
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@summary: Event use to inform state of server stack
|
||||
First time this event is called is when human client is connected
|
||||
Second time is after color depth nego, because color depth nego
|
||||
restart a connection sequence
|
||||
@see: rdp.RDPServerObserver.onReady
|
||||
"""
|
||||
self.loopScenario()
|
||||
|
||||
def onClose(self):
|
||||
""" HoneyPot """
|
||||
|
||||
def onKeyEventScancode(self, code, isPressed):
|
||||
""" HoneyPot """
|
||||
|
||||
def onKeyEventUnicode(self, code, isPressed):
|
||||
""" HoneyPot """
|
||||
|
||||
def onPointerEvent(self, x, y, button, isPressed):
|
||||
""" HoneyPot """
|
||||
|
||||
def loopScenario(self):
|
||||
"""
|
||||
@summary: main loop event
|
||||
"""
|
||||
e = self._rssFile.nextEvent()
|
||||
if e is None:
|
||||
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)
|
||||
#restart connection sequence
|
||||
return
|
||||
reactor.callLater(float(e.timestamp.value) / 1000.0, self.loopScenario)
|
||||
|
||||
class HoneyPotServerFactory(rdp.ServerFactory):
|
||||
"""
|
||||
@summary: Factory on listening events
|
||||
"""
|
||||
def __init__(self, rssFilePath, privateKeyFilePath, certificateFilePath):
|
||||
"""
|
||||
@param rssFilePath: Recorded Session Scenario File path
|
||||
@param privateKeyFilePath: {str} file contain server private key (if none -> back to standard RDP security)
|
||||
@param certificateFilePath: {str} file contain server certificate (if none -> back to standard RDP security)
|
||||
"""
|
||||
rdp.ServerFactory.__init__(self, 16, privateKeyFilePath, certificateFilePath)
|
||||
self._rssFilePath = rssFilePath
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
"""
|
||||
@param controller: {rdp.RDPServerController}
|
||||
@param addr: destination address
|
||||
@see: rdp.ServerFactory.buildObserver
|
||||
"""
|
||||
return HoneyPotServer(controller, rss.createReader(self._rssFilePath))
|
||||
|
||||
def help():
|
||||
"""
|
||||
@summary: Print help in console
|
||||
"""
|
||||
print """
|
||||
Usage: rdpy-rdphoneypot.py rss_filepath
|
||||
[-l listen_port default 3389]
|
||||
[-k private_key_file_path (mandatory for SSL)]
|
||||
[-c certificate_file_path (mandatory for SSL)]
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
listen = "3389"
|
||||
privateKeyFilePath = None
|
||||
certificateFilePath = None
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "hl:k:c:")
|
||||
except getopt.GetoptError:
|
||||
help()
|
||||
for opt, arg in opts:
|
||||
if opt == "-h":
|
||||
help()
|
||||
sys.exit()
|
||||
elif opt == "-l":
|
||||
listen = arg
|
||||
elif opt == "-k":
|
||||
privateKeyFilePath = arg
|
||||
elif opt == "-c":
|
||||
certificateFilePath = arg
|
||||
|
||||
reactor.listenTCP(int(listen), HoneyPotServerFactory(args[0], privateKeyFilePath, certificateFilePath))
|
||||
reactor.run()
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
"""
|
||||
RDP proxy with Man in the middle capabilities
|
||||
Save RDP events in output file in rsr file format
|
||||
Save RDP events in output RSR file format
|
||||
RSR file format can be read by rdpy-rsrplayer.py
|
||||
----------------------------
|
||||
Client RDP -> | ProxyServer | ProxyClient | -> Server RDP
|
||||
@@ -31,7 +31,7 @@ Client RDP -> | ProxyServer | ProxyClient | -> Server RDP
|
||||
|
||||
import sys, os, getopt, time
|
||||
|
||||
from rdpy.core import log, error, type, rsr
|
||||
from rdpy.core import log, error, rss
|
||||
from rdpy.protocol.rdp import rdp
|
||||
from twisted.internet import reactor
|
||||
|
||||
@@ -41,16 +41,17 @@ class ProxyServer(rdp.RDPServerObserver):
|
||||
"""
|
||||
@summary: Server side of proxy
|
||||
"""
|
||||
def __init__(self, controller, target, rsrRecorder):
|
||||
def __init__(self, controller, target, clientSecurityLevel, rssRecorder):
|
||||
"""
|
||||
@param controller: {RDPServerController}
|
||||
@param target: {tuple(ip, port)}
|
||||
@param rsrRecorder: {rsr.FileRecorder} use to record session
|
||||
@param rssRecorder: {rss.FileRecorder} use to record session
|
||||
"""
|
||||
rdp.RDPServerObserver.__init__(self, controller)
|
||||
self._target = target
|
||||
self._client = None
|
||||
self._rsr = rsrRecorder
|
||||
self._rss = rssRecorder
|
||||
self._clientSecurityLevel = clientSecurityLevel
|
||||
|
||||
def setClient(self, client):
|
||||
"""
|
||||
@@ -70,13 +71,13 @@ class ProxyServer(rdp.RDPServerObserver):
|
||||
if self._client is None:
|
||||
#try a connection
|
||||
domain, username, password = self._controller.getCredentials()
|
||||
self._rsr.recInfo(username, password, domain, self._controller.getHostname())
|
||||
self._rss.recInfo(username, password, domain, self._controller.getHostname())
|
||||
|
||||
width, height = self._controller.getScreen()
|
||||
self._rsr.recResize(width, height)
|
||||
self._rss.recScreen(width, height, self._controller.getColorDepth())
|
||||
|
||||
reactor.connectTCP(self._target[0], int(self._target[1]), ProxyClientFactory(self, width, height,
|
||||
domain, username, password))
|
||||
domain, username, password,self._clientSecurityLevel))
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
@@ -90,8 +91,8 @@ class ProxyServer(rdp.RDPServerObserver):
|
||||
def onKeyEventScancode(self, code, isPressed):
|
||||
"""
|
||||
@summary: Event call when a keyboard event is catch in scan code format
|
||||
@param code: scan code of key
|
||||
@param isPressed: True if key is down
|
||||
@param code: {int} scan code of key
|
||||
@param isPressed: {bool} True if key is down
|
||||
@see: rdp.RDPServerObserver.onKeyEventScancode
|
||||
"""
|
||||
if self._client is None:
|
||||
@@ -112,10 +113,10 @@ class ProxyServer(rdp.RDPServerObserver):
|
||||
def onPointerEvent(self, x, y, button, isPressed):
|
||||
"""
|
||||
@summary: Event call on mouse event
|
||||
@param x: x position
|
||||
@param y: y position
|
||||
@param button: 1, 2 or 3 button
|
||||
@param isPressed: True if mouse button is pressed
|
||||
@param x: {int} x position
|
||||
@param y: {int} y position
|
||||
@param button: {int} 1, 2 or 3 button
|
||||
@param isPressed: {bool} True if mouse button is pressed
|
||||
@see: rdp.RDPServerObserver.onPointerEvent
|
||||
"""
|
||||
if self._client is None:
|
||||
@@ -126,24 +127,28 @@ class ProxyServerFactory(rdp.ServerFactory):
|
||||
"""
|
||||
@summary: Factory on listening events
|
||||
"""
|
||||
def __init__(self, target, ouputDir, privateKeyFilePath = None, certificateFilePath = None):
|
||||
def __init__(self, target, ouputDir, privateKeyFilePath, certificateFilePath, clientSecurity):
|
||||
"""
|
||||
@param target: {tuple(ip, prt)}
|
||||
@param privateKeyFilePath: {str} file contain server private key (if none -> back to standard RDP security)
|
||||
@param certificateFilePath: {str} file contain server certificate (if none -> back to standard RDP security)
|
||||
@param clientSecurity: {str(ssl|rdp)} security layer use in client connection side
|
||||
"""
|
||||
rdp.ServerFactory.__init__(self, 16, privateKeyFilePath, certificateFilePath)
|
||||
self._target = target
|
||||
self._ouputDir = ouputDir
|
||||
self._clientSecurity = clientSecurity
|
||||
#use produce unique file by connection
|
||||
self._uniqueId = 0
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
"""
|
||||
@param controller: rdp.RDPServerController
|
||||
@param controller: {rdp.RDPServerController}
|
||||
@param addr: destination address
|
||||
@see: rdp.ServerFactory.buildObserver
|
||||
"""
|
||||
#first build main session
|
||||
return ProxyServer(controller, self._target, rsr.createRecorder(os.path.join(self._ouputDir, "%s_%s.rsr"%(time.strftime('%Y%m%d%H%M%S'), addr.host))))
|
||||
self._uniqueId += 1
|
||||
return ProxyServer(controller, self._target, self._clientSecurity, rss.createRecorder(os.path.join(self._ouputDir, "%s_%s_%s.rss"%(time.strftime('%Y%m%d%H%M%S'), addr.host, self._uniqueId))))
|
||||
|
||||
class ProxyClient(rdp.RDPClientObserver):
|
||||
"""
|
||||
@@ -151,8 +156,8 @@ class ProxyClient(rdp.RDPClientObserver):
|
||||
"""
|
||||
def __init__(self, controller, server):
|
||||
"""
|
||||
@param controller: rdp.RDPClientController
|
||||
@param server: ProxyServer
|
||||
@param controller: {rdp.RDPClientController}
|
||||
@param server: {ProxyServer}
|
||||
"""
|
||||
rdp.RDPClientObserver.__init__(self, controller)
|
||||
self._server = server
|
||||
@@ -188,21 +193,22 @@ class ProxyClient(rdp.RDPClientObserver):
|
||||
@param data: {str} bitmap data
|
||||
@see: rdp.RDPClientObserver.onUpdate
|
||||
"""
|
||||
self._server._rsr.recUpdate(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, rsr.UpdateFormat.BMP if isCompress else rsr.UpdateFormat.RAW, data)
|
||||
self._server._rss.recUpdate(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):
|
||||
"""
|
||||
@summary: Factory for proxy client
|
||||
"""
|
||||
def __init__(self, server, width, height, domain, username, password):
|
||||
def __init__(self, server, width, height, domain, username, password, security):
|
||||
"""
|
||||
@param server: ProxyServer
|
||||
@param width: screen width
|
||||
@param height: screen height
|
||||
@param domain: domain session
|
||||
@param username: username session
|
||||
@param password: password session
|
||||
@param server: {ProxyServer}
|
||||
@param width: {int} screen width
|
||||
@param height: {int} screen height
|
||||
@param domain: {str} domain session
|
||||
@param username: {str} username session
|
||||
@param password: {str} password session
|
||||
@param security: {str(ssl|rdp)} security level
|
||||
"""
|
||||
self._server = server
|
||||
self._width = width
|
||||
@@ -210,7 +216,7 @@ class ProxyClientFactory(rdp.ClientFactory):
|
||||
self._domain = domain
|
||||
self._username = username
|
||||
self._password = password
|
||||
self._security = "ssl"
|
||||
self._security = security
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
"""
|
||||
@@ -234,7 +240,13 @@ def help():
|
||||
"""
|
||||
@summary: Print help in console
|
||||
"""
|
||||
print "Usage: rdpy-rdpmitm.py -o output_directory [-l listen_port default 3389] [-k private_key_file_path (mandatory for SSL)] [-c certificate_file_path (mandatory for SSL)] target"
|
||||
print """
|
||||
Usage: rdpy-rdpmitm.py -o output_directory target
|
||||
[-l listen_port default 3389]
|
||||
[-k private_key_file_path (mandatory for SSL)]
|
||||
[-c certificate_file_path (mandatory for SSL)]
|
||||
[-r RDP standard security (XP or server 2003 client or older)]
|
||||
"""
|
||||
|
||||
def parseIpPort(interface, defaultPort = "3389"):
|
||||
if ':' in interface:
|
||||
@@ -247,9 +259,10 @@ if __name__ == '__main__':
|
||||
privateKeyFilePath = None
|
||||
certificateFilePath = None
|
||||
ouputDirectory = None
|
||||
clientSecurity = "ssl"
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "hl:k:c:o:")
|
||||
opts, args = getopt.getopt(sys.argv[1:], "hl:k:c:o:r")
|
||||
except getopt.GetoptError:
|
||||
help()
|
||||
for opt, arg in opts:
|
||||
@@ -264,11 +277,13 @@ if __name__ == '__main__':
|
||||
certificateFilePath = arg
|
||||
elif opt == "-o":
|
||||
ouputDirectory = arg
|
||||
elif opt == "-r":
|
||||
clientSecurity = "rdp"
|
||||
|
||||
if ouputDirectory is None or not os.path.dirname(ouputDirectory):
|
||||
log.error("%s is an invalid output directory"%ouputDirectory)
|
||||
help()
|
||||
sys.exit()
|
||||
|
||||
reactor.listenTCP(int(listen), ProxyServerFactory(parseIpPort(args[0]), ouputDirectory, privateKeyFilePath, certificateFilePath))
|
||||
reactor.listenTCP(int(listen), ProxyServerFactory(parseIpPort(args[0]), ouputDirectory, privateKeyFilePath, certificateFilePath, clientSecurity))
|
||||
reactor.run()
|
||||
@@ -1,320 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2014 Sylvain Peyrefitte
|
||||
#
|
||||
# This file is part of rdpy.
|
||||
#
|
||||
# rdpy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
"""
|
||||
RDP proxy with share capabilities
|
||||
Share your desktop for demo
|
||||
---------------------------
|
||||
Client RDP -> | ProxyServer | ProxyClient | -> Server RDP
|
||||
---------------------------
|
||||
| ProxyShadow |
|
||||
--------------
|
||||
^
|
||||
Shadow client ------------|
|
||||
"""
|
||||
|
||||
import sys, os, getopt
|
||||
|
||||
from rdpy.core import log, error
|
||||
from rdpy.protocol.rdp import rdp
|
||||
from twisted.internet import reactor
|
||||
|
||||
log._LOG_LEVEL = log.Level.DEBUG
|
||||
|
||||
class ProxyServer(rdp.RDPServerObserver):
|
||||
"""
|
||||
@summary: Server side of proxy
|
||||
"""
|
||||
def __init__(self, controller, target):
|
||||
"""
|
||||
@param controller: {RDPServerController}
|
||||
@param target: {tuple(ip, port)}
|
||||
"""
|
||||
rdp.RDPServerObserver.__init__(self, controller)
|
||||
self._target = target
|
||||
self._client = None
|
||||
self._close = False
|
||||
|
||||
def onClientReady(self, client):
|
||||
"""
|
||||
@summary: Event throw by client when it's ready
|
||||
@param client: {ProxyClient}
|
||||
"""
|
||||
self._client = client
|
||||
#need to reevaluate color depth
|
||||
self._controller.setColorDepth(self._client._controller.getColorDepth())
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@summary: Event use to inform state of server stack
|
||||
First time this event is called is when human client is connected
|
||||
Second time is after color depth nego, because color depth nego
|
||||
restart a connection sequence
|
||||
@see: rdp.RDPServerObserver.onReady
|
||||
"""
|
||||
if self._client is None:
|
||||
#try a connection
|
||||
domain, username, password = self._controller.getCredentials()
|
||||
|
||||
width, height = self._controller.getScreen()
|
||||
reactor.connectTCP(self._target[0], int(self._target[1]), ProxyClientFactory(self, width, height,
|
||||
domain, username, password))
|
||||
else:
|
||||
#refresh client
|
||||
width, height = self._controller.getScreen()
|
||||
self._client._controller.sendRefreshOrder(0, 0, width, height)
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
@summary: Call when human client close connection
|
||||
@see: rdp.RDPServerObserver.onClose
|
||||
"""
|
||||
self._close = True
|
||||
if self._client is None:
|
||||
return
|
||||
|
||||
#close proxy client
|
||||
self._client._controller.close()
|
||||
|
||||
def onKeyEventScancode(self, code, isPressed):
|
||||
"""
|
||||
@summary: Event call when a keyboard event is catch in scan code format
|
||||
@param code: scan code of key
|
||||
@param isPressed: True if key is down
|
||||
@see: rdp.RDPServerObserver.onKeyEventScancode
|
||||
"""
|
||||
if self._client is None:
|
||||
return
|
||||
self._client._controller.sendKeyEventScancode(code, isPressed)
|
||||
|
||||
def onKeyEventUnicode(self, code, isPressed):
|
||||
"""
|
||||
@summary: Event call when a keyboard event is catch in unicode format
|
||||
@param code: unicode of key
|
||||
@param isPressed: True if key is down
|
||||
@see: rdp.RDPServerObserver.onKeyEventUnicode
|
||||
"""
|
||||
if self._client is None:
|
||||
return
|
||||
self._client._controller.sendKeyEventUnicode(code, isPressed)
|
||||
|
||||
def onPointerEvent(self, x, y, button, isPressed):
|
||||
"""
|
||||
@summary: Event call on mouse event
|
||||
@param x: x position
|
||||
@param y: y position
|
||||
@param button: 1, 2 or 3 button
|
||||
@param isPressed: True if mouse button is pressed
|
||||
@see: rdp.RDPServerObserver.onPointerEvent
|
||||
"""
|
||||
if self._client is None:
|
||||
return
|
||||
self._client._controller.sendPointerEvent(x, y, button, isPressed)
|
||||
|
||||
class ProxyServerFactory(rdp.ServerFactory):
|
||||
"""
|
||||
@summary: Factory on listening events
|
||||
"""
|
||||
def __init__(self, target, privateKeyFilePath = None, certificateFilePath = None):
|
||||
"""
|
||||
@param target: {tuple(ip, prt)}
|
||||
@param privateKeyFilePath: {str} file contain server private key (if none -> back to standard RDP security)
|
||||
@param certificateFilePath: {str} file contain server certificate (if none -> back to standard RDP security)
|
||||
"""
|
||||
rdp.ServerFactory.__init__(self, 16, privateKeyFilePath, certificateFilePath)
|
||||
self._target = target
|
||||
self._main = None
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
"""
|
||||
@param controller: rdp.RDPServerController
|
||||
@param addr: destination address
|
||||
@see: rdp.ServerFactory.buildObserver
|
||||
"""
|
||||
#first build main session
|
||||
if self._main is None or self._main._close:
|
||||
self._main = ProxyServer(controller, self._target)
|
||||
return self._main
|
||||
clientController = self._main._client._controller if not self._main._client is None else None
|
||||
return Shadow(controller, self._main._client._controller)
|
||||
|
||||
class ProxyClient(rdp.RDPClientObserver):
|
||||
"""
|
||||
@summary: Client side of proxy
|
||||
"""
|
||||
def __init__(self, controller, server):
|
||||
"""
|
||||
@param controller: rdp.RDPClientController
|
||||
@param server: ProxyServer
|
||||
"""
|
||||
rdp.RDPClientObserver.__init__(self, controller)
|
||||
self._server = server
|
||||
self._connected = False
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@summary: Event use to signal that RDP stack is ready
|
||||
Inform ProxyServer that i'm connected
|
||||
@see: rdp.RDPClientObserver.onReady
|
||||
"""
|
||||
#prevent multiple on ready event
|
||||
#because each deactive-reactive sequence
|
||||
#launch an onReady message
|
||||
if self._connected:
|
||||
return
|
||||
else:
|
||||
self._connected = True
|
||||
|
||||
self._server.onClientReady(self)
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
@summary: Event inform that stack is close
|
||||
@see: rdp.RDPClientObserver.onClose
|
||||
"""
|
||||
self._server._controller.close()
|
||||
|
||||
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||
"""
|
||||
@summary: Event use to inform 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
|
||||
@see: rdp.RDPClientObserver.onUpdate
|
||||
"""
|
||||
self._server._controller.sendUpdate(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data)
|
||||
|
||||
class ProxyClientFactory(rdp.ClientFactory):
|
||||
"""
|
||||
@summary: Factory for proxy client
|
||||
"""
|
||||
def __init__(self, server, width, height, domain, username, password):
|
||||
"""
|
||||
@param server: ProxyServer
|
||||
@param width: screen width
|
||||
@param height: screen height
|
||||
@param domain: domain session
|
||||
@param username: username session
|
||||
@param password: password session
|
||||
"""
|
||||
self._server = server
|
||||
self._width = width
|
||||
self._height = height
|
||||
self._domain = domain
|
||||
self._username = username
|
||||
self._password = password
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
"""
|
||||
@summary: Build observer
|
||||
@param controller: rdp.RDPClientController
|
||||
@param addr: destination address
|
||||
@see: rdp.ClientFactory.buildObserver
|
||||
@return: ProxyClient
|
||||
"""
|
||||
#set screen resolution
|
||||
controller.setScreen(self._width, self._height)
|
||||
#set credential
|
||||
controller.setDomain(self._domain)
|
||||
controller.setUsername(self._username)
|
||||
controller.setPassword(self._password)
|
||||
controller.setPerformanceSession()
|
||||
return ProxyClient(controller, self._server)
|
||||
|
||||
class Shadow(rdp.RDPServerObserver):
|
||||
"""
|
||||
@summary: Use to manage admin session
|
||||
"""
|
||||
def __init__(self, controller, clientController):
|
||||
"""
|
||||
@param server: rdp.RDPServerController
|
||||
"""
|
||||
rdp.RDPServerObserver.__init__(self, controller)
|
||||
self._clientController = clientController
|
||||
self._client = None
|
||||
if not self._clientController is None:
|
||||
self._client = ProxyClient(clientController, self)
|
||||
self._controller.setColorDepth(self._client._controller.getColorDepth())
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@summary: Stack is ready and connected
|
||||
May be called after an setColorDepth too
|
||||
@see: rdp.RDPServerObserver.onReady
|
||||
"""
|
||||
if self._client is None:
|
||||
self._controller.close()
|
||||
return
|
||||
|
||||
width, height = self._controller.getScreen()
|
||||
self._client._controller.sendRefreshOrder(0, 0, width, height)
|
||||
|
||||
def onClose(self):
|
||||
""" Shadow """
|
||||
|
||||
def onKeyEventScancode(self, code, isPressed):
|
||||
""" Shadow """
|
||||
|
||||
def onKeyEventUnicode(self, code, isPressed):
|
||||
""" Shadow """
|
||||
|
||||
def onPointerEvent(self, x, y, button, isPressed):
|
||||
""" Shadow """
|
||||
|
||||
def help():
|
||||
"""
|
||||
@summary: Print help in console
|
||||
"""
|
||||
print "Usage: rdpy-rdpshare.py [-l listen_port default 3389] [-k private_key_file_path (mandatory for SSL)] [-c certificate_file_path (mandatory for SSL)] [-i admin_ip[:admin_port]] target"
|
||||
|
||||
def parseIpPort(interface, defaultPort = "3389"):
|
||||
if ':' in interface:
|
||||
return interface.split(':')
|
||||
else:
|
||||
return interface, defaultPort
|
||||
|
||||
if __name__ == '__main__':
|
||||
listen = "3389"
|
||||
privateKeyFilePath = None
|
||||
certificateFilePath = None
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "hl:k:c:")
|
||||
except getopt.GetoptError:
|
||||
help()
|
||||
for opt, arg in opts:
|
||||
if opt == "-h":
|
||||
help()
|
||||
sys.exit()
|
||||
elif opt == "-l":
|
||||
listen = arg
|
||||
elif opt == "-k":
|
||||
privateKeyFilePath = arg
|
||||
elif opt == "-c":
|
||||
certificateFilePath = arg
|
||||
|
||||
reactor.listenTCP(int(listen), ProxyServerFactory(parseIpPort(args[0]), privateKeyFilePath, certificateFilePath))
|
||||
reactor.run()
|
||||
@@ -18,23 +18,23 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
"""
|
||||
rsr file player
|
||||
rss file player
|
||||
"""
|
||||
|
||||
import sys, os, getopt, socket
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
|
||||
from rdpy.core import log, rsr
|
||||
from rdpy.core import log, rss
|
||||
from rdpy.ui.qt4 import QRemoteDesktop, RDPBitmapToQtImage
|
||||
log._LOG_LEVEL = log.Level.INFO
|
||||
|
||||
class RsrPlayerWidget(QRemoteDesktop):
|
||||
class RssPlayerWidget(QRemoteDesktop):
|
||||
"""
|
||||
@summary: special rsr player widget
|
||||
@summary: special rss player widget
|
||||
"""
|
||||
def __init__(self, width, height):
|
||||
class RsrAdaptor(object):
|
||||
class RssAdaptor(object):
|
||||
def sendMouseEvent(self, e, isPressed):
|
||||
""" Not Handle """
|
||||
def sendKeyEvent(self, e, isPressed):
|
||||
@@ -43,51 +43,37 @@ class RsrPlayerWidget(QRemoteDesktop):
|
||||
""" Not Handle """
|
||||
def closeEvent(self, e):
|
||||
""" Not Handle """
|
||||
QRemoteDesktop.__init__(self, width, height, RsrAdaptor())
|
||||
QRemoteDesktop.__init__(self, width, height, RssAdaptor())
|
||||
|
||||
def drawInfos(self, username):
|
||||
drawArea = QtGui.QImage(100, 100, QtGui.QImage.Format_RGB32)
|
||||
#fill with background Color
|
||||
drawArea.fill(QtCore.Qt.red)
|
||||
with QtGui.QPainter(drawArea) as qp:
|
||||
rect = QtCore.QRect(0, 0, 100, 100)
|
||||
qp.setPen(QtCore.Qt.black)
|
||||
qp.setFont(QtGui.QFont('arial', 12, QtGui.QFont.Bold))
|
||||
qp.drawText(rect, QtCore.Qt.AlignCenter, "username %s"%username)
|
||||
|
||||
self.notifyImage(0, 0, drawArea, 100, 100)
|
||||
|
||||
def drawInfos(self, domain, username, password, hostname):
|
||||
QtGui.QMessageBox.about(self, "Credentials Event", "domain : %s\nusername : %s\npassword : %s\nhostname : %s" % (
|
||||
domain, username, password, hostname))
|
||||
|
||||
def help():
|
||||
print "Usage: rdpy-rsrplayer [-h] path"
|
||||
print "Usage: rdpy-rssplayer [-h] rss_filepath"
|
||||
|
||||
def loop(widget, rsrFile):
|
||||
def loop(widget, rssFile):
|
||||
"""
|
||||
@summary: timer function
|
||||
@param widget: {QRemoteDesktop}
|
||||
@param rsrFile: {rsr.FileReader}
|
||||
@param rssFile: {rss.FileReader}
|
||||
"""
|
||||
e = rsrFile.nextEvent()
|
||||
e = rssFile.nextEvent()
|
||||
if e is None:
|
||||
widget.close()
|
||||
return
|
||||
|
||||
if e.type.value == rsr.EventType.UPDATE:
|
||||
image = RDPBitmapToQtImage(e.event.width.value, e.event.height.value, e.event.bpp.value, e.event.format.value == rsr.UpdateFormat.BMP, e.event.data.value);
|
||||
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 == rsr.EventType.RESIZE:
|
||||
elif e.type.value == rss.EventType.SCREEN:
|
||||
widget.resize(e.event.width.value, e.event.height.value)
|
||||
|
||||
elif e.type.value == rsr.EventType.INFO:
|
||||
widget.drawInfos(e.event.username)
|
||||
log.info("*" * 50)
|
||||
log.info("username : %s"%e.event.username.value)
|
||||
log.info("password : %s"%e.event.password.value)
|
||||
log.info("domain : %s"%e.event.domain.value)
|
||||
log.info("hostname : %s"%e.event.hostname.value)
|
||||
log.info("*" * 50)
|
||||
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+ 1000,lambda:loop(widget, rsrFile))
|
||||
QtCore.QTimer.singleShot(e.timestamp.value,lambda:loop(widget, rssFile))
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
@@ -102,8 +88,8 @@ if __name__ == '__main__':
|
||||
filepath = args[0]
|
||||
#create application
|
||||
app = QtGui.QApplication(sys.argv)
|
||||
widget = RsrPlayerWidget(800, 600)
|
||||
widget = RssPlayerWidget(800, 600)
|
||||
widget.show()
|
||||
rsrFile = rsr.createReader(filepath)
|
||||
loop(widget, rsrFile)
|
||||
rssFile = rss.createReader(filepath)
|
||||
loop(widget, rssFile)
|
||||
sys.exit(app.exec_())
|
||||
@@ -18,7 +18,7 @@
|
||||
#
|
||||
|
||||
"""
|
||||
Remote Session Recorder File format
|
||||
Remote Session Scenario File format
|
||||
Private protocol format to save events
|
||||
"""
|
||||
|
||||
@@ -31,7 +31,7 @@ class EventType(object):
|
||||
@summary: event type
|
||||
"""
|
||||
UPDATE = 0x0001
|
||||
RESIZE = 0x0002
|
||||
SCREEN = 0x0002
|
||||
INFO = 0x0003
|
||||
|
||||
class UpdateFormat(object):
|
||||
@@ -55,7 +55,7 @@ class Event(CompositeType):
|
||||
"""
|
||||
@summary: Closure for event factory
|
||||
"""
|
||||
for c in [UpdateEvent, ResizeEvent, InfoEvent]:
|
||||
for c in [UpdateEvent, ScreenEvent, InfoEvent]:
|
||||
if self.type.value == c._TYPE_:
|
||||
return c(readLen = self.length)
|
||||
log.debug("unknown event type : %s"%hex(self.type.value))
|
||||
@@ -103,15 +103,16 @@ class InfoEvent(CompositeType):
|
||||
self.lenHostname = UInt16Le(lambda:sizeof(self.hostname))
|
||||
self.hostname = String(readLen = self.lenHostname)
|
||||
|
||||
class ResizeEvent(CompositeType):
|
||||
class ScreenEvent(CompositeType):
|
||||
"""
|
||||
@summary: resize event
|
||||
@summary: screen information event
|
||||
"""
|
||||
_TYPE_ = EventType.RESIZE
|
||||
_TYPE_ = EventType.SCREEN
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.width = UInt16Le()
|
||||
self.height = UInt16Le()
|
||||
self.colorDepth = UInt8()
|
||||
|
||||
def timeMs():
|
||||
"""
|
||||
@@ -174,16 +175,18 @@ class FileRecorder(object):
|
||||
updateEvent.data.value = data
|
||||
self.rec(updateEvent)
|
||||
|
||||
def recResize(self, width, height):
|
||||
def recScreen(self, width, height, colorDepth):
|
||||
"""
|
||||
@summary: record resize event of screen (maybe first event)
|
||||
@param width: {int} width of screen
|
||||
@param height: {int} height of screen
|
||||
@param colorDepth: {int} colorDepth
|
||||
"""
|
||||
resizeEvent = ResizeEvent()
|
||||
resizeEvent.width.value = width
|
||||
resizeEvent.height.value = height
|
||||
self.rec(resizeEvent)
|
||||
screenEvent = ScreenEvent()
|
||||
screenEvent.width.value = width
|
||||
screenEvent.height.value = height
|
||||
screenEvent.colorDepth.value = colorDepth
|
||||
self.rec(screenEvent)
|
||||
|
||||
def recInfo(self, username, password, domain = "", hostname = ""):
|
||||
"""
|
||||
16
setup.py
16
setup.py
@@ -7,8 +7,17 @@ setup(name='rdpy',
|
||||
version='1.2.0',
|
||||
description='Remote Desktop Protocol in Python',
|
||||
long_description="""
|
||||
RDPY is a pure Python implementation of the Microsoft RDP (Remote Desktop Protocol) protocol.
|
||||
RDPY is a pure Python implementation of the Microsoft RDP (Remote Desktop Protocol) protocol (Client and Server side).
|
||||
RDPY is built over the event driven network engine Twisted.
|
||||
|
||||
RDPY provide RDP and VNC binaries :
|
||||
\t-RDP Man In The Middle proxy which record session
|
||||
\t-RDP Honeypot
|
||||
\t-RDP screen shooter
|
||||
\t-RDP client
|
||||
\t-VNC client
|
||||
\t-VNC screen shooter
|
||||
\t-RSS Player
|
||||
""",
|
||||
author='Sylvain Peyrefitte',
|
||||
author_email='citronneur@gmail.com',
|
||||
@@ -25,10 +34,11 @@ setup(name='rdpy',
|
||||
],
|
||||
ext_modules=[Extension('rle', ['ext/rle.c'])],
|
||||
scripts = [
|
||||
'bin/rdpy-rdpclient.py',
|
||||
'bin/rdpy-rdpy-rsrplayer.py',
|
||||
'bin/rdpy-rdpclient.py',
|
||||
'bin/rdpy-rdphoneypot.py',
|
||||
'bin/rdpy-rdpmitm.py',
|
||||
'bin/rdpy-rdpscreenshot.py',
|
||||
'bin/rdpy-rssplayer.py',
|
||||
'bin/rdpy-vncclient.py',
|
||||
'bin/rdpy-vncscreenshot.py'
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user