Compare commits
1 Commits
v1.2.2
...
revert-11-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58ad5782c1 |
81
README.md
81
README.md
@@ -1,25 +1,25 @@
|
|||||||
# RDPY [](https://travis-ci.org/citronneur/rdpy)
|
# RDPY [](https://travis-ci.org/citronneur/rdpy)
|
||||||
|
|
||||||
Remote Desktop Protocol in twisted python.
|
Remote Desktop Protocol in twisted PYthon.
|
||||||
|
|
||||||
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 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 provides the following RDP and VNC binaries :
|
RDPY provide RDP and VNC binaries :
|
||||||
* RDP Man In The Middle proxy which record session
|
* RDP Man In The Middle proxy which record session
|
||||||
* RDP Honeypot
|
* RDP Honeypot
|
||||||
* RDP screenshoter
|
* RDP screen shooter
|
||||||
* RDP client
|
* RDP client
|
||||||
* VNC client
|
* VNC client
|
||||||
* VNC screenshoter
|
* VNC screen shooter
|
||||||
* RSS Player
|
* RSS Player
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
RDPY is fully implemented in python, except the bitmap decompression algorithm which is implemented in C for performance purposes.
|
RDPY is fully implemented in python, except the bitmap uncompression algorithm which is implemented in C for performance purposes.
|
||||||
|
|
||||||
### Dependencies
|
### Depends
|
||||||
|
|
||||||
Dependencies are only needed for pyqt4 binaries :
|
Depends are only needed for pyqt4 binaries :
|
||||||
* rdpy-rdpclient
|
* rdpy-rdpclient
|
||||||
* rdpy-rdpscreenshot
|
* rdpy-rdpscreenshot
|
||||||
* rdpy-vncclient
|
* rdpy-vncclient
|
||||||
@@ -28,7 +28,7 @@ Dependencies are only needed for pyqt4 binaries :
|
|||||||
|
|
||||||
#### Linux
|
#### Linux
|
||||||
|
|
||||||
Example for Debian based systems :
|
Exemple from Debian based system :
|
||||||
```
|
```
|
||||||
sudo apt-get install python-qt4
|
sudo apt-get install python-qt4
|
||||||
```
|
```
|
||||||
@@ -53,7 +53,7 @@ Or use PIP:
|
|||||||
$ pip install rdpy
|
$ pip install rdpy
|
||||||
```
|
```
|
||||||
|
|
||||||
For virtualenv, you will need to link the qt4 library to it:
|
For virtualenv, you need to link qt4 library to it:
|
||||||
```
|
```
|
||||||
$ ln -s /usr/lib/python2.7/dist-packages/PyQt4/ $VIRTUAL_ENV/lib/python2.7/site-packages/
|
$ ln -s /usr/lib/python2.7/dist-packages/PyQt4/ $VIRTUAL_ENV/lib/python2.7/site-packages/
|
||||||
$ ln -s /usr/lib/python2.7/dist-packages/sip.so $VIRTUAL_ENV/lib/python2.7/site-packages/
|
$ ln -s /usr/lib/python2.7/dist-packages/sip.so $VIRTUAL_ENV/lib/python2.7/site-packages/
|
||||||
@@ -65,13 +65,13 @@ RDPY comes with some very useful binaries. These binaries are linux and windows
|
|||||||
|
|
||||||
### rdpy-rdpclient
|
### rdpy-rdpclient
|
||||||
|
|
||||||
rdpy-rdpclient is a simple RDP Qt4 client.
|
rdpy-rdpclient is a simple RDP Qt4 client .
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rdpy-rdpclient.py [-u username] [-p password] [-d domain] [-r rss_ouput_file] [...] 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 in a Recorder Session Scenario, used in rdpy-rdphoneypot.
|
You can use rdpy-rdpclient as Recorder Session Scenario, used in rdpy-rdphoneypot.
|
||||||
|
|
||||||
### rdpy-vncclient
|
### rdpy-vncclient
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ $ rdpy-vncclient.py [-p password] XXX.XXX.XXX.XXX[:5900]
|
|||||||
|
|
||||||
### rdpy-rdpscreenshot
|
### rdpy-rdpscreenshot
|
||||||
|
|
||||||
rdpy-rdpscreenshot saves login screen in file.
|
rdpy-rdpscreenshot save login screen in file.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rdpy-rdpscreenshot.py [-w width] [-l height] [-o output_file_path] XXX.XXX.XXX.XXX[:3389]
|
$ rdpy-rdpscreenshot.py [-w width] [-l height] [-o output_file_path] XXX.XXX.XXX.XXX[:3389]
|
||||||
@@ -91,7 +91,7 @@ $ rdpy-rdpscreenshot.py [-w width] [-l height] [-o output_file_path] XXX.XXX.XXX
|
|||||||
|
|
||||||
### rdpy-vncscreenshot
|
### rdpy-vncscreenshot
|
||||||
|
|
||||||
rdpy-vncscreenshot saves the first screen update in file.
|
rdpy-vncscreenshot save first screen update in file.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rdpy-vncscreenshot.py [-p password] [-o output_file_path] XXX.XXX.XXX.XXX[:5900]
|
$ rdpy-vncscreenshot.py [-p password] [-o output_file_path] XXX.XXX.XXX.XXX[:5900]
|
||||||
@@ -100,25 +100,24 @@ $ rdpy-vncscreenshot.py [-p password] [-o output_file_path] XXX.XXX.XXX.XXX[:590
|
|||||||
### rdpy-rdpmitm
|
### rdpy-rdpmitm
|
||||||
|
|
||||||
rdpy-rdpmitm is a RDP proxy allows you to do a Man In The Middle attack on 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 replayed by rdpy-rssplayer.
|
Record Session Scenario into rss file which can be replay by rdpy-rssplayer.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rdpy-rdpmitm.py -o output_dir [-l listen_port] [-k private_key_file_path] [-c certificate_file_path] [-r (for XP or server 2003 client)] target_host[:target_port]
|
$ rdpy-rdpmitm.py -o output_dir [-l listen_port] [-k private_key_file_path] [-c certificate_file_path] [-r (for XP or server 2003 client)] target_host[:target_port]
|
||||||
```
|
```
|
||||||
|
|
||||||
Output directory is used to save the rss file with following format (YYYYMMDDHHMMSS_ip_index.rss)
|
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.
|
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-rdphoneypot
|
### rdpy-rdphoneypot
|
||||||
|
|
||||||
rdpy-rdphoneypot is an RDP honey Pot. Use Recorded Session Scenario to replay scenario through RDP Protocol.
|
rdpy-rdphoneypot is a RDP honey Pot. Use Recorded Session Scenario to replay scenario through RDP Protocol.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rdpy-rdphoneypot.py [-l listen_port] [-k private_key_file_path] [-c certificate_file_path] rss_file_path_1 ... rss_file_path_N
|
$ 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.
|
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.
|
||||||
You can specify more than one files to match more common screen size.
|
|
||||||
|
|
||||||
### rdpy-rssplayer
|
### rdpy-rssplayer
|
||||||
|
|
||||||
@@ -130,11 +129,11 @@ $ rdpy-rssplayer.py rss_file_path
|
|||||||
|
|
||||||
## RDPY Qt Widget
|
## RDPY Qt Widget
|
||||||
|
|
||||||
RDPY can also be used as Qt widget through rdpy.ui.qt4.QRemoteDesktop class. It can be embedded in your own Qt application. qt4reactor must be used in your app for Twisted and Qt to work together. For more details, see sources of rdpy-rdpclient.
|
RDPY can also be used as Qt widget throw rdpy.ui.qt4.QRemoteDesktop class. It can be embedded in your own Qt application. qt4reactor must be used in your app for Twisted and Qt to work together. For more details, see sources of rdpy-rdpclient.
|
||||||
|
|
||||||
## RDPY library
|
## RDPY library
|
||||||
|
|
||||||
In a nutshell RDPY can be used as a protocol library with a twisted engine.
|
In a nutshell the RDPY can be used as a protocol library with a twisted engine.
|
||||||
|
|
||||||
### Simple RDP Client
|
### Simple RDP Client
|
||||||
|
|
||||||
@@ -145,14 +144,14 @@ class MyRDPFactory(rdp.ClientFactory):
|
|||||||
|
|
||||||
def clientConnectionLost(self, connector, reason):
|
def clientConnectionLost(self, connector, reason):
|
||||||
reactor.stop()
|
reactor.stop()
|
||||||
|
|
||||||
def clientConnectionFailed(self, connector, reason):
|
def clientConnectionFailed(self, connector, reason):
|
||||||
reactor.stop()
|
reactor.stop()
|
||||||
|
|
||||||
def buildObserver(self, controller, addr):
|
def buildObserver(self, controller, addr):
|
||||||
|
|
||||||
class MyObserver(rdp.RDPClientObserver)
|
class MyObserver(rdp.RDPClientObserver)
|
||||||
|
|
||||||
def onReady(self):
|
def onReady(self):
|
||||||
"""
|
"""
|
||||||
@summary: Call when stack is ready
|
@summary: Call when stack is ready
|
||||||
@@ -161,7 +160,7 @@ class MyRDPFactory(rdp.ClientFactory):
|
|||||||
self._controller.sendKeyEventUnicode(ord(unicode("r".toUtf8(), encoding="UTF-8")), True)
|
self._controller.sendKeyEventUnicode(ord(unicode("r".toUtf8(), encoding="UTF-8")), True)
|
||||||
#mouse move and click at pixel 200x200
|
#mouse move and click at pixel 200x200
|
||||||
self._controller.sendPointerEvent(200, 200, 1, true)
|
self._controller.sendPointerEvent(200, 200, 1, true)
|
||||||
|
|
||||||
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||||
"""
|
"""
|
||||||
@summary: Notify bitmap update
|
@summary: Notify bitmap update
|
||||||
@@ -175,7 +174,7 @@ class MyRDPFactory(rdp.ClientFactory):
|
|||||||
@param isCompress: use RLE compression
|
@param isCompress: use RLE compression
|
||||||
@param data: bitmap data
|
@param data: bitmap data
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def onClose(self):
|
def onClose(self):
|
||||||
"""
|
"""
|
||||||
@summary: Call when stack is close
|
@summary: Call when stack is close
|
||||||
@@ -195,15 +194,15 @@ from rdpy.protocol.rdp import rdp
|
|||||||
class MyRDPFactory(rdp.ServerFactory):
|
class MyRDPFactory(rdp.ServerFactory):
|
||||||
|
|
||||||
def buildObserver(self, controller, addr):
|
def buildObserver(self, controller, addr):
|
||||||
|
|
||||||
class MyObserver(rdp.RDPServerObserver)
|
class MyObserver(rdp.RDPServerObserver)
|
||||||
|
|
||||||
def onReady(self):
|
def onReady(self):
|
||||||
"""
|
"""
|
||||||
@summary: Call when server is ready
|
@summary: Call when server is ready
|
||||||
to send and receive messages
|
to send and receive messages
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def onKeyEventScancode(self, code, isPressed):
|
def onKeyEventScancode(self, code, isPressed):
|
||||||
"""
|
"""
|
||||||
@summary: Event call when a keyboard event is catch in scan code format
|
@summary: Event call when a keyboard event is catch in scan code format
|
||||||
@@ -211,7 +210,7 @@ class MyRDPFactory(rdp.ServerFactory):
|
|||||||
@param isPressed: True if key is down
|
@param isPressed: True if key is down
|
||||||
@see: rdp.RDPServerObserver.onKeyEventScancode
|
@see: rdp.RDPServerObserver.onKeyEventScancode
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def onKeyEventUnicode(self, code, isPressed):
|
def onKeyEventUnicode(self, code, isPressed):
|
||||||
"""
|
"""
|
||||||
@summary: Event call when a keyboard event is catch in unicode format
|
@summary: Event call when a keyboard event is catch in unicode format
|
||||||
@@ -219,7 +218,7 @@ class MyRDPFactory(rdp.ServerFactory):
|
|||||||
@param isPressed: True if key is down
|
@param isPressed: True if key is down
|
||||||
@see: rdp.RDPServerObserver.onKeyEventUnicode
|
@see: rdp.RDPServerObserver.onKeyEventUnicode
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def onPointerEvent(self, x, y, button, isPressed):
|
def onPointerEvent(self, x, y, button, isPressed):
|
||||||
"""
|
"""
|
||||||
@summary: Event call on mouse event
|
@summary: Event call on mouse event
|
||||||
@@ -229,7 +228,7 @@ class MyRDPFactory(rdp.ServerFactory):
|
|||||||
@param isPressed: True if mouse button is pressed
|
@param isPressed: True if mouse button is pressed
|
||||||
@see: rdp.RDPServerObserver.onPointerEvent
|
@see: rdp.RDPServerObserver.onPointerEvent
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def onClose(self):
|
def onClose(self):
|
||||||
"""
|
"""
|
||||||
@summary: Call when human client close connection
|
@summary: Call when human client close connection
|
||||||
@@ -245,19 +244,19 @@ reactor.run()
|
|||||||
|
|
||||||
### Simple VNC Client
|
### Simple VNC Client
|
||||||
```python
|
```python
|
||||||
from rdpy.protocol.rfb import rfb
|
from rdpy.protocol.rfb import rdp
|
||||||
|
|
||||||
class MyRFBFactory(rfb.ClientFactory):
|
class MyRFBFactory(rfb.ClientFactory):
|
||||||
|
|
||||||
def clientConnectionLost(self, connector, reason):
|
def clientConnectionLost(self, connector, reason):
|
||||||
reactor.stop()
|
reactor.stop()
|
||||||
|
|
||||||
def clientConnectionFailed(self, connector, reason):
|
def clientConnectionFailed(self, connector, reason):
|
||||||
reactor.stop()
|
reactor.stop()
|
||||||
|
|
||||||
def buildObserver(self, controller, addr):
|
def buildObserver(self, controller, addr):
|
||||||
class MyObserver(rfb.RFBClientObserver)
|
class MyObserver(rfb.RFBClientObserver)
|
||||||
|
|
||||||
def onReady(self):
|
def onReady(self):
|
||||||
"""
|
"""
|
||||||
@summary: Event when network stack is ready to receive or send event
|
@summary: Event when network stack is ready to receive or send event
|
||||||
@@ -274,18 +273,18 @@ class MyRFBFactory(rfb.ClientFactory):
|
|||||||
@param encoding: encoding type rfb.message.Encoding
|
@param encoding: encoding type rfb.message.Encoding
|
||||||
@param data: image data in accordance with pixel format and encoding
|
@param data: image data in accordance with pixel format and encoding
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def onCutText(self, text):
|
def onCutText(self, text):
|
||||||
"""
|
"""
|
||||||
@summary: event when server send cut text event
|
@summary: event when server send cut text event
|
||||||
@param text: text received
|
@param text: text received
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def onBell(self):
|
def onBell(self):
|
||||||
"""
|
"""
|
||||||
@summary: event when server send biiip
|
@summary: event when server send biiip
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def onClose(self):
|
def onClose(self):
|
||||||
"""
|
"""
|
||||||
@summary: Call when stack is close
|
@summary: Call when stack is close
|
||||||
|
|||||||
@@ -31,15 +31,13 @@ from twisted.internet import reactor
|
|||||||
log._LOG_LEVEL = log.Level.INFO
|
log._LOG_LEVEL = log.Level.INFO
|
||||||
|
|
||||||
class HoneyPotServer(rdp.RDPServerObserver):
|
class HoneyPotServer(rdp.RDPServerObserver):
|
||||||
def __init__(self, controller, rssFileSizeList):
|
def __init__(self, controller, rssFile):
|
||||||
"""
|
"""
|
||||||
@param controller: {RDPServerController}
|
@param controller: {RDPServerController}
|
||||||
@param rssFileSizeList: {Tuple} Tuple(Tuple(width, height), rssFilePath)
|
|
||||||
"""
|
"""
|
||||||
rdp.RDPServerObserver.__init__(self, controller)
|
rdp.RDPServerObserver.__init__(self, controller)
|
||||||
self._rssFileSizeList = rssFileSizeList
|
self._rssFile = rssFile
|
||||||
self._dx, self._dy = 0, 0
|
self._dx, self._dy = 0, 0
|
||||||
self._rssFile = None
|
|
||||||
|
|
||||||
def onReady(self):
|
def onReady(self):
|
||||||
"""
|
"""
|
||||||
@@ -49,14 +47,6 @@ class HoneyPotServer(rdp.RDPServerObserver):
|
|||||||
restart a connection sequence
|
restart a connection sequence
|
||||||
@see: rdp.RDPServerObserver.onReady
|
@see: rdp.RDPServerObserver.onReady
|
||||||
"""
|
"""
|
||||||
if self._rssFile is None:
|
|
||||||
#compute which RSS file to keep
|
|
||||||
width, height = self._controller.getScreen()
|
|
||||||
size = width * height
|
|
||||||
rssFilePath = sorted(self._rssFileSizeList, key = lambda x: abs(x[0][0] * x[0][1] - size))[0][1]
|
|
||||||
log.info("select file (%s, %s) -> %s"%(width, height, rssFilePath))
|
|
||||||
self._rssFile = rss.createReader(rssFilePath)
|
|
||||||
|
|
||||||
domain, username, password = self._controller.getCredentials()
|
domain, username, password = self._controller.getCredentials()
|
||||||
hostname = self._controller.getHostname()
|
hostname = self._controller.getHostname()
|
||||||
log.info("""Credentials:
|
log.info("""Credentials:
|
||||||
@@ -99,7 +89,7 @@ class HoneyPotServer(rdp.RDPServerObserver):
|
|||||||
clientSize = nextEvent.event.width.value, nextEvent.event.height.value
|
clientSize = nextEvent.event.width.value, nextEvent.event.height.value
|
||||||
serverSize = self._controller.getScreen()
|
serverSize = self._controller.getScreen()
|
||||||
|
|
||||||
self._dx, self._dy = (max(0, serverSize[0] - clientSize[0]) / 2), max(0, (serverSize[1] - clientSize[1]) / 2)
|
self._dx, self._dy = (serverSize[0] - clientSize[0]) / 2, (serverSize[1] - clientSize[1]) / 2
|
||||||
#restart connection sequence
|
#restart connection sequence
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -110,14 +100,14 @@ class HoneyPotServerFactory(rdp.ServerFactory):
|
|||||||
"""
|
"""
|
||||||
@summary: Factory on listening events
|
@summary: Factory on listening events
|
||||||
"""
|
"""
|
||||||
def __init__(self, rssFileSizeList, privateKeyFilePath, certificateFilePath):
|
def __init__(self, rssFilePath, privateKeyFilePath, certificateFilePath):
|
||||||
"""
|
"""
|
||||||
@param rssFileSizeList: {Tuple} Tuple(Tuple(width, height), rssFilePath)
|
@param rssFilePath: Recorded Session Scenario File path
|
||||||
@param privateKeyFilePath: {str} file contain server private key (if none -> back to standard RDP security)
|
@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 certificateFilePath: {str} file contain server certificate (if none -> back to standard RDP security)
|
||||||
"""
|
"""
|
||||||
rdp.ServerFactory.__init__(self, 16, privateKeyFilePath, certificateFilePath)
|
rdp.ServerFactory.__init__(self, 16, privateKeyFilePath, certificateFilePath)
|
||||||
self._rssFileSizeList = rssFileSizeList
|
self._rssFilePath = rssFilePath
|
||||||
|
|
||||||
def buildObserver(self, controller, addr):
|
def buildObserver(self, controller, addr):
|
||||||
"""
|
"""
|
||||||
@@ -126,27 +116,14 @@ class HoneyPotServerFactory(rdp.ServerFactory):
|
|||||||
@see: rdp.ServerFactory.buildObserver
|
@see: rdp.ServerFactory.buildObserver
|
||||||
"""
|
"""
|
||||||
log.info("Connection from %s:%s"%(addr.host, addr.port))
|
log.info("Connection from %s:%s"%(addr.host, addr.port))
|
||||||
return HoneyPotServer(controller, self._rssFileSizeList)
|
return HoneyPotServer(controller, rss.createReader(self._rssFilePath))
|
||||||
|
|
||||||
def readSize(filePath):
|
|
||||||
"""
|
|
||||||
@summary: read size event in rss file
|
|
||||||
@param filePath: path of rss file
|
|
||||||
"""
|
|
||||||
r = rss.createReader(filePath)
|
|
||||||
while True:
|
|
||||||
e = r.nextEvent()
|
|
||||||
if e is None:
|
|
||||||
return None
|
|
||||||
elif e.type.value == rss.EventType.SCREEN:
|
|
||||||
return e.event.width.value, e.event.height.value
|
|
||||||
|
|
||||||
def help():
|
def help():
|
||||||
"""
|
"""
|
||||||
@summary: Print help in console
|
@summary: Print help in console
|
||||||
"""
|
"""
|
||||||
print """
|
print """
|
||||||
Usage: rdpy-rdphoneypot.py rss_filepath(1..n)
|
Usage: rdpy-rdphoneypot.py rss_filepath
|
||||||
[-l listen_port default 3389]
|
[-l listen_port default 3389]
|
||||||
[-k private_key_file_path (mandatory for SSL)]
|
[-k private_key_file_path (mandatory for SSL)]
|
||||||
[-c certificate_file_path (mandatory for SSL)]
|
[-c certificate_file_path (mandatory for SSL)]
|
||||||
@@ -156,7 +133,6 @@ if __name__ == '__main__':
|
|||||||
listen = "3389"
|
listen = "3389"
|
||||||
privateKeyFilePath = None
|
privateKeyFilePath = None
|
||||||
certificateFilePath = None
|
certificateFilePath = None
|
||||||
rssFileSizeList = []
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
opts, args = getopt.getopt(sys.argv[1:], "hl:k:c:")
|
opts, args = getopt.getopt(sys.argv[1:], "hl:k:c:")
|
||||||
@@ -173,12 +149,5 @@ if __name__ == '__main__':
|
|||||||
elif opt == "-c":
|
elif opt == "-c":
|
||||||
certificateFilePath = arg
|
certificateFilePath = arg
|
||||||
|
|
||||||
#build size map
|
reactor.listenTCP(int(listen), HoneyPotServerFactory(args[0], privateKeyFilePath, certificateFilePath))
|
||||||
log.info("Build size map")
|
|
||||||
for arg in args:
|
|
||||||
size = readSize(arg)
|
|
||||||
rssFileSizeList.append((size, arg))
|
|
||||||
log.info("(%s, %s) -> %s"%(size[0], size[1], arg))
|
|
||||||
|
|
||||||
reactor.listenTCP(int(listen), HoneyPotServerFactory(rssFileSizeList, privateKeyFilePath, certificateFilePath))
|
|
||||||
reactor.run()
|
reactor.run()
|
||||||
@@ -121,7 +121,7 @@ class RDPScreenShotFactory(rdp.ClientFactory):
|
|||||||
"""
|
"""
|
||||||
@summary: callback use when bitmap is received
|
@summary: callback use when bitmap is received
|
||||||
"""
|
"""
|
||||||
image = RDPBitmapToQtImage(width, height, bitsPerPixel, isCompress, data);
|
image = RDPBitmapToQtImage(destLeft, width, height, bitsPerPixel, isCompress, data);
|
||||||
with QtGui.QPainter(self._buffer) as qp:
|
with QtGui.QPainter(self._buffer) as qp:
|
||||||
#draw image
|
#draw image
|
||||||
qp.drawImage(destLeft, destTop, image, 0, 0, destRight - destLeft + 1, destBottom - destTop + 1)
|
qp.drawImage(destLeft, destTop, image, 0, 0, destRight - destLeft + 1, destBottom - destTop + 1)
|
||||||
|
|||||||
@@ -252,5 +252,4 @@ def createReader(path):
|
|||||||
@param path: {str} path of input file
|
@param path: {str} path of input file
|
||||||
@return: {FileReader}
|
@return: {FileReader}
|
||||||
"""
|
"""
|
||||||
with open(path, "rb") as f:
|
return FileReader(open(path, "rb"))
|
||||||
return FileReader(f)
|
|
||||||
@@ -257,7 +257,7 @@ class SimpleType(Type, CallableValue):
|
|||||||
@raise InvalidSize: if there is not enough data in stream
|
@raise InvalidSize: if there is not enough data in stream
|
||||||
"""
|
"""
|
||||||
if s.dataLen() < self._typeSize:
|
if s.dataLen() < self._typeSize:
|
||||||
raise InvalidSize("Stream is too small to read expected SimpleType")
|
raise InvalidSize("Stream is too small to read expected Simple")
|
||||||
self.value = struct.unpack(self._structFormat, s.read(self._typeSize))[0]
|
self.value = struct.unpack(self._structFormat, s.read(self._typeSize))[0]
|
||||||
|
|
||||||
def mask(self):
|
def mask(self):
|
||||||
@@ -498,9 +498,6 @@ class CompositeType(Type):
|
|||||||
@summary: Call sizeof on each sub type
|
@summary: Call sizeof on each sub type
|
||||||
@return: sum of sizeof of each Type attributes
|
@return: sum of sizeof of each Type attributes
|
||||||
"""
|
"""
|
||||||
if self._is_readed and not self._readLen is None:
|
|
||||||
return self._readLen.value
|
|
||||||
|
|
||||||
size = 0
|
size = 0
|
||||||
for name in self._typeName:
|
for name in self._typeName:
|
||||||
size += sizeof(self.__dict__[name])
|
size += sizeof(self.__dict__[name])
|
||||||
@@ -811,7 +808,7 @@ class String(Type, CallableValue):
|
|||||||
self.value = s.getvalue()[s.pos:]
|
self.value = s.getvalue()[s.pos:]
|
||||||
else:
|
else:
|
||||||
self.value = ""
|
self.value = ""
|
||||||
while self.value[-len(self._until):] != self._until and s.dataLen() != 0:
|
while self.value[-len(self._until):] != self._until or s.dataLen() == 0:
|
||||||
self.value += s.read(1)
|
self.value += s.read(1)
|
||||||
else:
|
else:
|
||||||
self.value = s.read(self._readLen.value)
|
self.value = s.read(self._readLen.value)
|
||||||
|
|||||||
@@ -256,17 +256,17 @@ class ClientCoreData(CompositeType):
|
|||||||
self.keyboardType = UInt32Le(KeyboardType.IBM_101_102_KEYS)
|
self.keyboardType = UInt32Le(KeyboardType.IBM_101_102_KEYS)
|
||||||
self.keyboardSubType = UInt32Le(0)
|
self.keyboardSubType = UInt32Le(0)
|
||||||
self.keyboardFnKeys = UInt32Le(12)
|
self.keyboardFnKeys = UInt32Le(12)
|
||||||
self.imeFileName = String("\x00"*64, readLen = UInt8(64), optional = True)
|
self.imeFileName = String("\x00"*64, readLen = UInt8(64))
|
||||||
self.postBeta2ColorDepth = UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP, optional = True)
|
self.postBeta2ColorDepth = UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP)
|
||||||
self.clientProductId = UInt16Le(1, optional = True)
|
self.clientProductId = UInt16Le(1)
|
||||||
self.serialNumber = UInt32Le(0, optional = True)
|
self.serialNumber = UInt32Le(0)
|
||||||
self.highColorDepth = UInt16Le(HighColor.HIGH_COLOR_24BPP, optional = True)
|
self.highColorDepth = UInt16Le(HighColor.HIGH_COLOR_24BPP)
|
||||||
self.supportedColorDepths = UInt16Le(Support.RNS_UD_15BPP_SUPPORT | Support.RNS_UD_16BPP_SUPPORT | Support.RNS_UD_24BPP_SUPPORT | Support.RNS_UD_32BPP_SUPPORT, optional = True)
|
self.supportedColorDepths = UInt16Le(Support.RNS_UD_15BPP_SUPPORT | Support.RNS_UD_16BPP_SUPPORT | Support.RNS_UD_24BPP_SUPPORT | Support.RNS_UD_32BPP_SUPPORT)
|
||||||
self.earlyCapabilityFlags = UInt16Le(CapabilityFlags.RNS_UD_CS_SUPPORT_ERRINFO_PDU, optional = True)
|
self.earlyCapabilityFlags = UInt16Le(CapabilityFlags.RNS_UD_CS_SUPPORT_ERRINFO_PDU)
|
||||||
self.clientDigProductId = String("\x00"*64, readLen = UInt8(64), optional = True)
|
self.clientDigProductId = String("\x00"*64, readLen = UInt8(64))
|
||||||
self.connectionType = UInt8(optional = True)
|
self.connectionType = UInt8()
|
||||||
self.pad1octet = UInt8(optional = True)
|
self.pad1octet = UInt8()
|
||||||
self.serverSelectedProtocol = UInt32Le(optional = True)
|
self.serverSelectedProtocol = UInt32Le()
|
||||||
|
|
||||||
class ServerCoreData(CompositeType):
|
class ServerCoreData(CompositeType):
|
||||||
"""
|
"""
|
||||||
@@ -278,8 +278,7 @@ class ServerCoreData(CompositeType):
|
|||||||
def __init__(self, readLen = None):
|
def __init__(self, readLen = None):
|
||||||
CompositeType.__init__(self, readLen = readLen)
|
CompositeType.__init__(self, readLen = readLen)
|
||||||
self.rdpVersion = UInt32Le(Version.RDP_VERSION_5_PLUS)
|
self.rdpVersion = UInt32Le(Version.RDP_VERSION_5_PLUS)
|
||||||
self.clientRequestedProtocol = UInt32Le(optional = True)
|
self.clientRequestedProtocol = UInt32Le()
|
||||||
self.earlyCapabilityFlags = UInt32Le(optional = True)
|
|
||||||
|
|
||||||
class ClientSecurityData(CompositeType):
|
class ClientSecurityData(CompositeType):
|
||||||
"""
|
"""
|
||||||
@@ -354,8 +353,8 @@ class ProprietaryServerCertificate(CompositeType):
|
|||||||
self.wPublicKeyBlobLen = UInt16Le(lambda:sizeof(self.PublicKeyBlob))
|
self.wPublicKeyBlobLen = UInt16Le(lambda:sizeof(self.PublicKeyBlob))
|
||||||
self.PublicKeyBlob = RSAPublicKey(readLen = self.wPublicKeyBlobLen)
|
self.PublicKeyBlob = RSAPublicKey(readLen = self.wPublicKeyBlobLen)
|
||||||
self.wSignatureBlobType = UInt16Le(0x0008, constant = True)
|
self.wSignatureBlobType = UInt16Le(0x0008, constant = True)
|
||||||
self.wSignatureBlobLen = UInt16Le(lambda:(sizeof(self.SignatureBlob) + sizeof(self.padding)))
|
self.wSignatureBlobLen = UInt16Le(lambda:(sizeof(self.SignatureBlob) - 8))
|
||||||
self.SignatureBlob = String(readLen = UInt16Le(lambda:(self.wSignatureBlobLen.value - sizeof(self.padding))))
|
self.SignatureBlob = String(readLen = self.wSignatureBlobLen)
|
||||||
self.padding = String(b"\x00" * 8, readLen = UInt8(8))
|
self.padding = String(b"\x00" * 8, readLen = UInt8(8))
|
||||||
|
|
||||||
def getPublicKey(self):
|
def getPublicKey(self):
|
||||||
@@ -381,7 +380,7 @@ class ProprietaryServerCertificate(CompositeType):
|
|||||||
md5Digest = md5.new()
|
md5Digest = md5.new()
|
||||||
md5Digest.update(s.getvalue())
|
md5Digest.update(s.getvalue())
|
||||||
|
|
||||||
return md5Digest.digest() + "\x00" + "\xff" * 45 + "\x01"
|
return md5Digest.digest() + "\x00" + "\xff" * 46 + "\x01"
|
||||||
|
|
||||||
def sign(self):
|
def sign(self):
|
||||||
"""
|
"""
|
||||||
@@ -464,7 +463,7 @@ class ChannelDef(CompositeType):
|
|||||||
|
|
||||||
class ClientNetworkData(CompositeType):
|
class ClientNetworkData(CompositeType):
|
||||||
"""
|
"""
|
||||||
@summary: GCC client network block
|
GCC client network block
|
||||||
All channels asked by client are listed here
|
All channels asked by client are listed here
|
||||||
@see: http://msdn.microsoft.com/en-us/library/cc240512.aspx
|
@see: http://msdn.microsoft.com/en-us/library/cc240512.aspx
|
||||||
"""
|
"""
|
||||||
@@ -477,7 +476,7 @@ class ClientNetworkData(CompositeType):
|
|||||||
|
|
||||||
class ServerNetworkData(CompositeType):
|
class ServerNetworkData(CompositeType):
|
||||||
"""
|
"""
|
||||||
@summary: GCC server network block
|
GCC server network block
|
||||||
All channels asked by client are listed here
|
All channels asked by client are listed here
|
||||||
@see: All channels asked by client are listed here
|
@see: All channels asked by client are listed here
|
||||||
"""
|
"""
|
||||||
@@ -492,7 +491,7 @@ class ServerNetworkData(CompositeType):
|
|||||||
|
|
||||||
class Settings(CompositeType):
|
class Settings(CompositeType):
|
||||||
"""
|
"""
|
||||||
@summary: Class which group all clients settings supported by RDPY
|
Class which group all clients settings supported by RDPY
|
||||||
"""
|
"""
|
||||||
def __init__(self, init = [], readLen = None):
|
def __init__(self, init = [], readLen = None):
|
||||||
CompositeType.__init__(self, readLen = readLen)
|
CompositeType.__init__(self, readLen = readLen)
|
||||||
@@ -519,21 +518,21 @@ class Settings(CompositeType):
|
|||||||
|
|
||||||
def clientSettings():
|
def clientSettings():
|
||||||
"""
|
"""
|
||||||
@summary: Build settings for client
|
Build settings for client
|
||||||
@return: Settings
|
@return: Settings
|
||||||
"""
|
"""
|
||||||
return Settings([ClientCoreData(), ClientNetworkData(), ClientSecurityData()])
|
return Settings([ClientCoreData(), ClientNetworkData(), ClientSecurityData()])
|
||||||
|
|
||||||
def serverSettings():
|
def serverSettings():
|
||||||
"""
|
"""
|
||||||
@summary: Build settings for server
|
Build settings for server
|
||||||
@return Settings
|
@return Settings
|
||||||
"""
|
"""
|
||||||
return Settings([ServerCoreData(), ServerSecurityData(), ServerNetworkData()])
|
return Settings([ServerCoreData(), ServerSecurityData(), ServerNetworkData()])
|
||||||
|
|
||||||
def readConferenceCreateRequest(s):
|
def readConferenceCreateRequest(s):
|
||||||
"""
|
"""
|
||||||
@summary: Read a response from client
|
Read a response from client
|
||||||
GCC create request
|
GCC create request
|
||||||
@param s: Stream
|
@param s: Stream
|
||||||
@param client settings (Settings)
|
@param client settings (Settings)
|
||||||
@@ -560,7 +559,7 @@ def readConferenceCreateRequest(s):
|
|||||||
|
|
||||||
def readConferenceCreateResponse(s):
|
def readConferenceCreateResponse(s):
|
||||||
"""
|
"""
|
||||||
@summary: Read response from server
|
Read response from server
|
||||||
and return server settings read from this response
|
and return server settings read from this response
|
||||||
@param s: Stream
|
@param s: Stream
|
||||||
@return: ServerSettings
|
@return: ServerSettings
|
||||||
@@ -584,7 +583,7 @@ def readConferenceCreateResponse(s):
|
|||||||
|
|
||||||
def writeConferenceCreateRequest(userData):
|
def writeConferenceCreateRequest(userData):
|
||||||
"""
|
"""
|
||||||
@summary: Write conference create request structure
|
Write conference create request structure
|
||||||
@param userData: Settings for client
|
@param userData: Settings for client
|
||||||
@return: GCC packet
|
@return: GCC packet
|
||||||
"""
|
"""
|
||||||
@@ -599,7 +598,7 @@ def writeConferenceCreateRequest(userData):
|
|||||||
|
|
||||||
def writeConferenceCreateResponse(serverData):
|
def writeConferenceCreateResponse(serverData):
|
||||||
"""
|
"""
|
||||||
@summary: Write a conference create response packet
|
Write a conference create response packet
|
||||||
@param serverData: Settings for server
|
@param serverData: Settings for server
|
||||||
@return: gcc packet
|
@return: gcc packet
|
||||||
"""
|
"""
|
||||||
@@ -608,6 +607,6 @@ def writeConferenceCreateResponse(serverData):
|
|||||||
|
|
||||||
return (per.writeChoice(0), per.writeObjectIdentifier(t124_02_98_oid),
|
return (per.writeChoice(0), per.writeObjectIdentifier(t124_02_98_oid),
|
||||||
per.writeLength(len(serverDataStream.getvalue()) + 14), per.writeChoice(0x14),
|
per.writeLength(len(serverDataStream.getvalue()) + 14), per.writeChoice(0x14),
|
||||||
per.writeInteger16(0x79F3, 1001), per.writeInteger(1), per.writeEnumerates(0),
|
per.writeInteger16(0x79F3, 1001), per.writeInteger(1), per.writeEnumerates(16),
|
||||||
per.writeNumberOfSet(1), per.writeChoice(0xc0),
|
per.writeNumberOfSet(1), per.writeChoice(0xc0),
|
||||||
per.writeOctetStream(h221_sc_key, 4), per.writeOctetStream(serverDataStream.getvalue()))
|
per.writeOctetStream(h221_sc_key, 4), per.writeOctetStream(serverDataStream.getvalue()))
|
||||||
@@ -97,8 +97,8 @@ class LicenseBinaryBlob(CompositeType):
|
|||||||
@summary: Blob use by license manager to exchange security data
|
@summary: Blob use by license manager to exchange security data
|
||||||
@see: http://msdn.microsoft.com/en-us/library/cc240481.aspx
|
@see: http://msdn.microsoft.com/en-us/library/cc240481.aspx
|
||||||
"""
|
"""
|
||||||
def __init__(self, blobType = BinaryBlobType.BB_ANY_BLOB, optional = False):
|
def __init__(self, blobType = BinaryBlobType.BB_ANY_BLOB):
|
||||||
CompositeType.__init__(self, optional = optional)
|
CompositeType.__init__(self)
|
||||||
self.wBlobType = UInt16Le(blobType, constant = True if blobType != BinaryBlobType.BB_ANY_BLOB else False)
|
self.wBlobType = UInt16Le(blobType, constant = True if blobType != BinaryBlobType.BB_ANY_BLOB else False)
|
||||||
self.wBlobLen = UInt16Le(lambda:sizeof(self.blobData))
|
self.wBlobLen = UInt16Le(lambda:sizeof(self.blobData))
|
||||||
self.blobData = String(readLen = self.wBlobLen)
|
self.blobData = String(readLen = self.wBlobLen)
|
||||||
@@ -110,11 +110,11 @@ class LicensingErrorMessage(CompositeType):
|
|||||||
"""
|
"""
|
||||||
_MESSAGE_TYPE_ = MessageType.ERROR_ALERT
|
_MESSAGE_TYPE_ = MessageType.ERROR_ALERT
|
||||||
|
|
||||||
def __init__(self, readLen = None):
|
def __init__(self):
|
||||||
CompositeType.__init__(self, readLen = readLen)
|
CompositeType.__init__(self)
|
||||||
self.dwErrorCode = UInt32Le()
|
self.dwErrorCode = UInt32Le()
|
||||||
self.dwStateTransition = UInt32Le()
|
self.dwStateTransition = UInt32Le()
|
||||||
self.blob = LicenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB)
|
self.blob = LicenseBinaryBlob(BinaryBlobType.BB_ERROR_BLOB)
|
||||||
|
|
||||||
class ProductInformation(CompositeType):
|
class ProductInformation(CompositeType):
|
||||||
"""
|
"""
|
||||||
@@ -159,8 +159,8 @@ class ServerLicenseRequest(CompositeType):
|
|||||||
"""
|
"""
|
||||||
_MESSAGE_TYPE_ = MessageType.LICENSE_REQUEST
|
_MESSAGE_TYPE_ = MessageType.LICENSE_REQUEST
|
||||||
|
|
||||||
def __init__(self, readLen = None):
|
def __init__(self):
|
||||||
CompositeType.__init__(self, readLen = readLen)
|
CompositeType.__init__(self)
|
||||||
self.serverRandom = String("\x00" * 32, readLen = UInt8(32))
|
self.serverRandom = String("\x00" * 32, readLen = UInt8(32))
|
||||||
self.productInfo = ProductInformation()
|
self.productInfo = ProductInformation()
|
||||||
self.keyExchangeList = LicenseBinaryBlob(BinaryBlobType.BB_KEY_EXCHG_ALG_BLOB)
|
self.keyExchangeList = LicenseBinaryBlob(BinaryBlobType.BB_KEY_EXCHG_ALG_BLOB)
|
||||||
@@ -175,8 +175,8 @@ class ClientNewLicenseRequest(CompositeType):
|
|||||||
"""
|
"""
|
||||||
_MESSAGE_TYPE_ = MessageType.NEW_LICENSE_REQUEST
|
_MESSAGE_TYPE_ = MessageType.NEW_LICENSE_REQUEST
|
||||||
|
|
||||||
def __init__(self, readLen = None):
|
def __init__(self):
|
||||||
CompositeType.__init__(self, readLen = readLen)
|
CompositeType.__init__(self)
|
||||||
#RSA and must be only RSA
|
#RSA and must be only RSA
|
||||||
self.preferredKeyExchangeAlg = UInt32Le(0x00000001, constant = True)
|
self.preferredKeyExchangeAlg = UInt32Le(0x00000001, constant = True)
|
||||||
#pure microsoft client ;-)
|
#pure microsoft client ;-)
|
||||||
@@ -194,8 +194,8 @@ class ServerPlatformChallenge(CompositeType):
|
|||||||
"""
|
"""
|
||||||
_MESSAGE_TYPE_ = MessageType.PLATFORM_CHALLENGE
|
_MESSAGE_TYPE_ = MessageType.PLATFORM_CHALLENGE
|
||||||
|
|
||||||
def __init__(self, readLen = None):
|
def __init__(self):
|
||||||
CompositeType.__init__(self, readLen = readLen)
|
CompositeType.__init__(self)
|
||||||
self.connectFlags = UInt32Le()
|
self.connectFlags = UInt32Le()
|
||||||
self.encryptedPlatformChallenge = LicenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB)
|
self.encryptedPlatformChallenge = LicenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB)
|
||||||
self.MACData = String(readLen = UInt8(16))
|
self.MACData = String(readLen = UInt8(16))
|
||||||
@@ -207,8 +207,8 @@ class ClientPLatformChallengeResponse(CompositeType):
|
|||||||
"""
|
"""
|
||||||
_MESSAGE_TYPE_ = MessageType.PLATFORM_CHALLENGE_RESPONSE
|
_MESSAGE_TYPE_ = MessageType.PLATFORM_CHALLENGE_RESPONSE
|
||||||
|
|
||||||
def __init__(self, readLen = None):
|
def __init__(self):
|
||||||
CompositeType.__init__(self, readLen = readLen)
|
CompositeType.__init__(self)
|
||||||
self.encryptedPlatformChallengeResponse = LicenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB)
|
self.encryptedPlatformChallengeResponse = LicenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB)
|
||||||
self.encryptedHWID = LicenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB)
|
self.encryptedHWID = LicenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB)
|
||||||
self.MACData = String(readLen = UInt8(16))
|
self.MACData = String(readLen = UInt8(16))
|
||||||
@@ -231,7 +231,7 @@ class LicPacket(CompositeType):
|
|||||||
"""
|
"""
|
||||||
for c in [LicensingErrorMessage, ServerLicenseRequest, ClientNewLicenseRequest, ServerPlatformChallenge, ClientPLatformChallengeResponse]:
|
for c in [LicensingErrorMessage, ServerLicenseRequest, ClientNewLicenseRequest, ServerPlatformChallenge, ClientPLatformChallengeResponse]:
|
||||||
if self.bMsgtype.value == c._MESSAGE_TYPE_:
|
if self.bMsgtype.value == c._MESSAGE_TYPE_:
|
||||||
return c(readLen = self.wMsgSize - 4)
|
return c()
|
||||||
log.debug("unknown license message : %s"%self.bMsgtype.value)
|
log.debug("unknown license message : %s"%self.bMsgtype.value)
|
||||||
return String()
|
return String()
|
||||||
|
|
||||||
|
|||||||
@@ -515,16 +515,15 @@ class Server(MCSLayer):
|
|||||||
self.readDomainParams(data)
|
self.readDomainParams(data)
|
||||||
self.readDomainParams(data)
|
self.readDomainParams(data)
|
||||||
self._clientSettings = gcc.readConferenceCreateRequest(Stream(ber.readOctetString(data)))
|
self._clientSettings = gcc.readConferenceCreateRequest(Stream(ber.readOctetString(data)))
|
||||||
|
|
||||||
if not self._clientSettings.CS_NET is None:
|
i = 1
|
||||||
i = 1
|
for channelDef in self._clientSettings.getBlock(gcc.MessageType.CS_NET).channelDefArray._array:
|
||||||
for channelDef in self._clientSettings.CS_NET.channelDefArray._array:
|
self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelIdArray._array.append(UInt16Le(i + Channel.MCS_GLOBAL_CHANNEL))
|
||||||
self._serverSettings.SC_NET.channelIdArray._array.append(UInt16Le(i + Channel.MCS_GLOBAL_CHANNEL))
|
#if channel can be handle by serve add it
|
||||||
#if channel can be handle by serve add it
|
for serverChannelDef, layer in self._virtualChannels:
|
||||||
for serverChannelDef, layer in self._virtualChannels:
|
if channelDef.name == serverChannelDef.name:
|
||||||
if channelDef.name == serverChannelDef.name:
|
self._channels[i + Channel.MCS_GLOBAL_CHANNEL] = layer
|
||||||
self._channels[i + Channel.MCS_GLOBAL_CHANNEL] = layer
|
i += 1
|
||||||
i += 1
|
|
||||||
|
|
||||||
self.sendConnectResponse()
|
self.sendConnectResponse()
|
||||||
self.setNextState(self.recvErectDomainRequest)
|
self.setNextState(self.recvErectDomainRequest)
|
||||||
|
|||||||
@@ -429,8 +429,7 @@ class ShareControlHeader(CompositeType):
|
|||||||
#share control header
|
#share control header
|
||||||
self.totalLength = UInt16Le(totalLength)
|
self.totalLength = UInt16Le(totalLength)
|
||||||
self.pduType = UInt16Le(pduType)
|
self.pduType = UInt16Le(pduType)
|
||||||
#for xp sp3 and deactiveallpdu PDUSource may not be present
|
self.PDUSource = UInt16Le(userId)
|
||||||
self.PDUSource = UInt16Le(userId, optional = True)
|
|
||||||
|
|
||||||
class ShareDataHeader(CompositeType):
|
class ShareDataHeader(CompositeType):
|
||||||
"""
|
"""
|
||||||
@@ -520,9 +519,7 @@ class DeactiveAllPDU(CompositeType):
|
|||||||
_PDUTYPE_ = PDUType.PDUTYPE_DEACTIVATEALLPDU
|
_PDUTYPE_ = PDUType.PDUTYPE_DEACTIVATEALLPDU
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
#in old version this packet is empty i don't know
|
CompositeType.__init__(self)
|
||||||
#and not specified
|
|
||||||
CompositeType.__init__(self, optional = True)
|
|
||||||
self.shareId = UInt32Le()
|
self.shareId = UInt32Le()
|
||||||
self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor))
|
self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor))
|
||||||
self.sourceDescriptor = String("rdpy", readLen = self.lengthSourceDescriptor)
|
self.sourceDescriptor = String("rdpy", readLen = self.lengthSourceDescriptor)
|
||||||
|
|||||||
@@ -179,9 +179,6 @@ class Client(PDULayer):
|
|||||||
|
|
||||||
for cap in pdu.pduMessage.capabilitySets._array:
|
for cap in pdu.pduMessage.capabilitySets._array:
|
||||||
self._serverCapabilities[cap.capabilitySetType] = cap
|
self._serverCapabilities[cap.capabilitySetType] = cap
|
||||||
|
|
||||||
#secure checksum cap here maybe protocol (another) design error
|
|
||||||
self._transport._enableSecureCheckSum = bool(self._serverCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability.extraFlags & caps.GeneralExtraFlag.ENC_SALTED_CHECKSUM)
|
|
||||||
|
|
||||||
self.sendConfirmActivePDU()
|
self.sendConfirmActivePDU()
|
||||||
#send synchronize
|
#send synchronize
|
||||||
@@ -315,7 +312,7 @@ class Client(PDULayer):
|
|||||||
generalCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability
|
generalCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability
|
||||||
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS
|
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS
|
||||||
generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT
|
generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT
|
||||||
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.ENC_SALTED_CHECKSUM
|
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR
|
||||||
if not self._fastPathSender is None:
|
if not self._fastPathSender is None:
|
||||||
generalCapability.extraFlags.value |= caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED
|
generalCapability.extraFlags.value |= caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED
|
||||||
|
|
||||||
@@ -416,9 +413,6 @@ class Server(PDULayer):
|
|||||||
#find use full flag
|
#find use full flag
|
||||||
self._clientFastPathSupported = bool(self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability.extraFlags.value & caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED)
|
self._clientFastPathSupported = bool(self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability.extraFlags.value & caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED)
|
||||||
|
|
||||||
#secure checksum cap here maybe protocol (another) design error
|
|
||||||
self._transport._enableSecureCheckSum = bool(self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability.extraFlags & caps.GeneralExtraFlag.ENC_SALTED_CHECKSUM)
|
|
||||||
|
|
||||||
self.setNextState(self.recvClientSynchronizePDU)
|
self.setNextState(self.recvClientSynchronizePDU)
|
||||||
|
|
||||||
def recvClientSynchronizePDU(self, s):
|
def recvClientSynchronizePDU(self, s):
|
||||||
@@ -531,7 +525,7 @@ class Server(PDULayer):
|
|||||||
generalCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability
|
generalCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability
|
||||||
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS
|
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS
|
||||||
generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT
|
generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT
|
||||||
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED | caps.GeneralExtraFlag.ENC_SALTED_CHECKSUM
|
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED | caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR | caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED
|
||||||
|
|
||||||
inputCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_INPUT].capability
|
inputCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_INPUT].capability
|
||||||
inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX
|
inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX
|
||||||
|
|||||||
@@ -164,12 +164,12 @@ def macData(macSaltKey, data):
|
|||||||
md5Digest = md5.new()
|
md5Digest = md5.new()
|
||||||
|
|
||||||
#encode length
|
#encode length
|
||||||
dataLength = Stream()
|
s = Stream()
|
||||||
dataLength.writeType(UInt32Le(len(data)))
|
s.writeType(UInt32Le(len(data)))
|
||||||
|
|
||||||
sha1Digest.update(macSaltKey)
|
sha1Digest.update(macSaltKey)
|
||||||
sha1Digest.update("\x36" * 40)
|
sha1Digest.update("\x36" * 40)
|
||||||
sha1Digest.update(dataLength.getvalue())
|
sha1Digest.update(s.getvalue())
|
||||||
sha1Digest.update(data)
|
sha1Digest.update(data)
|
||||||
|
|
||||||
sha1Sig = sha1Digest.digest()
|
sha1Sig = sha1Digest.digest()
|
||||||
@@ -180,38 +180,6 @@ def macData(macSaltKey, data):
|
|||||||
|
|
||||||
return md5Digest.digest()
|
return md5Digest.digest()
|
||||||
|
|
||||||
def macSaltedData(macSaltKey, data, encryptionCount):
|
|
||||||
"""
|
|
||||||
@see: https://msdn.microsoft.com/en-us/library/cc240789.aspx
|
|
||||||
@param macSaltKey: {str} mac key
|
|
||||||
@param data: {str} data to sign
|
|
||||||
@param encryptionCount: nb encrypted packet
|
|
||||||
@return: {str} signature
|
|
||||||
"""
|
|
||||||
sha1Digest = sha.new()
|
|
||||||
md5Digest = md5.new()
|
|
||||||
|
|
||||||
#encode length
|
|
||||||
dataLengthS = Stream()
|
|
||||||
dataLengthS.writeType(UInt32Le(len(data)))
|
|
||||||
|
|
||||||
encryptionCountS = Stream()
|
|
||||||
encryptionCountS.writeType(UInt32Le(encryptionCount))
|
|
||||||
|
|
||||||
sha1Digest.update(macSaltKey)
|
|
||||||
sha1Digest.update("\x36" * 40)
|
|
||||||
sha1Digest.update(dataLengthS.getvalue())
|
|
||||||
sha1Digest.update(data)
|
|
||||||
sha1Digest.update(encryptionCountS.getvalue())
|
|
||||||
|
|
||||||
sha1Sig = sha1Digest.digest()
|
|
||||||
|
|
||||||
md5Digest.update(macSaltKey)
|
|
||||||
md5Digest.update("\x5c" * 48)
|
|
||||||
md5Digest.update(sha1Sig)
|
|
||||||
|
|
||||||
return md5Digest.digest()
|
|
||||||
|
|
||||||
def tempKey(initialKey, currentKey):
|
def tempKey(initialKey, currentKey):
|
||||||
"""
|
"""
|
||||||
@see: http://msdn.microsoft.com/en-us/library/cc240792.aspx
|
@see: http://msdn.microsoft.com/en-us/library/cc240792.aspx
|
||||||
@@ -374,9 +342,6 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
|||||||
#True if classic encryption is enable
|
#True if classic encryption is enable
|
||||||
self._enableEncryption = False
|
self._enableEncryption = False
|
||||||
|
|
||||||
#Enable Secure Mac generation
|
|
||||||
self._enableSecureCheckSum = False
|
|
||||||
|
|
||||||
#initialise decrypt and encrypt keys
|
#initialise decrypt and encrypt keys
|
||||||
self._macKey = None
|
self._macKey = None
|
||||||
self._initialDecrytKey = None
|
self._initialDecrytKey = None
|
||||||
@@ -393,11 +358,10 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
|||||||
self._encryptRc4 = None
|
self._encryptRc4 = None
|
||||||
|
|
||||||
|
|
||||||
def readEncryptedPayload(self, s, saltedMacGeneration):
|
def readEncryptedPayload(self, s):
|
||||||
"""
|
"""
|
||||||
@summary: decrypt basic RDP security payload
|
@summary: decrypt basic RDP security payload
|
||||||
@param s: {Stream} encrypted stream
|
@param s: {Stream} encrypted stream
|
||||||
@param saltedMacGeneration: {bool} use salted mac generation
|
|
||||||
@return: {Stream} decrypted
|
@return: {Stream} decrypted
|
||||||
"""
|
"""
|
||||||
#if update is needed
|
#if update is needed
|
||||||
@@ -414,22 +378,18 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
|||||||
decrypted = rc4.crypt(self._decryptRc4, encryptedPayload.value)
|
decrypted = rc4.crypt(self._decryptRc4, encryptedPayload.value)
|
||||||
|
|
||||||
#ckeck signature
|
#ckeck signature
|
||||||
if not saltedMacGeneration and macData(self._macKey, decrypted)[:8] != signature.value:
|
if macData(self._macKey, decrypted)[:8] != signature.value:
|
||||||
raise InvalidExpectedDataException("bad signature")
|
raise InvalidExpectedDataException("Bad packet signature")
|
||||||
|
|
||||||
if saltedMacGeneration and macSaltedData(self._macKey, decrypted, self._nbDecryptedPacket)[:8] != signature.value:
|
|
||||||
raise InvalidExpectedDataException("bad signature")
|
|
||||||
|
|
||||||
#count
|
#count
|
||||||
self._nbDecryptedPacket += 1
|
self._nbDecryptedPacket += 1
|
||||||
|
|
||||||
return Stream(decrypted)
|
return Stream(decrypted)
|
||||||
|
|
||||||
def writeEncryptedPayload(self, data, saltedMacGeneration):
|
def writeEncryptedPayload(self, data):
|
||||||
"""
|
"""
|
||||||
@summary: sign and crypt data
|
@summary: sign and crypt data
|
||||||
@param data: {Type} raw stream
|
@param s: {Stream} raw stream
|
||||||
@param saltedMacGeneration: {bool} use salted mac generation
|
|
||||||
@return: {Tuple} (signature, encryptedData)
|
@return: {Tuple} (signature, encryptedData)
|
||||||
"""
|
"""
|
||||||
if self._nbEncryptedPacket == 4096:
|
if self._nbEncryptedPacket == 4096:
|
||||||
@@ -440,14 +400,9 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
|||||||
self._nbEncryptedPacket = 0
|
self._nbEncryptedPacket = 0
|
||||||
|
|
||||||
self._nbEncryptedPacket += 1
|
self._nbEncryptedPacket += 1
|
||||||
|
|
||||||
s = Stream()
|
s = Stream()
|
||||||
s.writeType(data)
|
s.writeType(data)
|
||||||
|
return (String(macData(self._macKey, s.getvalue())[:8]), String(rc4.crypt(self._encryptRc4, s.getvalue())))
|
||||||
if saltedMacGeneration:
|
|
||||||
return (String(macSaltedData(self._macKey, s.getvalue(), self._nbEncryptedPacket - 1)[:8]), String(rc4.crypt(self._encryptRc4, s.getvalue())))
|
|
||||||
else:
|
|
||||||
return (String(macData(self._macKey, s.getvalue())[:8]), String(rc4.crypt(self._encryptRc4, s.getvalue())))
|
|
||||||
|
|
||||||
def recv(self, data):
|
def recv(self, data):
|
||||||
"""
|
"""
|
||||||
@@ -464,7 +419,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
|||||||
data.readType((securityFlag, securityFlagHi))
|
data.readType((securityFlag, securityFlagHi))
|
||||||
|
|
||||||
if securityFlag.value & SecurityFlag.SEC_ENCRYPT:
|
if securityFlag.value & SecurityFlag.SEC_ENCRYPT:
|
||||||
data = self.readEncryptedPayload(data, securityFlag.value & SecurityFlag.SEC_SECURE_CHECKSUM)
|
data = self.readEncryptedPayload(data)
|
||||||
|
|
||||||
self._presentation.recv(data)
|
self._presentation.recv(data)
|
||||||
|
|
||||||
@@ -478,12 +433,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
|||||||
self._transport.send(data)
|
self._transport.send(data)
|
||||||
return
|
return
|
||||||
|
|
||||||
flag = SecurityFlag.SEC_ENCRYPT
|
self.sendFlagged(SecurityFlag.SEC_ENCRYPT, data)
|
||||||
|
|
||||||
if self._enableSecureCheckSum:
|
|
||||||
flag |= SecurityFlag.SEC_SECURE_CHECKSUM
|
|
||||||
|
|
||||||
self.sendFlagged(flag, data)
|
|
||||||
|
|
||||||
def sendFlagged(self, flag, data):
|
def sendFlagged(self, flag, data):
|
||||||
"""
|
"""
|
||||||
@@ -494,7 +444,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
|||||||
@param data: {Type | Tuple}
|
@param data: {Type | Tuple}
|
||||||
"""
|
"""
|
||||||
if flag & SecurityFlag.SEC_ENCRYPT:
|
if flag & SecurityFlag.SEC_ENCRYPT:
|
||||||
data = self.writeEncryptedPayload(data, flag & SecurityFlag.SEC_SECURE_CHECKSUM)
|
data = self.writeEncryptedPayload(data)
|
||||||
self._transport.send((UInt16Le(flag), UInt16Le(), data))
|
self._transport.send((UInt16Le(flag), UInt16Le(), data))
|
||||||
|
|
||||||
def recvFastPath(self, secFlag, fastPathS):
|
def recvFastPath(self, secFlag, fastPathS):
|
||||||
@@ -504,7 +454,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
|||||||
@param fastPathS: {Stream}
|
@param fastPathS: {Stream}
|
||||||
"""
|
"""
|
||||||
if self._enableEncryption and secFlag & tpkt.SecFlags.FASTPATH_OUTPUT_ENCRYPTED:
|
if self._enableEncryption and secFlag & tpkt.SecFlags.FASTPATH_OUTPUT_ENCRYPTED:
|
||||||
fastPathS = self.readEncryptedPayload(fastPathS, secFlag & tpkt.SecFlags.FASTPATH_OUTPUT_SECURE_CHECKSUM)
|
fastPathS = self.readEncryptedPayload(fastPathS)
|
||||||
|
|
||||||
self._fastPathPresentation.recvFastPath(secFlag, fastPathS)
|
self._fastPathPresentation.recvFastPath(secFlag, fastPathS)
|
||||||
|
|
||||||
@@ -522,11 +472,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
|||||||
"""
|
"""
|
||||||
if self._enableEncryption:
|
if self._enableEncryption:
|
||||||
secFlag |= tpkt.SecFlags.FASTPATH_OUTPUT_ENCRYPTED
|
secFlag |= tpkt.SecFlags.FASTPATH_OUTPUT_ENCRYPTED
|
||||||
|
fastPathS = self.writeEncryptedPayload(fastPathS)
|
||||||
if self._enableSecureCheckSum:
|
|
||||||
secFlag |= tpkt.SecFlags.FASTPATH_OUTPUT_SECURE_CHECKSUM
|
|
||||||
|
|
||||||
fastPathS = self.writeEncryptedPayload(fastPathS, self._enableSecureCheckSum)
|
|
||||||
|
|
||||||
self._fastPathTransport.sendFastPath(secFlag, fastPathS)
|
self._fastPathTransport.sendFastPath(secFlag, fastPathS)
|
||||||
|
|
||||||
@@ -715,7 +661,7 @@ class Server(SecLayer):
|
|||||||
raise InvalidExpectedDataException("Waiting info packet")
|
raise InvalidExpectedDataException("Waiting info packet")
|
||||||
|
|
||||||
if securityFlag.value & SecurityFlag.SEC_ENCRYPT:
|
if securityFlag.value & SecurityFlag.SEC_ENCRYPT:
|
||||||
s = self.readEncryptedPayload(s, securityFlag.value & SecurityFlag.SEC_SECURE_CHECKSUM)
|
s = self.readEncryptedPayload(s)
|
||||||
|
|
||||||
s.readType(self._info)
|
s.readType(self._info)
|
||||||
#next state send error license
|
#next state send error license
|
||||||
|
|||||||
16
setup.py
16
setup.py
@@ -4,20 +4,20 @@ import setuptools
|
|||||||
from distutils.core import setup, Extension
|
from distutils.core import setup, Extension
|
||||||
|
|
||||||
setup(name='rdpy',
|
setup(name='rdpy',
|
||||||
version='1.2.2',
|
version='1.2.1',
|
||||||
description='Remote Desktop Protocol in Python',
|
description='Remote Desktop Protocol in Python',
|
||||||
long_description="""
|
long_description="""
|
||||||
RDPY is a pure Python implementation of the Microsoft RDP (Remote Desktop Protocol) protocol (Client and Server side).
|
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 is built over the event driven network engine Twisted.
|
||||||
|
|
||||||
RDPY provide RDP and VNC binaries :
|
RDPY provide RDP and VNC binaries :
|
||||||
- RDP Man In The Middle proxy which record session
|
\t-RDP Man In The Middle proxy which record session
|
||||||
- RDP Honeypot
|
\t-RDP Honeypot
|
||||||
- RDP screenshoter
|
\t-RDP screenshoter
|
||||||
- RDP client
|
\t-RDP client
|
||||||
- VNC client
|
\t-VNC client
|
||||||
- VNC screenshoter
|
\t-VNC screenshoter
|
||||||
- RSS Player
|
\t-RSS Player
|
||||||
""",
|
""",
|
||||||
author='Sylvain Peyrefitte',
|
author='Sylvain Peyrefitte',
|
||||||
author_email='citronneur@gmail.com',
|
author_email='citronneur@gmail.com',
|
||||||
|
|||||||
Reference in New Issue
Block a user