Compare commits
1 Commits
bd7c708bf3
...
revert-11-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58ad5782c1 |
@@ -7,7 +7,7 @@ before_install:
|
||||
- sudo apt-get install python-qt4
|
||||
- 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/
|
||||
- pip install qt4reactor pyopenssl twisted service_identity rsa pyasn1
|
||||
- pip install qt4reactor pyopenssl twisted service_identity rsa
|
||||
|
||||
install:
|
||||
- python setup.py install
|
||||
|
||||
285
README.md
285
README.md
@@ -1,25 +1,25 @@
|
||||
# RDPY [](https://travis-ci.org/citronneur/rdpy) [](http://badge.fury.io/py/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 support standard RDP security layer, RDP over SSL and NLA authentication (through ntlmv2 authentication 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 provides the following RDP and VNC binaries :
|
||||
RDPY provide RDP and VNC binaries :
|
||||
* RDP Man In The Middle proxy which record session
|
||||
* RDP Honeypot
|
||||
* RDP screenshoter
|
||||
* RDP screen shooter
|
||||
* RDP client
|
||||
* VNC client
|
||||
* VNC screenshoter
|
||||
* VNC screen shooter
|
||||
* RSS Player
|
||||
|
||||
## 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-rdpscreenshot
|
||||
* rdpy-vncclient
|
||||
@@ -28,17 +28,11 @@ Dependencies are only needed for pyqt4 binaries :
|
||||
|
||||
#### Linux
|
||||
|
||||
Example for Debian based systems :
|
||||
Exemple from Debian based system :
|
||||
```
|
||||
sudo apt-get install python-qt4
|
||||
```
|
||||
|
||||
#### OS X
|
||||
Example for OS X to install PyQt with homebrew
|
||||
```
|
||||
$ brew install qt sip pyqt
|
||||
```
|
||||
|
||||
#### Windows
|
||||
|
||||
x86 | x86_64
|
||||
@@ -50,7 +44,7 @@ x86 | x86_64
|
||||
|
||||
```
|
||||
$ git clone https://github.com/citronneur/rdpy.git rdpy
|
||||
$ pip install twisted pyopenssl qt4reactor service_identity rsa pyasn1
|
||||
$ pip install twisted pyopenssl qt4reactor service_identity rsa
|
||||
$ python rdpy/setup.py install
|
||||
```
|
||||
|
||||
@@ -59,7 +53,7 @@ Or use PIP:
|
||||
$ 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/sip.so $VIRTUAL_ENV/lib/python2.7/site-packages/
|
||||
@@ -71,13 +65,13 @@ RDPY comes with some very useful binaries. These binaries are linux and windows
|
||||
|
||||
### 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]
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
@@ -89,7 +83,7 @@ $ rdpy-vncclient.py [-p password] XXX.XXX.XXX.XXX[:5900]
|
||||
|
||||
### 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]
|
||||
@@ -97,7 +91,7 @@ $ rdpy-rdpscreenshot.py [-w width] [-l height] [-o output_file_path] XXX.XXX.XXX
|
||||
|
||||
### 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]
|
||||
@@ -106,25 +100,24 @@ $ 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 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]
|
||||
```
|
||||
|
||||
Output directory is used to save the 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 If one of both parameters are omitted, the server use standard RDP as security layer.
|
||||
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-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. 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.
|
||||
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-rssplayer
|
||||
|
||||
@@ -136,11 +129,11 @@ $ rdpy-rssplayer.py rss_file_path
|
||||
|
||||
## 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
|
||||
|
||||
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
|
||||
|
||||
@@ -149,48 +142,48 @@ from rdpy.protocol.rdp import rdp
|
||||
|
||||
class MyRDPFactory(rdp.ClientFactory):
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
reactor.stop()
|
||||
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
reactor.stop()
|
||||
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
|
||||
class MyObserver(rdp.RDPClientObserver)
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@summary: Call when stack is ready
|
||||
"""
|
||||
#send 'r' key
|
||||
self._controller.sendKeyEventUnicode(ord(unicode("r".toUtf8(), encoding="UTF-8")), True)
|
||||
#mouse move and click at pixel 200x200
|
||||
self._controller.sendPointerEvent(200, 200, 1, true)
|
||||
|
||||
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||
"""
|
||||
@summary: Notify bitmap update
|
||||
@param destLeft: xmin position
|
||||
@param destTop: ymin position
|
||||
@param destRight: xmax position because RDP can send bitmap with padding
|
||||
@param destBottom: ymax position because RDP can send bitmap with padding
|
||||
@param width: width of bitmap
|
||||
@param height: height of bitmap
|
||||
@param bitsPerPixel: number of bit per pixel
|
||||
@param isCompress: use RLE compression
|
||||
@param data: bitmap data
|
||||
"""
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
@summary: Call when stack is close
|
||||
"""
|
||||
|
||||
class MyObserver(rdp.RDPClientObserver):
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@summary: Call when stack is ready
|
||||
"""
|
||||
#send 'r' key
|
||||
self._controller.sendKeyEventUnicode(ord(unicode("r".toUtf8(), encoding="UTF-8")), True)
|
||||
#mouse move and click at pixel 200x200
|
||||
self._controller.sendPointerEvent(200, 200, 1, true)
|
||||
|
||||
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||
"""
|
||||
@summary: Notify bitmap update
|
||||
@param destLeft: xmin position
|
||||
@param destTop: ymin position
|
||||
@param destRight: xmax position because RDP can send bitmap with padding
|
||||
@param destBottom: ymax position because RDP can send bitmap with padding
|
||||
@param width: width of bitmap
|
||||
@param height: height of bitmap
|
||||
@param bitsPerPixel: number of bit per pixel
|
||||
@param isCompress: use RLE compression
|
||||
@param data: bitmap data
|
||||
"""
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
@summary: Call when stack is close
|
||||
"""
|
||||
|
||||
return MyObserver(controller)
|
||||
return MyObserver(controller)
|
||||
|
||||
from twisted.internet import reactor
|
||||
reactor.connectTCP("XXX.XXX.XXX.XXX", 3389, MyRDPFactory())
|
||||
reactor.connectTCP("XXX.XXX.XXX.XXX", 3389), MyRDPFactory())
|
||||
reactor.run()
|
||||
```
|
||||
|
||||
@@ -201,48 +194,48 @@ from rdpy.protocol.rdp import rdp
|
||||
class MyRDPFactory(rdp.ServerFactory):
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
|
||||
class MyObserver(rdp.RDPServerObserver)
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@summary: Call when server is ready
|
||||
to send and receive messages
|
||||
"""
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
@summary: Call when human client close connection
|
||||
@see: rdp.RDPServerObserver.onClose
|
||||
"""
|
||||
|
||||
class MyObserver(rdp.RDPServerObserver):
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@summary: Call when server is ready
|
||||
to send and receive messages
|
||||
"""
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
@summary: Call when human client close connection
|
||||
@see: rdp.RDPServerObserver.onClose
|
||||
"""
|
||||
|
||||
return MyObserver(controller)
|
||||
return MyObserver(controller)
|
||||
|
||||
from twisted.internet import reactor
|
||||
reactor.listenTCP(3389, MyRDPFactory())
|
||||
@@ -251,55 +244,55 @@ reactor.run()
|
||||
|
||||
### Simple VNC Client
|
||||
```python
|
||||
from rdpy.protocol.rfb import rfb
|
||||
from rdpy.protocol.rfb import rdp
|
||||
|
||||
class MyRFBFactory(rfb.ClientFactory):
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
reactor.stop()
|
||||
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
reactor.stop()
|
||||
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
class MyObserver(rfb.RFBClientObserver):
|
||||
class MyObserver(rfb.RFBClientObserver)
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@summary: Event when network stack is ready to receive or send event
|
||||
"""
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@summary: Event when network stack is ready to receive or send event
|
||||
"""
|
||||
def onUpdate(self, width, height, x, y, pixelFormat, encoding, data):
|
||||
"""
|
||||
@summary: Implement RFBClientObserver interface
|
||||
@param width: width of new image
|
||||
@param height: height of new image
|
||||
@param x: x position of new image
|
||||
@param y: y position of new image
|
||||
@param pixelFormat: pixefFormat structure in rfb.message.PixelFormat
|
||||
@param encoding: encoding type rfb.message.Encoding
|
||||
@param data: image data in accordance with pixel format and encoding
|
||||
"""
|
||||
|
||||
def onCutText(self, text):
|
||||
"""
|
||||
@summary: event when server send cut text event
|
||||
@param text: text received
|
||||
"""
|
||||
|
||||
def onBell(self):
|
||||
"""
|
||||
@summary: event when server send biiip
|
||||
"""
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
@summary: Call when stack is close
|
||||
"""
|
||||
|
||||
def onUpdate(self, width, height, x, y, pixelFormat, encoding, data):
|
||||
"""
|
||||
@summary: Implement RFBClientObserver interface
|
||||
@param width: width of new image
|
||||
@param height: height of new image
|
||||
@param x: x position of new image
|
||||
@param y: y position of new image
|
||||
@param pixelFormat: pixefFormat structure in rfb.message.PixelFormat
|
||||
@param encoding: encoding type rfb.message.Encoding
|
||||
@param data: image data in accordance with pixel format and encoding
|
||||
"""
|
||||
|
||||
def onCutText(self, text):
|
||||
"""
|
||||
@summary: event when server send cut text event
|
||||
@param text: text received
|
||||
"""
|
||||
|
||||
def onBell(self):
|
||||
"""
|
||||
@summary: event when server send biiip
|
||||
"""
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
@summary: Call when stack is close
|
||||
"""
|
||||
|
||||
return MyObserver(controller)
|
||||
return MyObserver(controller)
|
||||
|
||||
from twisted.internet import reactor
|
||||
reactor.connectTCP("XXX.XXX.XXX.XXX", 3389, MyRFBFactory())
|
||||
reactor.connectTCP("XXX.XXX.XXX.XXX", 3389), MyRFBFactory())
|
||||
reactor.run()
|
||||
```
|
||||
|
||||
@@ -115,11 +115,7 @@ class RDPClientQtFactory(rdp.ClientFactory):
|
||||
self._nego = security == "nego"
|
||||
self._recodedPath = recodedPath
|
||||
if self._nego:
|
||||
#compute start nego nla need credentials
|
||||
if username != "" and password != "":
|
||||
self._security = rdp.SecurityLevel.RDP_LEVEL_NLA
|
||||
else:
|
||||
self._security = rdp.SecurityLevel.RDP_LEVEL_SSL
|
||||
self._security = "ssl"
|
||||
else:
|
||||
self._security = security
|
||||
self._w = None
|
||||
@@ -167,7 +163,7 @@ class RDPClientQtFactory(rdp.ClientFactory):
|
||||
#stop nego
|
||||
log.info("due to security nego error back to standard RDP security layer")
|
||||
self._nego = False
|
||||
self._security = rdp.SecurityLevel.RDP_LEVEL_RDP
|
||||
self._security = "rdp"
|
||||
self._client._widget.hide()
|
||||
connector.connect()
|
||||
return
|
||||
@@ -194,7 +190,7 @@ def autoDetectKeyboardLayout():
|
||||
if os.name == 'posix':
|
||||
from subprocess import check_output
|
||||
result = check_output(["setxkbmap", "-print"])
|
||||
if 'azerty' in result:
|
||||
if "azerty" in result:
|
||||
return "fr"
|
||||
elif os.name == 'nt':
|
||||
import win32api, win32con, win32process
|
||||
|
||||
@@ -31,15 +31,13 @@ from twisted.internet import reactor
|
||||
log._LOG_LEVEL = log.Level.INFO
|
||||
|
||||
class HoneyPotServer(rdp.RDPServerObserver):
|
||||
def __init__(self, controller, rssFileSizeList):
|
||||
def __init__(self, controller, rssFile):
|
||||
"""
|
||||
@param controller: {RDPServerController}
|
||||
@param rssFileSizeList: {Tuple} Tuple(Tuple(width, height), rssFilePath)
|
||||
"""
|
||||
rdp.RDPServerObserver.__init__(self, controller)
|
||||
self._rssFileSizeList = rssFileSizeList
|
||||
self._rssFile = rssFile
|
||||
self._dx, self._dy = 0, 0
|
||||
self._rssFile = None
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@@ -49,14 +47,6 @@ class HoneyPotServer(rdp.RDPServerObserver):
|
||||
restart a connection sequence
|
||||
@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()
|
||||
hostname = self._controller.getHostname()
|
||||
log.info("""Credentials:
|
||||
@@ -99,7 +89,7 @@ class HoneyPotServer(rdp.RDPServerObserver):
|
||||
clientSize = nextEvent.event.width.value, nextEvent.event.height.value
|
||||
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
|
||||
return
|
||||
|
||||
@@ -110,14 +100,14 @@ class HoneyPotServerFactory(rdp.ServerFactory):
|
||||
"""
|
||||
@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 certificateFilePath: {str} file contain server certificate (if none -> back to standard RDP security)
|
||||
"""
|
||||
rdp.ServerFactory.__init__(self, 16, privateKeyFilePath, certificateFilePath)
|
||||
self._rssFileSizeList = rssFileSizeList
|
||||
self._rssFilePath = rssFilePath
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
"""
|
||||
@@ -126,27 +116,14 @@ class HoneyPotServerFactory(rdp.ServerFactory):
|
||||
@see: rdp.ServerFactory.buildObserver
|
||||
"""
|
||||
log.info("Connection from %s:%s"%(addr.host, addr.port))
|
||||
return HoneyPotServer(controller, self._rssFileSizeList)
|
||||
|
||||
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
|
||||
return HoneyPotServer(controller, rss.createReader(self._rssFilePath))
|
||||
|
||||
def help():
|
||||
"""
|
||||
@summary: Print help in console
|
||||
"""
|
||||
print """
|
||||
Usage: rdpy-rdphoneypot.py rss_filepath(1..n)
|
||||
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)]
|
||||
@@ -156,7 +133,6 @@ if __name__ == '__main__':
|
||||
listen = "3389"
|
||||
privateKeyFilePath = None
|
||||
certificateFilePath = None
|
||||
rssFileSizeList = []
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "hl:k:c:")
|
||||
@@ -173,12 +149,5 @@ if __name__ == '__main__':
|
||||
elif opt == "-c":
|
||||
certificateFilePath = arg
|
||||
|
||||
#build size map
|
||||
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.listenTCP(int(listen), HoneyPotServerFactory(args[0], privateKeyFilePath, certificateFilePath))
|
||||
reactor.run()
|
||||
@@ -102,7 +102,6 @@ class ProxyServer(rdp.RDPServerObserver):
|
||||
if self._client is None:
|
||||
return
|
||||
self._client._controller.sendKeyEventScancode(code, isPressed)
|
||||
self._rss.keyScancode(code, isPressed)
|
||||
|
||||
def onKeyEventUnicode(self, code, isPressed):
|
||||
"""
|
||||
@@ -114,7 +113,6 @@ class ProxyServer(rdp.RDPServerObserver):
|
||||
if self._client is None:
|
||||
return
|
||||
self._client._controller.sendKeyEventUnicode(code, isPressed)
|
||||
self._rss.keyUnicode(code, isPressed)
|
||||
|
||||
def onPointerEvent(self, x, y, button, isPressed):
|
||||
"""
|
||||
@@ -253,9 +251,7 @@ def help():
|
||||
[-l listen_port default 3389]
|
||||
[-k private_key_file_path (mandatory for SSL)]
|
||||
[-c certificate_file_path (mandatory for SSL)]
|
||||
[-o output directory for recoded files]
|
||||
[-r RDP standard security (XP or server 2003 client or older)]
|
||||
[-n For NLA Client authentication (need to provide credentials)]
|
||||
"""
|
||||
|
||||
def parseIpPort(interface, defaultPort = "3389"):
|
||||
@@ -269,11 +265,10 @@ if __name__ == '__main__':
|
||||
privateKeyFilePath = None
|
||||
certificateFilePath = None
|
||||
ouputDirectory = None
|
||||
#for anonymous authentication
|
||||
clientSecurity = rdp.SecurityLevel.RDP_LEVEL_SSL
|
||||
clientSecurity = "ssl"
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "hl:k:c:o:rn")
|
||||
opts, args = getopt.getopt(sys.argv[1:], "hl:k:c:o:r")
|
||||
except getopt.GetoptError:
|
||||
help()
|
||||
for opt, arg in opts:
|
||||
@@ -289,9 +284,7 @@ if __name__ == '__main__':
|
||||
elif opt == "-o":
|
||||
ouputDirectory = arg
|
||||
elif opt == "-r":
|
||||
clientSecurity = rdp.SecurityLevel.RDP_LEVEL_RDP
|
||||
elif opt == "-n":
|
||||
clientSecurity = rdp.SecurityLevel.RDP_LEVEL_NLA
|
||||
clientSecurity = "rdp"
|
||||
|
||||
if ouputDirectory is None or not os.path.dirname(ouputDirectory):
|
||||
log.error("%s is an invalid output directory"%ouputDirectory)
|
||||
|
||||
@@ -56,8 +56,7 @@ class RDPScreenShotFactory(rdp.ClientFactory):
|
||||
self._height = height
|
||||
self._path = path
|
||||
self._timeout = timeout
|
||||
#NLA server can't be screenshooting
|
||||
self._security = rdp.SecurityLevel.RDP_LEVEL_SSL
|
||||
self._security = "ssl"
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
"""
|
||||
@@ -67,7 +66,7 @@ class RDPScreenShotFactory(rdp.ClientFactory):
|
||||
"""
|
||||
if reason.type == RDPSecurityNegoFail and self._security != "rdp":
|
||||
log.info("due to RDPSecurityNegoFail try standard security layer")
|
||||
self._security = rdp.SecurityLevel.RDP_LEVEL_RDP
|
||||
self._security = "rdp"
|
||||
connector.connect()
|
||||
return
|
||||
|
||||
@@ -122,7 +121,7 @@ class RDPScreenShotFactory(rdp.ClientFactory):
|
||||
"""
|
||||
@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:
|
||||
#draw image
|
||||
qp.drawImage(destLeft, destTop, image, 0, 0, destRight - destLeft + 1, destBottom - destTop + 1)
|
||||
|
||||
@@ -27,7 +27,6 @@ from PyQt4 import QtGui, QtCore
|
||||
|
||||
from rdpy.core import log, rss
|
||||
from rdpy.ui.qt4 import QRemoteDesktop, RDPBitmapToQtImage
|
||||
from rdpy.core.scancode import scancodeToChar
|
||||
log._LOG_LEVEL = log.Level.INFO
|
||||
|
||||
class RssPlayerWidget(QRemoteDesktop):
|
||||
@@ -46,28 +45,9 @@ class RssPlayerWidget(QRemoteDesktop):
|
||||
""" Not Handle """
|
||||
QRemoteDesktop.__init__(self, width, height, RssAdaptor())
|
||||
|
||||
class RssPlayerWindow(QtGui.QWidget):
|
||||
"""
|
||||
@summary: main window of rss player
|
||||
"""
|
||||
def __init__(self):
|
||||
super(RssPlayerWindow, self).__init__()
|
||||
|
||||
self._viewer = RssPlayerWidget(800, 600)
|
||||
self._text = QtGui.QTextEdit()
|
||||
self._text.setReadOnly(True)
|
||||
self._text.setFixedHeight(150)
|
||||
|
||||
scrollViewer = QtGui.QScrollArea()
|
||||
scrollViewer.setWidget(self._viewer)
|
||||
|
||||
layout = QtGui.QVBoxLayout()
|
||||
layout.addWidget(scrollViewer, 1)
|
||||
layout.addWidget(self._text, 2)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
self.setGeometry(0, 0, 800, 600)
|
||||
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-rssplayer [-h] rss_filepath"
|
||||
@@ -84,20 +64,16 @@ def loop(widget, rssFile, nextEvent):
|
||||
|
||||
if nextEvent.type.value == rss.EventType.UPDATE:
|
||||
image = RDPBitmapToQtImage(nextEvent.event.width.value, nextEvent.event.height.value, nextEvent.event.bpp.value, nextEvent.event.format.value == rss.UpdateFormat.BMP, nextEvent.event.data.value);
|
||||
widget._viewer.notifyImage(nextEvent.event.destLeft.value, nextEvent.event.destTop.value, image, nextEvent.event.destRight.value - nextEvent.event.destLeft.value + 1, nextEvent.event.destBottom.value - nextEvent.event.destTop.value + 1)
|
||||
widget.notifyImage(nextEvent.event.destLeft.value, nextEvent.event.destTop.value, image, nextEvent.event.destRight.value - nextEvent.event.destLeft.value + 1, nextEvent.event.destBottom.value - nextEvent.event.destTop.value + 1)
|
||||
|
||||
elif nextEvent.type.value == rss.EventType.SCREEN:
|
||||
widget._viewer.resize(nextEvent.event.width.value, nextEvent.event.height.value)
|
||||
widget.resize(nextEvent.event.width.value, nextEvent.event.height.value)
|
||||
|
||||
elif nextEvent.type.value == rss.EventType.INFO:
|
||||
widget._text.append("Domain : %s\nUsername : %s\nPassword : %s\nHostname : %s\n" % (
|
||||
nextEvent.event.domain.value, nextEvent.event.username.value, nextEvent.event.password.value, nextEvent.event.hostname.value))
|
||||
elif nextEvent.type.value == rss.EventType.KEY_SCANCODE:
|
||||
if nextEvent.event.isPressed.value == 0:
|
||||
widget._text.moveCursor(QtGui.QTextCursor.End)
|
||||
widget._text.insertPlainText(scancodeToChar(nextEvent.event.code.value))
|
||||
widget.drawInfos(nextEvent.event.domain.value, nextEvent.event.username.value, nextEvent.event.password.value, nextEvent.event.hostname.value)
|
||||
|
||||
elif nextEvent.type.value == rss.EventType.CLOSE:
|
||||
widget.close()
|
||||
return
|
||||
|
||||
e = rssFile.nextEvent()
|
||||
@@ -116,10 +92,8 @@ if __name__ == '__main__':
|
||||
filepath = args[0]
|
||||
#create application
|
||||
app = QtGui.QApplication(sys.argv)
|
||||
|
||||
mainWindow = RssPlayerWindow()
|
||||
mainWindow.show()
|
||||
|
||||
widget = RssPlayerWidget(800, 600)
|
||||
widget.show()
|
||||
rssFile = rss.createReader(filepath)
|
||||
start(mainWindow, rssFile)
|
||||
start(widget, rssFile)
|
||||
sys.exit(app.exec_())
|
||||
@@ -1,98 +0,0 @@
|
||||
# Copyright (c) 2009, David Buxton <david@gasmark6.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""Tools to convert between Python datetime instances and Microsoft times.
|
||||
"""
|
||||
from datetime import datetime, timedelta, tzinfo
|
||||
from calendar import timegm
|
||||
|
||||
|
||||
# http://support.microsoft.com/kb/167296
|
||||
# How To Convert a UNIX time_t to a Win32 FILETIME or SYSTEMTIME
|
||||
EPOCH_AS_FILETIME = 116444736000000000 # January 1, 1970 as MS file time
|
||||
HUNDREDS_OF_NANOSECONDS = 10000000
|
||||
|
||||
|
||||
ZERO = timedelta(0)
|
||||
HOUR = timedelta(hours=1)
|
||||
|
||||
|
||||
class UTC(tzinfo):
|
||||
"""UTC"""
|
||||
def utcoffset(self, dt):
|
||||
return ZERO
|
||||
|
||||
def tzname(self, dt):
|
||||
return "UTC"
|
||||
|
||||
def dst(self, dt):
|
||||
return ZERO
|
||||
|
||||
|
||||
utc = UTC()
|
||||
|
||||
|
||||
def dt_to_filetime(dt):
|
||||
"""Converts a datetime to Microsoft filetime format. If the object is
|
||||
time zone-naive, it is forced to UTC before conversion.
|
||||
|
||||
>>> "%.0f" % dt_to_filetime(datetime(2009, 7, 25, 23, 0))
|
||||
'128930364000000000'
|
||||
|
||||
>>> "%.0f" % dt_to_filetime(datetime(1970, 1, 1, 0, 0, tzinfo=utc))
|
||||
'116444736000000000'
|
||||
|
||||
>>> "%.0f" % dt_to_filetime(datetime(1970, 1, 1, 0, 0))
|
||||
'116444736000000000'
|
||||
|
||||
>>> dt_to_filetime(datetime(2009, 7, 25, 23, 0, 0, 100))
|
||||
128930364000001000
|
||||
"""
|
||||
if (dt.tzinfo is None) or (dt.tzinfo.utcoffset(dt) is None):
|
||||
dt = dt.replace(tzinfo=utc)
|
||||
ft = EPOCH_AS_FILETIME + (timegm(dt.timetuple()) * HUNDREDS_OF_NANOSECONDS)
|
||||
return ft + (dt.microsecond * 10)
|
||||
|
||||
|
||||
def filetime_to_dt(ft):
|
||||
"""Converts a Microsoft filetime number to a Python datetime. The new
|
||||
datetime object is time zone-naive but is equivalent to tzinfo=utc.
|
||||
|
||||
>>> filetime_to_dt(116444736000000000)
|
||||
datetime.datetime(1970, 1, 1, 0, 0)
|
||||
|
||||
>>> filetime_to_dt(128930364000000000)
|
||||
datetime.datetime(2009, 7, 25, 23, 0)
|
||||
|
||||
>>> filetime_to_dt(128930364000001000)
|
||||
datetime.datetime(2009, 7, 25, 23, 0, 0, 100)
|
||||
"""
|
||||
# Get seconds and remainder in terms of Unix epoch
|
||||
(s, ns100) = divmod(ft - EPOCH_AS_FILETIME, HUNDREDS_OF_NANOSECONDS)
|
||||
# Convert to datetime object
|
||||
dt = datetime.utcfromtimestamp(s)
|
||||
# Add remainder in as microseconds. Python 3.2 requires an integer
|
||||
dt = dt.replace(microsecond=(ns100 // 10))
|
||||
return dt
|
||||
|
||||
@@ -137,7 +137,7 @@ class RawLayerClientFactory(protocol.ClientFactory):
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "connectionLost", "RawLayerClientFactory"))
|
||||
|
||||
class RawLayerServerFactory(protocol.ServerFactory):
|
||||
class RawLayerServerFactory(protocol.ClientFactory):
|
||||
"""
|
||||
@summary: Abstract class for Raw layer server factory
|
||||
"""
|
||||
@@ -200,7 +200,7 @@ class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender):
|
||||
#add in buffer
|
||||
self._buffer += data
|
||||
#while buffer have expected size call local callback
|
||||
while self._expectedLen > 0 and len(self._buffer) >= self._expectedLen:
|
||||
while len(self._buffer) >= self._expectedLen:
|
||||
#expected data is first expected bytes
|
||||
expectedData = Stream(self._buffer[0:self._expectedLen])
|
||||
#rest is for next event of automata
|
||||
@@ -222,19 +222,13 @@ class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender):
|
||||
"""
|
||||
self._factory.connectionLost(self, reason)
|
||||
|
||||
def getDescriptor(self):
|
||||
"""
|
||||
@return: the twited file descriptor
|
||||
"""
|
||||
return self.transport
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
@summary: Close raw layer
|
||||
Use File descriptor directly to not use TLS close
|
||||
Because is bugged
|
||||
"""
|
||||
FileDescriptor.loseConnection(self.getDescriptor())
|
||||
FileDescriptor.loseConnection(self.transport)
|
||||
|
||||
def expect(self, expectedLen, callback = None):
|
||||
"""
|
||||
|
||||
@@ -39,7 +39,7 @@ def log(message):
|
||||
@summary: Main log function
|
||||
@param message: string to print
|
||||
"""
|
||||
print "[*] %s"%message
|
||||
print message
|
||||
|
||||
def error(message):
|
||||
"""
|
||||
@@ -48,7 +48,7 @@ def error(message):
|
||||
"""
|
||||
if _LOG_LEVEL > Level.ERROR:
|
||||
return
|
||||
log("ERROR:\t%s"%message)
|
||||
log("ERROR : %s"%message)
|
||||
|
||||
def warning(message):
|
||||
"""
|
||||
@@ -57,7 +57,7 @@ def warning(message):
|
||||
"""
|
||||
if _LOG_LEVEL > Level.WARNING:
|
||||
return
|
||||
log("WARNING:\t%s"%message)
|
||||
log("WARNING : %s"%message)
|
||||
|
||||
def info(message):
|
||||
"""
|
||||
@@ -66,7 +66,7 @@ def info(message):
|
||||
"""
|
||||
if _LOG_LEVEL > Level.INFO:
|
||||
return
|
||||
log("INFO:\t%s"%message)
|
||||
log("INFO : %s"%message)
|
||||
|
||||
def debug(message):
|
||||
"""
|
||||
@@ -75,4 +75,4 @@ def debug(message):
|
||||
"""
|
||||
if _LOG_LEVEL > Level.DEBUG:
|
||||
return
|
||||
log("DEBUG:\t%s"%message)
|
||||
log("DEBUG : %s"%message)
|
||||
@@ -34,8 +34,6 @@ class EventType(object):
|
||||
SCREEN = 0x0002
|
||||
INFO = 0x0003
|
||||
CLOSE = 0x0004
|
||||
KEY_UNICODE = 0x0005
|
||||
KEY_SCANCODE = 0x0006
|
||||
|
||||
class UpdateFormat(object):
|
||||
"""
|
||||
@@ -58,7 +56,7 @@ class Event(CompositeType):
|
||||
"""
|
||||
@summary: Closure for event factory
|
||||
"""
|
||||
for c in [UpdateEvent, ScreenEvent, InfoEvent, CloseEvent, KeyEventScancode, KeyEventUnicode]:
|
||||
for c in [UpdateEvent, ScreenEvent, InfoEvent, CloseEvent]:
|
||||
if self.type.value == c._TYPE_:
|
||||
return c(readLen = self.length)
|
||||
log.debug("unknown event type : %s"%hex(self.type.value))
|
||||
@@ -125,26 +123,6 @@ class CloseEvent(CompositeType):
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
|
||||
class KeyEventUnicode(CompositeType):
|
||||
"""
|
||||
@summary: keyboard event (keylogger) as unicode event
|
||||
"""
|
||||
_TYPE_ = EventType.KEY_UNICODE
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.code = UInt32Le()
|
||||
self.isPressed = UInt8()
|
||||
|
||||
class KeyEventScancode(CompositeType):
|
||||
"""
|
||||
@summary: keyboard event (keylogger)
|
||||
"""
|
||||
_TYPE_ = EventType.KEY_SCANCODE
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.code = UInt32Le()
|
||||
self.isPressed = UInt8()
|
||||
|
||||
def timeMs():
|
||||
"""
|
||||
@return: {int} time stamp in milliseconds
|
||||
@@ -233,28 +211,6 @@ class FileRecorder(object):
|
||||
infoEvent.domain.value = domain
|
||||
infoEvent.hostname.value = hostname
|
||||
self.rec(infoEvent)
|
||||
|
||||
def keyUnicode(self, code, isPressed):
|
||||
"""
|
||||
@summary: record key event as unicode
|
||||
@param code: unicode code
|
||||
@param isPressed: True if a key press event
|
||||
"""
|
||||
keyEvent = KeyEventUnicode()
|
||||
keyEvent.code.value = code
|
||||
keyEvent.isPressed.value = 0 if isPressed else 1
|
||||
self.rec(keyEvent)
|
||||
|
||||
def keyScancode(self, code, isPressed):
|
||||
"""
|
||||
@summary: record key event as scancode
|
||||
@param code: scancode code
|
||||
@param isPressed: True if a key press event
|
||||
"""
|
||||
keyEvent = KeyEventScancode()
|
||||
keyEvent.code.value = code
|
||||
keyEvent.isPressed.value = 0 if isPressed else 1
|
||||
self.rec(keyEvent)
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
@@ -296,5 +252,4 @@ def createReader(path):
|
||||
@param path: {str} path of input file
|
||||
@return: {FileReader}
|
||||
"""
|
||||
with open(path, "rb") as f:
|
||||
return FileReader(f)
|
||||
return FileReader(open(path, "rb"))
|
||||
@@ -1,60 +0,0 @@
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Basic virtual scancode mapping
|
||||
"""
|
||||
|
||||
_SCANCODE_QWERTY_ = {
|
||||
0x10 : "q",
|
||||
0x11 : "w",
|
||||
0x12 : "e",
|
||||
0x13 : "r",
|
||||
0x14 : "t",
|
||||
0x15 : "y",
|
||||
0x16 : "u",
|
||||
0x17 : "i",
|
||||
0x18 : "o",
|
||||
0x19 : "p",
|
||||
0x1e : "a",
|
||||
0x1f : "s",
|
||||
0x20 : "d",
|
||||
0x21 : "f",
|
||||
0x22 : "g",
|
||||
0x23 : "h",
|
||||
0x24 : "j",
|
||||
0x25 : "k",
|
||||
0x26 : "l",
|
||||
0x2c : "z",
|
||||
0x2d : "x",
|
||||
0x2e : "c",
|
||||
0x2f : "v",
|
||||
0x30 : "b",
|
||||
0x31 : "n",
|
||||
0x32 : "m"
|
||||
}
|
||||
|
||||
def scancodeToChar(code):
|
||||
"""
|
||||
@summary: try to convert native code to char code
|
||||
@return: char
|
||||
"""
|
||||
if not _SCANCODE_QWERTY_.has_key(code):
|
||||
return "<unknown scancode %x>"%code
|
||||
return _SCANCODE_QWERTY_[code];
|
||||
@@ -257,7 +257,7 @@ class SimpleType(Type, CallableValue):
|
||||
@raise InvalidSize: if there is not enough data in stream
|
||||
"""
|
||||
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]
|
||||
|
||||
def mask(self):
|
||||
@@ -498,9 +498,6 @@ class CompositeType(Type):
|
||||
@summary: Call sizeof on each sub type
|
||||
@return: sum of sizeof of each Type attributes
|
||||
"""
|
||||
if self._is_readed and not self._readLen is None:
|
||||
return self._readLen.value
|
||||
|
||||
size = 0
|
||||
for name in self._typeName:
|
||||
size += sizeof(self.__dict__[name])
|
||||
@@ -811,7 +808,7 @@ class String(Type, CallableValue):
|
||||
self.value = s.getvalue()[s.pos:]
|
||||
else:
|
||||
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)
|
||||
else:
|
||||
self.value = s.read(self._readLen.value)
|
||||
|
||||
@@ -23,7 +23,7 @@ http://msdn.microsoft.com/en-us/library/cc240508.aspx
|
||||
"""
|
||||
|
||||
import md5
|
||||
from rdpy.core.type import UInt8, UInt16Le, UInt32Le, CompositeType, CallableValue, String, Stream, sizeof, FactoryType, ArrayType
|
||||
from rdpy.core.type import UInt8, UInt16Le, UInt32Le, CompositeType, String, Stream, sizeof, FactoryType, ArrayType
|
||||
import per, mcs
|
||||
from rdpy.core.error import InvalidExpectedDataException
|
||||
from rdpy.core import log
|
||||
@@ -252,21 +252,21 @@ class ClientCoreData(CompositeType):
|
||||
self.sasSequence = UInt16Le(Sequence.RNS_UD_SAS_DEL)
|
||||
self.kbdLayout = UInt32Le(KeyboardLayout.US)
|
||||
self.clientBuild = UInt32Le(3790)
|
||||
self.clientName = String("rdpy" + "\x00"*11, readLen = CallableValue(32), unicode = True)
|
||||
self.clientName = String("rdpy" + "\x00"*11, readLen = UInt8(32), unicode = True)
|
||||
self.keyboardType = UInt32Le(KeyboardType.IBM_101_102_KEYS)
|
||||
self.keyboardSubType = UInt32Le(0)
|
||||
self.keyboardFnKeys = UInt32Le(12)
|
||||
self.imeFileName = String("\x00"*64, readLen = CallableValue(64), optional = True)
|
||||
self.postBeta2ColorDepth = UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP, optional = True)
|
||||
self.clientProductId = UInt16Le(1, optional = True)
|
||||
self.serialNumber = UInt32Le(0, optional = True)
|
||||
self.highColorDepth = UInt16Le(HighColor.HIGH_COLOR_24BPP, 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, optional = True)
|
||||
self.earlyCapabilityFlags = UInt16Le(CapabilityFlags.RNS_UD_CS_SUPPORT_ERRINFO_PDU, optional = True)
|
||||
self.clientDigProductId = String("\x00"*64, readLen = CallableValue(64), optional = True)
|
||||
self.connectionType = UInt8(optional = True)
|
||||
self.pad1octet = UInt8(optional = True)
|
||||
self.serverSelectedProtocol = UInt32Le(optional = True)
|
||||
self.imeFileName = String("\x00"*64, readLen = UInt8(64))
|
||||
self.postBeta2ColorDepth = UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP)
|
||||
self.clientProductId = UInt16Le(1)
|
||||
self.serialNumber = UInt32Le(0)
|
||||
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)
|
||||
self.earlyCapabilityFlags = UInt16Le(CapabilityFlags.RNS_UD_CS_SUPPORT_ERRINFO_PDU)
|
||||
self.clientDigProductId = String("\x00"*64, readLen = UInt8(64))
|
||||
self.connectionType = UInt8()
|
||||
self.pad1octet = UInt8()
|
||||
self.serverSelectedProtocol = UInt32Le()
|
||||
|
||||
class ServerCoreData(CompositeType):
|
||||
"""
|
||||
@@ -278,8 +278,7 @@ class ServerCoreData(CompositeType):
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.rdpVersion = UInt32Le(Version.RDP_VERSION_5_PLUS)
|
||||
self.clientRequestedProtocol = UInt32Le(optional = True)
|
||||
self.earlyCapabilityFlags = UInt32Le(optional = True)
|
||||
self.clientRequestedProtocol = UInt32Le()
|
||||
|
||||
class ClientSecurityData(CompositeType):
|
||||
"""
|
||||
@@ -354,9 +353,9 @@ class ProprietaryServerCertificate(CompositeType):
|
||||
self.wPublicKeyBlobLen = UInt16Le(lambda:sizeof(self.PublicKeyBlob))
|
||||
self.PublicKeyBlob = RSAPublicKey(readLen = self.wPublicKeyBlobLen)
|
||||
self.wSignatureBlobType = UInt16Le(0x0008, constant = True)
|
||||
self.wSignatureBlobLen = UInt16Le(lambda:(sizeof(self.SignatureBlob) + sizeof(self.padding)))
|
||||
self.SignatureBlob = String(readLen = CallableValue(lambda:(self.wSignatureBlobLen.value - sizeof(self.padding))))
|
||||
self.padding = String(b"\x00" * 8, readLen = CallableValue(8))
|
||||
self.wSignatureBlobLen = UInt16Le(lambda:(sizeof(self.SignatureBlob) - 8))
|
||||
self.SignatureBlob = String(readLen = self.wSignatureBlobLen)
|
||||
self.padding = String(b"\x00" * 8, readLen = UInt8(8))
|
||||
|
||||
def getPublicKey(self):
|
||||
"""
|
||||
@@ -381,7 +380,7 @@ class ProprietaryServerCertificate(CompositeType):
|
||||
md5Digest = md5.new()
|
||||
md5Digest.update(s.getvalue())
|
||||
|
||||
return md5Digest.digest() + "\x00" + "\xff" * 45 + "\x01"
|
||||
return md5Digest.digest() + "\x00" + "\xff" * 46 + "\x01"
|
||||
|
||||
def sign(self):
|
||||
"""
|
||||
@@ -418,7 +417,7 @@ class X509CertificateChain(CompositeType):
|
||||
CompositeType.__init__(self)
|
||||
self.NumCertBlobs = UInt32Le()
|
||||
self.CertBlobArray = ArrayType(CertBlob, readLen = self.NumCertBlobs)
|
||||
self.padding = String(readLen = CallableValue(lambda:(8 + 4 * self.NumCertBlobs.value)))
|
||||
self.padding = String(readLen = UInt8(lambda:(8 + 4 * self.NumCertBlobs.value)))
|
||||
|
||||
def getPublicKey(self):
|
||||
"""
|
||||
@@ -447,8 +446,8 @@ class RSAPublicKey(CompositeType):
|
||||
self.bitlen = UInt32Le(lambda:((self.keylen.value - 8) * 8))
|
||||
self.datalen = UInt32Le(lambda:((self.bitlen.value / 8) - 1))
|
||||
self.pubExp = UInt32Le()
|
||||
self.modulus = String(readLen = CallableValue(lambda:(self.keylen.value - 8)))
|
||||
self.padding = String("\x00" * 8, readLen = CallableValue(8))
|
||||
self.modulus = String(readLen = UInt16Le(lambda:(self.keylen.value - 8)))
|
||||
self.padding = String("\x00" * 8, readLen = UInt8(8))
|
||||
|
||||
class ChannelDef(CompositeType):
|
||||
"""
|
||||
@@ -458,13 +457,13 @@ class ChannelDef(CompositeType):
|
||||
def __init__(self, name = "", options = 0):
|
||||
CompositeType.__init__(self)
|
||||
#name of channel
|
||||
self.name = String(name[0:8] + "\x00" * (8 - len(name)), readLen = CallableValue(8))
|
||||
self.name = String(name[0:8] + "\x00" * (8 - len(name)), readLen = UInt8(8))
|
||||
#unknown
|
||||
self.options = UInt32Le()
|
||||
|
||||
class ClientNetworkData(CompositeType):
|
||||
"""
|
||||
@summary: GCC client network block
|
||||
GCC client network block
|
||||
All channels asked by client are listed here
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240512.aspx
|
||||
"""
|
||||
@@ -477,7 +476,7 @@ class ClientNetworkData(CompositeType):
|
||||
|
||||
class ServerNetworkData(CompositeType):
|
||||
"""
|
||||
@summary: GCC server network block
|
||||
GCC server network block
|
||||
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):
|
||||
"""
|
||||
@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):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
@@ -519,21 +518,21 @@ class Settings(CompositeType):
|
||||
|
||||
def clientSettings():
|
||||
"""
|
||||
@summary: Build settings for client
|
||||
Build settings for client
|
||||
@return: Settings
|
||||
"""
|
||||
return Settings([ClientCoreData(), ClientNetworkData(), ClientSecurityData()])
|
||||
|
||||
def serverSettings():
|
||||
"""
|
||||
@summary: Build settings for server
|
||||
Build settings for server
|
||||
@return Settings
|
||||
"""
|
||||
return Settings([ServerCoreData(), ServerSecurityData(), ServerNetworkData()])
|
||||
|
||||
def readConferenceCreateRequest(s):
|
||||
"""
|
||||
@summary: Read a response from client
|
||||
Read a response from client
|
||||
GCC create request
|
||||
@param s: Stream
|
||||
@param client settings (Settings)
|
||||
@@ -554,13 +553,13 @@ def readConferenceCreateRequest(s):
|
||||
|
||||
per.readOctetStream(s, h221_cs_key, 4)
|
||||
length = per.readLength(s)
|
||||
clientSettings = Settings(readLen = CallableValue(length))
|
||||
clientSettings = Settings(readLen = UInt32Le(length))
|
||||
s.readType(clientSettings)
|
||||
return clientSettings
|
||||
|
||||
def readConferenceCreateResponse(s):
|
||||
"""
|
||||
@summary: Read response from server
|
||||
Read response from server
|
||||
and return server settings read from this response
|
||||
@param s: Stream
|
||||
@return: ServerSettings
|
||||
@@ -578,13 +577,13 @@ def readConferenceCreateResponse(s):
|
||||
raise InvalidExpectedDataException("cannot read h221_sc_key")
|
||||
|
||||
length = per.readLength(s)
|
||||
serverSettings = Settings(readLen = CallableValue(length))
|
||||
serverSettings = Settings(readLen = UInt32Le(length))
|
||||
s.readType(serverSettings)
|
||||
return serverSettings
|
||||
|
||||
def writeConferenceCreateRequest(userData):
|
||||
"""
|
||||
@summary: Write conference create request structure
|
||||
Write conference create request structure
|
||||
@param userData: Settings for client
|
||||
@return: GCC packet
|
||||
"""
|
||||
@@ -599,7 +598,7 @@ def writeConferenceCreateRequest(userData):
|
||||
|
||||
def writeConferenceCreateResponse(serverData):
|
||||
"""
|
||||
@summary: Write a conference create response packet
|
||||
Write a conference create response packet
|
||||
@param serverData: Settings for server
|
||||
@return: gcc packet
|
||||
"""
|
||||
@@ -608,6 +607,6 @@ def writeConferenceCreateResponse(serverData):
|
||||
|
||||
return (per.writeChoice(0), per.writeObjectIdentifier(t124_02_98_oid),
|
||||
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.writeOctetStream(h221_sc_key, 4), per.writeOctetStream(serverDataStream.getvalue()))
|
||||
@@ -22,11 +22,10 @@
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241880.aspx
|
||||
"""
|
||||
|
||||
from rdpy.core.type import CompositeType, CallableValue, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType, Stream
|
||||
from rdpy.core.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType, Stream
|
||||
from rdpy.core.error import InvalidExpectedDataException
|
||||
import rdpy.core.log as log
|
||||
import sec
|
||||
from t125 import gcc
|
||||
import sec, gcc
|
||||
from rdpy.security import rc4
|
||||
from rdpy.security import rsa_wrapper as rsa
|
||||
|
||||
@@ -98,8 +97,8 @@ class LicenseBinaryBlob(CompositeType):
|
||||
@summary: Blob use by license manager to exchange security data
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240481.aspx
|
||||
"""
|
||||
def __init__(self, blobType = BinaryBlobType.BB_ANY_BLOB, optional = False):
|
||||
CompositeType.__init__(self, optional = optional)
|
||||
def __init__(self, blobType = BinaryBlobType.BB_ANY_BLOB):
|
||||
CompositeType.__init__(self)
|
||||
self.wBlobType = UInt16Le(blobType, constant = True if blobType != BinaryBlobType.BB_ANY_BLOB else False)
|
||||
self.wBlobLen = UInt16Le(lambda:sizeof(self.blobData))
|
||||
self.blobData = String(readLen = self.wBlobLen)
|
||||
@@ -111,11 +110,11 @@ class LicensingErrorMessage(CompositeType):
|
||||
"""
|
||||
_MESSAGE_TYPE_ = MessageType.ERROR_ALERT
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.dwErrorCode = UInt32Le()
|
||||
self.dwStateTransition = UInt32Le()
|
||||
self.blob = LicenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB)
|
||||
self.blob = LicenseBinaryBlob(BinaryBlobType.BB_ERROR_BLOB)
|
||||
|
||||
class ProductInformation(CompositeType):
|
||||
"""
|
||||
@@ -160,9 +159,9 @@ class ServerLicenseRequest(CompositeType):
|
||||
"""
|
||||
_MESSAGE_TYPE_ = MessageType.LICENSE_REQUEST
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.serverRandom = String("\x00" * 32, readLen = CallableValue(32))
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.serverRandom = String("\x00" * 32, readLen = UInt8(32))
|
||||
self.productInfo = ProductInformation()
|
||||
self.keyExchangeList = LicenseBinaryBlob(BinaryBlobType.BB_KEY_EXCHG_ALG_BLOB)
|
||||
self.serverCertificate = LicenseBinaryBlob(BinaryBlobType.BB_CERTIFICATE_BLOB)
|
||||
@@ -176,14 +175,14 @@ class ClientNewLicenseRequest(CompositeType):
|
||||
"""
|
||||
_MESSAGE_TYPE_ = MessageType.NEW_LICENSE_REQUEST
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
#RSA and must be only RSA
|
||||
self.preferredKeyExchangeAlg = UInt32Le(0x00000001, constant = True)
|
||||
#pure microsoft client ;-)
|
||||
#http://msdn.microsoft.com/en-us/library/1040af38-c733-4fb3-acd1-8db8cc979eda#id10
|
||||
self.platformId = UInt32Le(0x04000000 | 0x00010000)
|
||||
self.clientRandom = String("\x00" * 32, readLen = CallableValue(32))
|
||||
self.clientRandom = String("\x00" * 32, readLen = UInt8(32))
|
||||
self.encryptedPreMasterSecret = LicenseBinaryBlob(BinaryBlobType.BB_RANDOM_BLOB)
|
||||
self.ClientUserName = LicenseBinaryBlob(BinaryBlobType.BB_CLIENT_USER_NAME_BLOB)
|
||||
self.ClientMachineName = LicenseBinaryBlob(BinaryBlobType.BB_CLIENT_MACHINE_NAME_BLOB)
|
||||
@@ -195,11 +194,11 @@ class ServerPlatformChallenge(CompositeType):
|
||||
"""
|
||||
_MESSAGE_TYPE_ = MessageType.PLATFORM_CHALLENGE
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.connectFlags = UInt32Le()
|
||||
self.encryptedPlatformChallenge = LicenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB)
|
||||
self.MACData = String(readLen = CallableValue(16))
|
||||
self.MACData = String(readLen = UInt8(16))
|
||||
|
||||
class ClientPLatformChallengeResponse(CompositeType):
|
||||
"""
|
||||
@@ -208,11 +207,11 @@ class ClientPLatformChallengeResponse(CompositeType):
|
||||
"""
|
||||
_MESSAGE_TYPE_ = MessageType.PLATFORM_CHALLENGE_RESPONSE
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.encryptedPlatformChallengeResponse = LicenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB)
|
||||
self.encryptedHWID = LicenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB)
|
||||
self.MACData = String(readLen = CallableValue(16))
|
||||
self.MACData = String(readLen = UInt8(16))
|
||||
|
||||
class LicPacket(CompositeType):
|
||||
"""
|
||||
@@ -232,7 +231,7 @@ class LicPacket(CompositeType):
|
||||
"""
|
||||
for c in [LicensingErrorMessage, ServerLicenseRequest, ClientNewLicenseRequest, ServerPlatformChallenge, ClientPLatformChallengeResponse]:
|
||||
if self.bMsgtype.value == c._MESSAGE_TYPE_:
|
||||
return c(readLen = self.wMsgSize - 4)
|
||||
return c()
|
||||
log.debug("unknown license message : %s"%self.bMsgtype.value)
|
||||
return String()
|
||||
|
||||
@@ -303,12 +302,9 @@ class LicenseManager(object):
|
||||
"""
|
||||
#get server information
|
||||
serverRandom = licenseRequest.serverRandom.value
|
||||
if self._transport.getGCCServerSettings().SC_SECURITY.serverCertificate._is_readed:
|
||||
serverCertificate = self._transport.getGCCServerSettings().SC_SECURITY.serverCertificate
|
||||
else:
|
||||
s = Stream(licenseRequest.serverCertificate.blobData.value)
|
||||
serverCertificate = gcc.ServerCertificate()
|
||||
s.readType(serverCertificate)
|
||||
s = Stream(licenseRequest.serverCertificate.blobData.value)
|
||||
serverCertificate = gcc.ServerCertificate()
|
||||
s.readType(serverCertificate)
|
||||
|
||||
#generate crypto values
|
||||
clientRandom = rsa.random(256)
|
||||
|
||||
@@ -27,7 +27,7 @@ It exist channel for file system order, audio channel, clipboard etc...
|
||||
from rdpy.core.layer import LayerAutomata, IStreamSender, Layer
|
||||
from rdpy.core.type import sizeof, Stream, UInt8, UInt16Le, String
|
||||
from rdpy.core.error import InvalidExpectedDataException, InvalidValue, InvalidSize, CallPureVirtualFuntion
|
||||
from ber import writeLength
|
||||
from rdpy.protocol.rdp.ber import writeLength
|
||||
import rdpy.core.log as log
|
||||
|
||||
import ber, gcc, per
|
||||
@@ -515,16 +515,15 @@ class Server(MCSLayer):
|
||||
self.readDomainParams(data)
|
||||
self.readDomainParams(data)
|
||||
self._clientSettings = gcc.readConferenceCreateRequest(Stream(ber.readOctetString(data)))
|
||||
|
||||
if not self._clientSettings.CS_NET is None:
|
||||
i = 1
|
||||
for channelDef in self._clientSettings.CS_NET.channelDefArray._array:
|
||||
self._serverSettings.SC_NET.channelIdArray._array.append(UInt16Le(i + Channel.MCS_GLOBAL_CHANNEL))
|
||||
#if channel can be handle by serve add it
|
||||
for serverChannelDef, layer in self._virtualChannels:
|
||||
if channelDef.name == serverChannelDef.name:
|
||||
self._channels[i + Channel.MCS_GLOBAL_CHANNEL] = layer
|
||||
i += 1
|
||||
|
||||
i = 1
|
||||
for channelDef in self._clientSettings.getBlock(gcc.MessageType.CS_NET).channelDefArray._array:
|
||||
self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelIdArray._array.append(UInt16Le(i + Channel.MCS_GLOBAL_CHANNEL))
|
||||
#if channel can be handle by serve add it
|
||||
for serverChannelDef, layer in self._virtualChannels:
|
||||
if channelDef.name == serverChannelDef.name:
|
||||
self._channels[i + Channel.MCS_GLOBAL_CHANNEL] = layer
|
||||
i += 1
|
||||
|
||||
self.sendConnectResponse()
|
||||
self.setNextState(self.recvErectDomainRequest)
|
||||
@@ -1,291 +0,0 @@
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
@summary: Credential Security Support Provider
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226764.aspx
|
||||
"""
|
||||
|
||||
from pyasn1.type import namedtype, univ, tag
|
||||
import pyasn1.codec.der.encoder as der_encoder
|
||||
import pyasn1.codec.der.decoder as der_decoder
|
||||
import pyasn1.codec.ber.encoder as ber_encoder
|
||||
|
||||
from rdpy.core.type import Stream
|
||||
from twisted.internet import protocol
|
||||
from OpenSSL import crypto
|
||||
from rdpy.security import x509
|
||||
from rdpy.core import error
|
||||
|
||||
class NegoToken(univ.Sequence):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('negoToken', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
)
|
||||
|
||||
class NegoData(univ.SequenceOf):
|
||||
"""
|
||||
@summary: contain spnego ntlm of kerberos data
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226781.aspx
|
||||
"""
|
||||
componentType = NegoToken()
|
||||
|
||||
class TSRequest(univ.Sequence):
|
||||
"""
|
||||
@summary: main structure
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226780.aspx
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('version', univ.Integer().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
namedtype.OptionalNamedType('negoTokens', NegoData().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
|
||||
namedtype.OptionalNamedType('authInfo', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))),
|
||||
namedtype.OptionalNamedType('pubKeyAuth', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))),
|
||||
namedtype.OptionalNamedType('errorCode', univ.Integer().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4)))
|
||||
)
|
||||
|
||||
class TSCredentials(univ.Sequence):
|
||||
"""
|
||||
@summary: contain user information
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226782.aspx
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('credType', univ.Integer().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
namedtype.NamedType('credentials', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
|
||||
)
|
||||
|
||||
class TSPasswordCreds(univ.Sequence):
|
||||
"""
|
||||
@summary: contain username and password
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226783.aspx
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('domainName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
namedtype.NamedType('userName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
|
||||
namedtype.NamedType('password', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2)))
|
||||
)
|
||||
|
||||
class TSCspDataDetail(univ.Sequence):
|
||||
"""
|
||||
@summary: smart card credentials
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226785.aspx
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('keySpec', univ.Integer().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
namedtype.OptionalNamedType('cardName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
|
||||
namedtype.OptionalNamedType('readerName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))),
|
||||
namedtype.OptionalNamedType('containerName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))),
|
||||
namedtype.OptionalNamedType('cspName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4)))
|
||||
)
|
||||
|
||||
class TSSmartCardCreds(univ.Sequence):
|
||||
"""
|
||||
@summary: smart card credentials
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226784.aspx
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('pin', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
namedtype.NamedType('cspData', TSCspDataDetail().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
|
||||
namedtype.OptionalNamedType('userHint', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))),
|
||||
namedtype.OptionalNamedType('domainHint', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)))
|
||||
)
|
||||
|
||||
class OpenSSLRSAPublicKey(univ.Sequence):
|
||||
"""
|
||||
@summary: asn1 public rsa key
|
||||
@see: https://tools.ietf.org/html/rfc3447
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('unknow', univ.Integer()),
|
||||
namedtype.NamedType('modulus', univ.Integer()),
|
||||
namedtype.NamedType('publicExponent', univ.Integer()),
|
||||
)
|
||||
|
||||
def encodeDERTRequest(negoTypes = [], authInfo = None, pubKeyAuth = None):
|
||||
"""
|
||||
@summary: create TSRequest from list of Type
|
||||
@param negoTypes: {list(Type)}
|
||||
@param authInfo: {str} authentication info TSCredentials encrypted with authentication protocol
|
||||
@param pubKeyAuth: {str} public key encrypted with authentication protocol
|
||||
@return: {str} TRequest der encoded
|
||||
"""
|
||||
negoData = NegoData().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))
|
||||
|
||||
#fill nego data tokens
|
||||
i = 0
|
||||
for negoType in negoTypes:
|
||||
s = Stream()
|
||||
s.writeType(negoType)
|
||||
negoToken = NegoToken()
|
||||
negoToken.setComponentByPosition(0, s.getvalue())
|
||||
negoData.setComponentByPosition(i, negoToken)
|
||||
i += 1
|
||||
|
||||
request = TSRequest()
|
||||
request.setComponentByName("version", univ.Integer(2).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
|
||||
|
||||
if i > 0:
|
||||
request.setComponentByName("negoTokens", negoData)
|
||||
|
||||
if not authInfo is None:
|
||||
request.setComponentByName("authInfo", univ.OctetString(authInfo).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2)))
|
||||
|
||||
if not pubKeyAuth is None:
|
||||
request.setComponentByName("pubKeyAuth", univ.OctetString(pubKeyAuth).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)))
|
||||
|
||||
return der_encoder.encode(request)
|
||||
|
||||
def decodeDERTRequest(s):
|
||||
"""
|
||||
@summary: Decode the stream as
|
||||
@param s: {str}
|
||||
"""
|
||||
return der_decoder.decode(s, asn1Spec=TSRequest())[0]
|
||||
|
||||
def getNegoTokens(tRequest):
|
||||
negoData = tRequest.getComponentByName("negoTokens")
|
||||
return [Stream(negoData.getComponentByPosition(i).getComponentByPosition(0).asOctets()) for i in range(len(negoData))]
|
||||
|
||||
def getPubKeyAuth(tRequest):
|
||||
return tRequest.getComponentByName("pubKeyAuth").asOctets()
|
||||
|
||||
def encodeDERTCredentials(domain, username, password):
|
||||
passwordCred = TSPasswordCreds()
|
||||
passwordCred.setComponentByName("domainName", univ.OctetString(domain).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
|
||||
passwordCred.setComponentByName("userName", univ.OctetString(username).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
|
||||
passwordCred.setComponentByName("password", univ.OctetString(password).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2)))
|
||||
|
||||
credentials = TSCredentials()
|
||||
credentials.setComponentByName("credType", univ.Integer(1).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
|
||||
credentials.setComponentByName("credentials", univ.OctetString(der_encoder.encode(passwordCred)).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
|
||||
|
||||
return der_encoder.encode(credentials)
|
||||
|
||||
class CSSP(protocol.Protocol):
|
||||
"""
|
||||
@summary: Handle CSSP connection
|
||||
Proxy class for authentication
|
||||
"""
|
||||
def __init__(self, layer, authenticationProtocol):
|
||||
"""
|
||||
@param layer: {type.Layer.RawLayer}
|
||||
@param authenticationProtocol: {sspi.IAuthenticationProtocol}
|
||||
"""
|
||||
self._layer = layer
|
||||
self._authenticationProtocol = authenticationProtocol
|
||||
#IGenericSecurityService
|
||||
self._interface = None
|
||||
#function call at the end of nego
|
||||
self._callback = None
|
||||
|
||||
def setFactory(self, factory):
|
||||
"""
|
||||
@summary: Call by RawLayer Factory
|
||||
@param param: RawLayerClientFactory or RawLayerFactory
|
||||
"""
|
||||
self._layer.setFactory(factory)
|
||||
|
||||
def dataReceived(self, data):
|
||||
"""
|
||||
@summary: Inherit from twisted.protocol class
|
||||
main event of received data
|
||||
@param data: string data receive from twisted
|
||||
"""
|
||||
self._layer.dataReceived(data)
|
||||
|
||||
def connectionLost(self, reason):
|
||||
"""
|
||||
@summary: Call from twisted engine when protocol is closed
|
||||
@param reason: str represent reason of close connection
|
||||
"""
|
||||
self._layer._factory.connectionLost(self, reason)
|
||||
|
||||
def connectionMade(self):
|
||||
"""
|
||||
@summary: install proxy
|
||||
"""
|
||||
self._layer.transport = self
|
||||
self._layer.getDescriptor = lambda:self.transport
|
||||
self._layer.connectionMade()
|
||||
|
||||
def write(self, data):
|
||||
"""
|
||||
@summary: write data on transport layer
|
||||
@param data: {str}
|
||||
"""
|
||||
self.transport.write(data)
|
||||
|
||||
def startTLS(self, sslContext):
|
||||
"""
|
||||
@summary: start TLS protocol
|
||||
@param sslContext: {ssl.ClientContextFactory | ssl.DefaultOpenSSLContextFactory} context use for TLS protocol
|
||||
"""
|
||||
self.transport.startTLS(sslContext)
|
||||
|
||||
def startNLA(self, sslContext, callback = None):
|
||||
"""
|
||||
@summary: start NLA authentication
|
||||
@param sslContext: {ssl.ClientContextFactory | ssl.DefaultOpenSSLContextFactory} context use for TLS protocol
|
||||
@param callback: {function} function call when cssp layer is read
|
||||
"""
|
||||
self._callback = callback
|
||||
self.startTLS(sslContext)
|
||||
#send negotiate message
|
||||
self.transport.write(encodeDERTRequest( negoTypes = [ self._authenticationProtocol.getNegotiateMessage() ] ))
|
||||
#next state is receive a challenge
|
||||
self.dataReceived = self.recvChallenge
|
||||
|
||||
def recvChallenge(self, data):
|
||||
"""
|
||||
@summary: second state in cssp automata
|
||||
@param data : {str} all data available on buffer
|
||||
"""
|
||||
request = decodeDERTRequest(data)
|
||||
message, self._interface = self._authenticationProtocol.getAuthenticateMessage(getNegoTokens(request)[0])
|
||||
#get back public key
|
||||
#convert from der to ber...
|
||||
pubKeyDer = crypto.dump_privatekey(crypto.FILETYPE_ASN1, self.transport.protocol._tlsConnection.get_peer_certificate().get_pubkey())
|
||||
pubKey = der_decoder.decode(pubKeyDer, asn1Spec=OpenSSLRSAPublicKey())[0]
|
||||
|
||||
rsa = x509.RSAPublicKey()
|
||||
rsa.setComponentByName("modulus", univ.Integer(pubKey.getComponentByName('modulus')._value))
|
||||
rsa.setComponentByName("publicExponent", univ.Integer(pubKey.getComponentByName('publicExponent')._value))
|
||||
self._pubKeyBer = ber_encoder.encode(rsa)
|
||||
|
||||
#send authenticate message with public key encoded
|
||||
self.transport.write(encodeDERTRequest( negoTypes = [ message ], pubKeyAuth = self._interface.GSS_WrapEx(self._pubKeyBer)))
|
||||
#next step is received public key incremented by one
|
||||
self.dataReceived = self.recvPubKeyInc
|
||||
|
||||
def recvPubKeyInc(self, data):
|
||||
"""
|
||||
@summary: the server send the pubKeyBer + 1
|
||||
@param data : {str} all data available on buffer
|
||||
"""
|
||||
request = decodeDERTRequest(data)
|
||||
pubKeyInc = self._interface.GSS_UnWrapEx(getPubKeyAuth(request))
|
||||
#check pubKeyInc = self._pubKeyBer + 1
|
||||
if not (self._pubKeyBer[1:] == pubKeyInc[1:] and ord(self._pubKeyBer[0]) + 1 == ord(pubKeyInc[0])):
|
||||
raise error.InvalidExpectedDataException("CSSP : Invalid public key increment")
|
||||
|
||||
domain, user, password = self._authenticationProtocol.getEncodedCredentials()
|
||||
#send credentials
|
||||
self.transport.write(encodeDERTRequest( authInfo = self._interface.GSS_WrapEx(encodeDERTCredentials(domain, user, password))))
|
||||
#reset state back to normal state
|
||||
self.dataReceived = lambda x: self.__class__.dataReceived(self, x)
|
||||
if not self._callback is None:
|
||||
self._callback()
|
||||
@@ -1,635 +0,0 @@
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
@summary: NTLM Authentication
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236621.aspx
|
||||
"""
|
||||
|
||||
import hashlib, hmac, struct, datetime
|
||||
import sspi
|
||||
import rdpy.security.pyDes as pyDes
|
||||
import rdpy.security.rc4 as rc4
|
||||
from rdpy.security.rsa_wrapper import random
|
||||
from rdpy.core.type import CompositeType, CallableValue, String, UInt8, UInt16Le, UInt24Le, UInt32Le, sizeof, Stream
|
||||
from rdpy.core import filetimes, error
|
||||
|
||||
class MajorVersion(object):
|
||||
"""
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236654.aspx
|
||||
@see: https://msdn.microsoft.com/en-us/library/a211d894-21bc-4b8b-86ba-b83d0c167b00#id29
|
||||
"""
|
||||
WINDOWS_MAJOR_VERSION_5 = 0x05
|
||||
WINDOWS_MAJOR_VERSION_6 = 0x06
|
||||
|
||||
class MinorVersion(object):
|
||||
"""
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236654.aspx
|
||||
@see: https://msdn.microsoft.com/en-us/library/a211d894-21bc-4b8b-86ba-b83d0c167b00#id30
|
||||
"""
|
||||
WINDOWS_MINOR_VERSION_0 = 0x00
|
||||
WINDOWS_MINOR_VERSION_1 = 0x01
|
||||
WINDOWS_MINOR_VERSION_2 = 0x02
|
||||
WINDOWS_MINOR_VERSION_3 = 0x03
|
||||
|
||||
class NTLMRevision(object):
|
||||
"""
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236654.aspx
|
||||
"""
|
||||
NTLMSSP_REVISION_W2K3 = 0x0F
|
||||
|
||||
class Negotiate(object):
|
||||
"""
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236650.aspx
|
||||
"""
|
||||
NTLMSSP_NEGOTIATE_56 = 0x80000000
|
||||
NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000
|
||||
NTLMSSP_NEGOTIATE_128 = 0x20000000
|
||||
NTLMSSP_NEGOTIATE_VERSION = 0x02000000
|
||||
NTLMSSP_NEGOTIATE_TARGET_INFO = 0x00800000
|
||||
NTLMSSP_REQUEST_NON_NT_SESSION_KEY = 0x00400000
|
||||
NTLMSSP_NEGOTIATE_IDENTIFY = 0x00100000
|
||||
NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000
|
||||
NTLMSSP_TARGET_TYPE_SERVER = 0x00020000
|
||||
NTLMSSP_TARGET_TYPE_DOMAIN = 0x00010000
|
||||
NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x00008000
|
||||
NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000
|
||||
NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED = 0x00001000
|
||||
NTLMSSP_NEGOTIATE_NTLM = 0x00000200
|
||||
NTLMSSP_NEGOTIATE_LM_KEY = 0x00000080
|
||||
NTLMSSP_NEGOTIATE_DATAGRAM = 0x00000040
|
||||
NTLMSSP_NEGOTIATE_SEAL = 0x00000020
|
||||
NTLMSSP_NEGOTIATE_SIGN = 0x00000010
|
||||
NTLMSSP_REQUEST_TARGET = 0x00000004
|
||||
NTLM_NEGOTIATE_OEM = 0x00000002
|
||||
NTLMSSP_NEGOTIATE_UNICODE = 0x00000001
|
||||
|
||||
class AvId(object):
|
||||
"""
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236646.aspx
|
||||
"""
|
||||
MsvAvEOL = 0x0000
|
||||
MsvAvNbComputerName = 0x0001
|
||||
MsvAvNbDomainName = 0x0002
|
||||
MsvAvDnsComputerName = 0x0003
|
||||
MsvAvDnsDomainName = 0x0004
|
||||
MsvAvDnsTreeName = 0x0005
|
||||
MsvAvFlags = 0x0006
|
||||
MsvAvTimestamp = 0x0007
|
||||
MsvAvSingleHost = 0x0008
|
||||
MsvAvTargetName = 0x0009
|
||||
MsvChannelBindings = 0x000A
|
||||
|
||||
def getPayLoadField(message, length, bufferOffset):
|
||||
if length == 0:
|
||||
return None
|
||||
offset = sizeof(message) - sizeof(message.Payload)
|
||||
start = bufferOffset - offset
|
||||
end = start + length
|
||||
return message.Payload.value[start:end]
|
||||
|
||||
class Version(CompositeType):
|
||||
"""
|
||||
@summary: Version structure as describe in NTLM spec
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236654.aspx
|
||||
"""
|
||||
def __init__(self, conditional):
|
||||
CompositeType.__init__(self, conditional = conditional)
|
||||
self.ProductMajorVersion = UInt8(MajorVersion.WINDOWS_MAJOR_VERSION_6)
|
||||
self.ProductMinorVersion = UInt8(MinorVersion.WINDOWS_MINOR_VERSION_0)
|
||||
self.ProductBuild = UInt16Le(6002)
|
||||
self.Reserved = UInt24Le()
|
||||
self.NTLMRevisionCurrent = UInt8(NTLMRevision.NTLMSSP_REVISION_W2K3)
|
||||
|
||||
class AvPair(CompositeType):
|
||||
"""
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236646.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.AvId = UInt16Le()
|
||||
self.AvLen = UInt16Le(lambda:sizeof(self.Value))
|
||||
self.Value = String(readLen = self.AvLen)
|
||||
|
||||
class MessageSignatureEx(CompositeType):
|
||||
"""
|
||||
@summary: Signature for message
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc422952.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.Version = UInt32Le(0x00000001, constant = True)
|
||||
self.Checksum = String(readLen = CallableValue(8))
|
||||
self.SeqNum = UInt32Le()
|
||||
|
||||
class NegotiateMessage(CompositeType):
|
||||
"""
|
||||
@summary: Message send from client to server to negotiate capability of NTLM Authentication
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236641.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.Signature = String("NTLMSSP\x00", readLen = CallableValue(8), constant = True)
|
||||
self.MessageType = UInt32Le(0x00000001, constant = True)
|
||||
|
||||
self.NegotiateFlags = UInt32Le()
|
||||
|
||||
self.DomainNameLen = UInt16Le()
|
||||
self.DomainNameMaxLen = UInt16Le(lambda:self.DomainNameLen.value)
|
||||
self.DomainNameBufferOffset = UInt32Le()
|
||||
|
||||
self.WorkstationLen = UInt16Le()
|
||||
self.WorkstationMaxLen = UInt16Le(lambda:self.WorkstationLen.value)
|
||||
self.WorkstationBufferOffset = UInt32Le()
|
||||
|
||||
self.Version = Version(conditional = lambda:(self.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_VERSION))
|
||||
|
||||
self.Payload = String()
|
||||
|
||||
class ChallengeMessage(CompositeType):
|
||||
"""
|
||||
@summary: Message send from server to client contains server challenge
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236642.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.Signature = String("NTLMSSP\x00", readLen = CallableValue(8), constant = True)
|
||||
self.MessageType = UInt32Le(0x00000002, constant = True)
|
||||
|
||||
self.TargetNameLen = UInt16Le()
|
||||
self.TargetNameMaxLen = UInt16Le(lambda:self.TargetNameLen.value)
|
||||
self.TargetNameBufferOffset = UInt32Le()
|
||||
|
||||
self.NegotiateFlags = UInt32Le()
|
||||
|
||||
self.ServerChallenge = String(readLen = CallableValue(8))
|
||||
self.Reserved = String("\x00" * 8, readLen = CallableValue(8))
|
||||
|
||||
self.TargetInfoLen = UInt16Le()
|
||||
self.TargetInfoMaxLen = UInt16Le(lambda:self.TargetInfoLen.value)
|
||||
self.TargetInfoBufferOffset = UInt32Le()
|
||||
|
||||
self.Version = Version(conditional = lambda:(self.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_VERSION))
|
||||
self.Payload = String()
|
||||
|
||||
def getTargetName(self):
|
||||
return getPayLoadField(self, self.TargetNameLen.value, self.TargetNameBufferOffset.value)
|
||||
|
||||
def getTargetInfo(self):
|
||||
return getPayLoadField(self, self.TargetInfoLen.value, self.TargetInfoBufferOffset.value)
|
||||
|
||||
def getTargetInfoAsAvPairArray(self):
|
||||
"""
|
||||
@summary: Parse Target info field to retrieve array of AvPair
|
||||
@return: {map(AvId, str)}
|
||||
"""
|
||||
result = {}
|
||||
s = Stream(self.getTargetInfo())
|
||||
while(True):
|
||||
avPair = AvPair()
|
||||
s.readType(avPair)
|
||||
if avPair.AvId.value == AvId.MsvAvEOL:
|
||||
return result
|
||||
result[avPair.AvId.value] = avPair.Value.value
|
||||
|
||||
|
||||
class AuthenticateMessage(CompositeType):
|
||||
"""
|
||||
@summary: Last message in ntlm authentication
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236643.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.Signature = String("NTLMSSP\x00", readLen = CallableValue(8), constant = True)
|
||||
self.MessageType = UInt32Le(0x00000003, constant = True)
|
||||
|
||||
self.LmChallengeResponseLen = UInt16Le()
|
||||
self.LmChallengeResponseMaxLen = UInt16Le(lambda:self.LmChallengeResponseLen.value)
|
||||
self.LmChallengeResponseBufferOffset = UInt32Le()
|
||||
|
||||
self.NtChallengeResponseLen = UInt16Le()
|
||||
self.NtChallengeResponseMaxLen = UInt16Le(lambda:self.NtChallengeResponseLen.value)
|
||||
self.NtChallengeResponseBufferOffset = UInt32Le()
|
||||
|
||||
self.DomainNameLen = UInt16Le()
|
||||
self.DomainNameMaxLen = UInt16Le(lambda:self.DomainNameLen.value)
|
||||
self.DomainNameBufferOffset = UInt32Le()
|
||||
|
||||
self.UserNameLen = UInt16Le()
|
||||
self.UserNameMaxLen = UInt16Le(lambda:self.UserNameLen.value)
|
||||
self.UserNameBufferOffset = UInt32Le()
|
||||
|
||||
self.WorkstationLen = UInt16Le()
|
||||
self.WorkstationMaxLen = UInt16Le(lambda:self.WorkstationLen.value)
|
||||
self.WorkstationBufferOffset = UInt32Le()
|
||||
|
||||
self.EncryptedRandomSessionLen = UInt16Le()
|
||||
self.EncryptedRandomSessionMaxLen = UInt16Le(lambda:self.EncryptedRandomSessionLen.value)
|
||||
self.EncryptedRandomSessionBufferOffset = UInt32Le()
|
||||
|
||||
self.NegotiateFlags = UInt32Le()
|
||||
self.Version = Version(conditional = lambda:(self.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_VERSION))
|
||||
|
||||
self.MIC = String("\x00" * 16, readLen = CallableValue(16))
|
||||
self.Payload = String()
|
||||
|
||||
def getUserName(self):
|
||||
return getPayLoadField(self, self.UserNameLen.value, self.UserNameBufferOffset.value)
|
||||
|
||||
def getDomainName(self):
|
||||
return getPayLoadField(self, self.DomainNameLen.value, self.DomainNameBufferOffset.value)
|
||||
|
||||
def getLmChallengeResponse(self):
|
||||
return getPayLoadField(self, self.LmChallengeResponseLen.value, self.LmChallengeResponseBufferOffset.value)
|
||||
|
||||
def getNtChallengeResponse(self):
|
||||
return getPayLoadField(self, self.NtChallengeResponseLen.value, self.NtChallengeResponseBufferOffset.value)
|
||||
|
||||
def getEncryptedRandomSession(self):
|
||||
return getPayLoadField(self, self.EncryptedRandomSessionLen.value, self.EncryptedRandomSessionBufferOffset.value)
|
||||
|
||||
def createAuthenticationMessage(NegFlag, domain, user, NtChallengeResponse, LmChallengeResponse, EncryptedRandomSessionKey, Workstation):
|
||||
"""
|
||||
@summary: Create an Authenticate Message
|
||||
@param domain: {str} domain microsoft
|
||||
@param user: {str} user microsoft
|
||||
@param NtChallengeResponse: {str} Challenge response
|
||||
@param LmChallengeResponse: {str} domain microsoft
|
||||
@param EncryptedRandomSessionKey: {str} EncryptedRandomSessionKey
|
||||
"""
|
||||
message = AuthenticateMessage()
|
||||
message.NegotiateFlags.value = NegFlag
|
||||
#fill message
|
||||
offset = sizeof(message)
|
||||
|
||||
message.DomainNameLen.value = len(domain)
|
||||
message.DomainNameBufferOffset.value = offset
|
||||
message.Payload.value += domain
|
||||
offset += len(domain)
|
||||
|
||||
message.UserNameLen.value = len(user)
|
||||
message.UserNameBufferOffset.value = offset
|
||||
message.Payload.value += user
|
||||
offset += len(user)
|
||||
|
||||
message.WorkstationLen.value = len(Workstation)
|
||||
message.WorkstationBufferOffset.value = offset
|
||||
message.Payload.value += Workstation
|
||||
offset += len(Workstation)
|
||||
|
||||
message.LmChallengeResponseLen.value = len(LmChallengeResponse)
|
||||
message.LmChallengeResponseBufferOffset.value = offset
|
||||
message.Payload.value += LmChallengeResponse
|
||||
offset += len(LmChallengeResponse)
|
||||
|
||||
message.NtChallengeResponseLen.value = len(NtChallengeResponse)
|
||||
message.NtChallengeResponseBufferOffset.value = offset
|
||||
message.Payload.value += NtChallengeResponse
|
||||
offset += len(NtChallengeResponse)
|
||||
|
||||
message.EncryptedRandomSessionLen.value = len(EncryptedRandomSessionKey)
|
||||
message.EncryptedRandomSessionBufferOffset.value = offset
|
||||
message.Payload.value += EncryptedRandomSessionKey
|
||||
offset += len(EncryptedRandomSessionKey)
|
||||
|
||||
return message
|
||||
|
||||
def expandDesKey(key):
|
||||
"""
|
||||
@summary: Expand the key from a 7-byte password key into a 8-byte DES key
|
||||
"""
|
||||
s = chr(((ord(key[0]) >> 1) & 0x7f) << 1)
|
||||
s = s + chr(((ord(key[0]) & 0x01) << 6 | ((ord(key[1]) >> 2) & 0x3f)) << 1)
|
||||
s = s + chr(((ord(key[1]) & 0x03) << 5 | ((ord(key[2]) >> 3) & 0x1f)) << 1)
|
||||
s = s + chr(((ord(key[2]) & 0x07) << 4 | ((ord(key[3]) >> 4) & 0x0f)) << 1)
|
||||
s = s + chr(((ord(key[3]) & 0x0f) << 3 | ((ord(key[4]) >> 5) & 0x07)) << 1)
|
||||
s = s + chr(((ord(key[4]) & 0x1f) << 2 | ((ord(key[5]) >> 6) & 0x03)) << 1)
|
||||
s = s + chr(((ord(key[5]) & 0x3f) << 1 | ((ord(key[6]) >> 7) & 0x01)) << 1)
|
||||
s = s + chr((ord(key[6]) & 0x7f) << 1)
|
||||
return s
|
||||
|
||||
def CurrentFileTimes():
|
||||
"""
|
||||
@summary: Current File times in 64 bits
|
||||
@return : {str[8]}
|
||||
"""
|
||||
return struct.pack("Q", filetimes.dt_to_filetime(datetime.datetime.now()))
|
||||
|
||||
def DES(key, data):
|
||||
"""
|
||||
@summary: DES use in microsoft specification
|
||||
@param key: {str} Des key on 56 bits or 7 bytes
|
||||
@param data: {str} data to encrypt
|
||||
"""
|
||||
return pyDes.des(expandDesKey(key)).encrypt(data)
|
||||
|
||||
def DESL(key, data):
|
||||
"""
|
||||
@summary: an krosoft security function (triple des = des + des + des ;-))
|
||||
@param key: {str} Des key
|
||||
@param data: {str} encrypted data
|
||||
"""
|
||||
return DES(key[0:7], data) + DES(key[7:14], data) + DES(key[14:16] + "\x00" * 5, data)
|
||||
|
||||
def UNICODE(s):
|
||||
"""
|
||||
@param s: source
|
||||
@return: {str} encoded in unicode
|
||||
"""
|
||||
return s.encode('utf-16le')
|
||||
|
||||
def MD4(s):
|
||||
"""
|
||||
@summary: compute the md4 sum
|
||||
@param s: {str} input data
|
||||
@return: {str} MD4(s)
|
||||
"""
|
||||
return hashlib.new('md4', s).digest()
|
||||
|
||||
def MD5(s):
|
||||
"""
|
||||
@summary: compute the md5 sum
|
||||
@param s: {str} input data
|
||||
@return: {str} MD5(s)
|
||||
"""
|
||||
return hashlib.new('md5', s).digest()
|
||||
|
||||
def Z(m):
|
||||
"""
|
||||
@summary: fill m zero in string
|
||||
@param m: {int} size of string
|
||||
@return: \x00 * m
|
||||
"""
|
||||
return "\x00" * m
|
||||
|
||||
def RC4K(key, plaintext):
|
||||
"""
|
||||
@summary: Context free of rc4 encoding
|
||||
@param key: {str} key
|
||||
@param plaintext: {str} plaintext
|
||||
@return {str} encrypted text
|
||||
"""
|
||||
return rc4.crypt(rc4.RC4Key(key), plaintext)
|
||||
|
||||
def KXKEYv2(SessionBaseKey, LmChallengeResponse, ServerChallenge):
|
||||
"""
|
||||
@summary: Key eXchange Key for NTLMv2
|
||||
@param SessionBaseKey: {str} computed by NTLMv1Anthentication or NTLMv2Authenticate function
|
||||
@param LmChallengeResponse : {str} computed by NTLMv1Anthentication or NTLMv2Authenticate function
|
||||
@param ServerChallenge : {str} Server chanllenge come from ChallengeMessage
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236710.aspx
|
||||
"""
|
||||
return SessionBaseKey
|
||||
|
||||
def SEALKEY(ExportedSessionKey, client):
|
||||
if client:
|
||||
return MD5(ExportedSessionKey + "session key to client-to-server sealing key magic constant\0")
|
||||
else:
|
||||
return MD5(ExportedSessionKey + "session key to server-to-client sealing key magic constant\0")
|
||||
|
||||
def SIGNKEY(ExportedSessionKey, client):
|
||||
if client:
|
||||
return MD5(ExportedSessionKey + "session key to client-to-server signing key magic constant\0")
|
||||
else:
|
||||
return MD5(ExportedSessionKey + "session key to server-to-client signing key magic constant\0")
|
||||
|
||||
def HMAC_MD5(key, data):
|
||||
"""
|
||||
@summary: classic HMAC algorithm with MD5 sum
|
||||
@param key: {str} key
|
||||
@param data: {str} data
|
||||
"""
|
||||
return hmac.new(key, data, hashlib.md5).digest()
|
||||
|
||||
def NTOWFv2(Passwd, User, UserDom):
|
||||
"""
|
||||
@summary: Version 2 of NTLM hash function
|
||||
@param Passwd: {str} Password
|
||||
@param User: {str} Username
|
||||
@param UserDom: {str} microsoft domain
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236700.aspx
|
||||
"""
|
||||
return HMAC_MD5(MD4(UNICODE(Passwd)), UNICODE(User.upper() + UserDom))
|
||||
|
||||
def LMOWFv2(Passwd, User, UserDom):
|
||||
"""
|
||||
@summary: Same as NTOWFv2
|
||||
@param Passwd: {str} Password
|
||||
@param User: {str} Username
|
||||
@param UserDom: {str} microsoft domain
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236700.aspx
|
||||
"""
|
||||
return NTOWFv2(Passwd, User, UserDom)
|
||||
|
||||
def ComputeResponsev2(ResponseKeyNT, ResponseKeyLM, ServerChallenge, ClientChallenge, Time, ServerName):
|
||||
"""
|
||||
@summary: process NTLMv2 Authenticate hash
|
||||
@param NegFlg: {int} Negotiation flags come from challenge message
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236700.aspx
|
||||
"""
|
||||
Responserversion = "\x01"
|
||||
HiResponserversion = "\x01"
|
||||
|
||||
temp = Responserversion + HiResponserversion + Z(6) + Time + ClientChallenge + Z(4) + ServerName
|
||||
NTProofStr = HMAC_MD5(ResponseKeyNT, ServerChallenge + temp)
|
||||
NtChallengeResponse = NTProofStr + temp
|
||||
LmChallengeResponse = HMAC_MD5(ResponseKeyLM, ServerChallenge + ClientChallenge) + ClientChallenge
|
||||
|
||||
SessionBaseKey = HMAC_MD5(ResponseKeyNT, NTProofStr)
|
||||
|
||||
return NtChallengeResponse, LmChallengeResponse, SessionBaseKey
|
||||
|
||||
def MAC(handle, SigningKey, SeqNum, Message):
|
||||
"""
|
||||
@summary: generate signature for application message
|
||||
@param handle: {rc4.RC4Key} handle on crypt
|
||||
@param SigningKey: {str} Signing key
|
||||
@param SeqNum: {int} Sequence number
|
||||
@param Message: Message to sign
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc422952.aspx
|
||||
"""
|
||||
signature = MessageSignatureEx()
|
||||
signature.SeqNum.value = SeqNum
|
||||
|
||||
#write the SeqNum
|
||||
s = Stream()
|
||||
s.writeType(signature.SeqNum)
|
||||
|
||||
signature.Checksum.value = rc4.crypt(handle, HMAC_MD5(SigningKey, s.getvalue() + Message)[:8])
|
||||
|
||||
return signature
|
||||
|
||||
def MIC(ExportedSessionKey, negotiateMessage, challengeMessage, authenticateMessage):
|
||||
"""
|
||||
@summary: Compute MIC signature
|
||||
@param negotiateMessage: {NegotiateMessage}
|
||||
@param challengeMessage: {ChallengeMessage}
|
||||
@param authenticateMessage: {AuthenticateMessage}
|
||||
@return: {str} signature
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236676.aspx
|
||||
"""
|
||||
s = Stream()
|
||||
s.writeType((negotiateMessage, challengeMessage, authenticateMessage))
|
||||
return HMAC_MD5(ExportedSessionKey, s.getvalue())
|
||||
|
||||
class NTLMv2(sspi.IAuthenticationProtocol):
|
||||
"""
|
||||
@summary: Handle NTLMv2 Authentication
|
||||
"""
|
||||
def __init__(self, domain, user, password):
|
||||
self._domain = domain
|
||||
self._user = user
|
||||
self._password = password
|
||||
self._enableUnicode = False
|
||||
#https://msdn.microsoft.com/en-us/library/cc236700.aspx
|
||||
self._ResponseKeyNT = NTOWFv2(password, user, domain)
|
||||
self._ResponseKeyLM = LMOWFv2(password, user, domain)
|
||||
|
||||
#For MIC computation
|
||||
self._negotiateMessage = None
|
||||
self._challengeMessage = None
|
||||
self._authenticateMessage = None
|
||||
|
||||
def getNegotiateMessage(self):
|
||||
"""
|
||||
@summary: generate first handshake messgae
|
||||
"""
|
||||
self._negotiateMessage = NegotiateMessage()
|
||||
self._negotiateMessage.NegotiateFlags.value = (Negotiate.NTLMSSP_NEGOTIATE_KEY_EXCH |
|
||||
Negotiate.NTLMSSP_NEGOTIATE_128 |
|
||||
Negotiate.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY |
|
||||
Negotiate.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
|
||||
Negotiate.NTLMSSP_NEGOTIATE_NTLM |
|
||||
Negotiate.NTLMSSP_NEGOTIATE_SEAL |
|
||||
Negotiate.NTLMSSP_NEGOTIATE_SIGN |
|
||||
Negotiate.NTLMSSP_REQUEST_TARGET |
|
||||
Negotiate.NTLMSSP_NEGOTIATE_UNICODE)
|
||||
return self._negotiateMessage
|
||||
|
||||
def getAuthenticateMessage(self, s):
|
||||
"""
|
||||
@summary: Client last handshake message
|
||||
@param s: {Stream} challenge message stream
|
||||
@return: {(AuthenticateMessage, NTLMv2SecurityInterface)} Last handshake message and security interface use to encrypt
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236676.aspx
|
||||
"""
|
||||
self._challengeMessage = ChallengeMessage()
|
||||
s.readType(self._challengeMessage)
|
||||
|
||||
ServerChallenge = self._challengeMessage.ServerChallenge.value
|
||||
ClientChallenge = random(64)
|
||||
|
||||
computeMIC = False
|
||||
ServerName = self._challengeMessage.getTargetInfo()
|
||||
infos = self._challengeMessage.getTargetInfoAsAvPairArray()
|
||||
if infos.has_key(AvId.MsvAvTimestamp):
|
||||
Timestamp = infos[AvId.MsvAvTimestamp]
|
||||
computeMIC = True
|
||||
else:
|
||||
Timestamp = CurrentFileTimes()
|
||||
|
||||
|
||||
NtChallengeResponse, LmChallengeResponse, SessionBaseKey = ComputeResponsev2(self._ResponseKeyNT, self._ResponseKeyLM, ServerChallenge, ClientChallenge, Timestamp, ServerName)
|
||||
KeyExchangeKey = KXKEYv2(SessionBaseKey, LmChallengeResponse, ServerChallenge)
|
||||
ExportedSessionKey = random(128)
|
||||
EncryptedRandomSessionKey = RC4K(KeyExchangeKey, ExportedSessionKey)
|
||||
|
||||
domain, user = self._domain, self._user
|
||||
if self._challengeMessage.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_UNICODE:
|
||||
self._enableUnicode = True
|
||||
domain, user = UNICODE(domain), UNICODE(user)
|
||||
self._authenticateMessage = createAuthenticationMessage(self._challengeMessage.NegotiateFlags.value, domain, user, NtChallengeResponse, LmChallengeResponse, EncryptedRandomSessionKey, "")
|
||||
|
||||
if computeMIC:
|
||||
self._authenticateMessage.MIC.value = MIC(ExportedSessionKey, self._negotiateMessage, self._challengeMessage, self._authenticateMessage)
|
||||
else:
|
||||
self._authenticateMessage.MIC._conditional = lambda:False
|
||||
|
||||
ClientSigningKey = SIGNKEY(ExportedSessionKey, True)
|
||||
ServerSigningKey = SIGNKEY(ExportedSessionKey, False)
|
||||
ClientSealingKey = SEALKEY(ExportedSessionKey, True)
|
||||
ServerSealingKey = SEALKEY(ExportedSessionKey, False)
|
||||
|
||||
interface = NTLMv2SecurityInterface(rc4.RC4Key(ClientSealingKey), rc4.RC4Key(ServerSealingKey), ClientSigningKey, ServerSigningKey)
|
||||
|
||||
return self._authenticateMessage, interface
|
||||
|
||||
def getEncodedCredentials(self):
|
||||
"""
|
||||
@summary: return encoded credentials accorded with authentication protocol nego
|
||||
@return: (domain, username, password)
|
||||
"""
|
||||
if self._enableUnicode:
|
||||
return UNICODE(self._domain), UNICODE(self._user), UNICODE(self._password)
|
||||
else:
|
||||
return self._domain, self._user, self._password
|
||||
|
||||
|
||||
class NTLMv2SecurityInterface(sspi.IGenericSecurityService):
|
||||
"""
|
||||
@summary: Generic Security Service for NTLM session
|
||||
"""
|
||||
def __init__(self, encryptHandle, decryptHandle, signingKey, verifyKey):
|
||||
"""
|
||||
@param encryptHandle: {rc4.RC4Key} rc4 keystream for encrypt phase
|
||||
@param decryptHandle: {rc4.RC4Key} rc4 keystream for decrypt phase
|
||||
@param signingKey: {str} signingKey
|
||||
@param verifyKey: {str} verifyKey
|
||||
"""
|
||||
self._encryptHandle = encryptHandle
|
||||
self._decryptHandle = decryptHandle
|
||||
self._signingKey = signingKey
|
||||
self._verifyKey = verifyKey
|
||||
self._seqNum = 0
|
||||
|
||||
def GSS_WrapEx(self, data):
|
||||
"""
|
||||
@summary: Encrypt function for NTLMv2 security service
|
||||
@param data: data to encrypt
|
||||
@return: {str} encrypted data
|
||||
"""
|
||||
encryptedData = rc4.crypt(self._encryptHandle, data)
|
||||
signature = MAC(self._encryptHandle, self._signingKey, self._seqNum, data)
|
||||
self._seqNum += 1
|
||||
s = Stream()
|
||||
s.writeType(signature)
|
||||
return s.getvalue() + encryptedData
|
||||
|
||||
def GSS_UnWrapEx(self, data):
|
||||
"""
|
||||
@summary: decrypt data with key exchange in Authentication protocol
|
||||
@param data: {str}
|
||||
"""
|
||||
signature = MessageSignatureEx()
|
||||
message = String()
|
||||
s = Stream(data)
|
||||
s.readType((signature, message))
|
||||
|
||||
#decrypt message
|
||||
plaintextMessage = rc4.crypt(self._decryptHandle, message.value)
|
||||
checksum = rc4.crypt(self._decryptHandle, signature.Checksum.value)
|
||||
|
||||
#recompute checksum
|
||||
t = Stream()
|
||||
t.writeType(signature.SeqNum)
|
||||
verify = HMAC_MD5(self._verifyKey, t.getvalue() + plaintextMessage)[:8]
|
||||
if verify != checksum:
|
||||
raise error.InvalidExpectedDataException("NTLMv2SecurityInterface : Invalid checksum")
|
||||
|
||||
return plaintextMessage
|
||||
@@ -1,69 +0,0 @@
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
@summary: security service provider interface (Microsoft)
|
||||
"""
|
||||
|
||||
from rdpy.core.error import CallPureVirtualFuntion
|
||||
|
||||
class IAuthenticationProtocol(object):
|
||||
"""
|
||||
@summary: generic class for authentication Protocol (ex: ntlmv2, SPNEGO or kerberos)
|
||||
"""
|
||||
def getNegotiateMessage(self):
|
||||
"""
|
||||
@summary: Client first handshake message for authentication protocol
|
||||
@return: {object} first handshake message
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getNegotiateMessage", "IAuthenticationProtocol"))
|
||||
|
||||
def getAuthenticateMessage(self, s):
|
||||
"""
|
||||
@summary: Client last handshake message
|
||||
@param s: {Stream} challenge message stream
|
||||
@return: {(object, IGenericSecurityService)} Last handshake message and interface for application
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getAuthenticateMessage", "IAuthenticationProtocol"))
|
||||
|
||||
def getEncodedCredentials(self):
|
||||
"""
|
||||
@summary: return encoded credentials accorded with authentication protocol nego
|
||||
@return: (domain, username, password)
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getEncodedCredentials", "IAuthenticationProtocol"))
|
||||
|
||||
class IGenericSecurityService(object):
|
||||
"""
|
||||
@summary: use by application from authentification protocol
|
||||
@see: http://www.rfc-editor.org/rfc/rfc2743.txt
|
||||
"""
|
||||
def GSS_WrapEx(self, data):
|
||||
"""
|
||||
@summary: encrypt data with key exchange in Authentication protocol
|
||||
@param data: {str}
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "GSS_WrapEx", "IGenericSecurityService"))
|
||||
|
||||
def GSS_UnWrapEx(self, data):
|
||||
"""
|
||||
@summary: decrypt data with key exchange in Authentication protocol
|
||||
@param data: {str}
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "GSS_UnWrapEx", "IGenericSecurityService"))
|
||||
@@ -24,7 +24,7 @@ Definition of structure use for capabilities nego
|
||||
Use in PDU layer
|
||||
"""
|
||||
|
||||
from rdpy.core.type import CompositeType, CallableValue, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType
|
||||
from rdpy.core.type import CompositeType, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType
|
||||
|
||||
class CapsType(object):
|
||||
"""
|
||||
@@ -308,7 +308,7 @@ class OrderCapability(CompositeType):
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.terminalDescriptor = String("\x00" * 16, readLen = CallableValue(16))
|
||||
self.terminalDescriptor = String("\x00" * 16, readLen = UInt8(16))
|
||||
self.pad4octetsA = UInt32Le(0)
|
||||
self.desktopSaveXGranularity = UInt16Le(1)
|
||||
self.desktopSaveYGranularity = UInt16Le(20)
|
||||
@@ -316,7 +316,7 @@ class OrderCapability(CompositeType):
|
||||
self.maximumOrderLevel = UInt16Le(1)
|
||||
self.numberFonts = UInt16Le()
|
||||
self.orderFlags = UInt16Le(OrderFlag.NEGOTIATEORDERSUPPORT)
|
||||
self.orderSupport = ArrayType(UInt8, init = [UInt8(0) for _ in range (0, 32)], readLen = CallableValue(32))
|
||||
self.orderSupport = ArrayType(UInt8, init = [UInt8(0) for _ in range (0, 32)], readLen = UInt8(32))
|
||||
self.textFlags = UInt16Le()
|
||||
self.orderSupportExFlags = UInt16Le()
|
||||
self.pad4octetsB = UInt32Le()
|
||||
@@ -388,7 +388,7 @@ class InputCapability(CompositeType):
|
||||
#same value as gcc.ClientCoreSettings.keyboardFnKeys
|
||||
self.keyboardFunctionKey = UInt32Le()
|
||||
#same value as gcc.ClientCoreSettingrrs.imeFileName
|
||||
self.imeFileName = String("\x00" * 64, readLen = CallableValue(64))
|
||||
self.imeFileName = String("\x00" * 64, readLen = UInt8(64))
|
||||
|
||||
class BrushCapability(CompositeType):
|
||||
"""
|
||||
@@ -412,7 +412,7 @@ class GlyphCapability(CompositeType):
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.glyphCache = ArrayType(CacheEntry, init = [CacheEntry() for _ in range(0,10)], readLen = CallableValue(10))
|
||||
self.glyphCache = ArrayType(CacheEntry, init = [CacheEntry() for _ in range(0,10)], readLen = UInt8(10))
|
||||
self.fragCache = UInt32Le()
|
||||
#all fonts are sent with bitmap format (very expensive)
|
||||
self.glyphSupportLevel = UInt16Le(GlyphSupport.GLYPH_SUPPORT_NONE)
|
||||
|
||||
@@ -22,7 +22,7 @@ Implement the main graphic layer
|
||||
|
||||
In this layer are managed all mains bitmap update orders end user inputs
|
||||
"""
|
||||
from rdpy.core.type import CompositeType, CallableValue, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType
|
||||
from rdpy.core.type import CompositeType, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType
|
||||
from rdpy.core.error import InvalidExpectedDataException
|
||||
import rdpy.core.log as log
|
||||
import caps, order
|
||||
@@ -429,8 +429,7 @@ class ShareControlHeader(CompositeType):
|
||||
#share control header
|
||||
self.totalLength = UInt16Le(totalLength)
|
||||
self.pduType = UInt16Le(pduType)
|
||||
#for xp sp3 and deactiveallpdu PDUSource may not be present
|
||||
self.PDUSource = UInt16Le(userId, optional = True)
|
||||
self.PDUSource = UInt16Le(userId)
|
||||
|
||||
class ShareDataHeader(CompositeType):
|
||||
"""
|
||||
@@ -520,9 +519,7 @@ class DeactiveAllPDU(CompositeType):
|
||||
_PDUTYPE_ = PDUType.PDUTYPE_DEACTIVATEALLPDU
|
||||
|
||||
def __init__(self):
|
||||
#in old version this packet is empty i don't know
|
||||
#and not specified
|
||||
CompositeType.__init__(self, optional = True)
|
||||
CompositeType.__init__(self)
|
||||
self.shareId = UInt32Le()
|
||||
self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor))
|
||||
self.sourceDescriptor = String("rdpy", readLen = self.lengthSourceDescriptor)
|
||||
@@ -670,7 +667,7 @@ class PersistentListPDU(CompositeType):
|
||||
self.bitMask = UInt8()
|
||||
self.pad2 = UInt8()
|
||||
self.pad3 = UInt16Le()
|
||||
self.entries = ArrayType(PersistentListEntry, readLen = CallableValue(lambda:(self.numEntriesCache0 + self.numEntriesCache1 + self.numEntriesCache2 + self.numEntriesCache3 + self.numEntriesCache4)))
|
||||
self.entries = ArrayType(PersistentListEntry, readLen = UInt16Le(lambda:(self.numEntriesCache0 + self.numEntriesCache1 + self.numEntriesCache2 + self.numEntriesCache3 + self.numEntriesCache4)))
|
||||
|
||||
class ClientInputEventPDU(CompositeType):
|
||||
"""
|
||||
@@ -873,7 +870,7 @@ class BitmapData(CompositeType):
|
||||
self.flags = UInt16Le()
|
||||
self.bitmapLength = UInt16Le(lambda:(sizeof(self.bitmapComprHdr) + sizeof(self.bitmapDataStream)))
|
||||
self.bitmapComprHdr = BitmapCompressedDataHeader(bodySize = lambda:sizeof(self.bitmapDataStream), scanWidth = lambda:self.width.value, uncompressedSize = lambda:(self.width.value * self.height.value * self.bitsPerPixel.value), conditional = lambda:((self.flags.value & BitmapFlag.BITMAP_COMPRESSION) and not (self.flags.value & BitmapFlag.NO_BITMAP_COMPRESSION_HDR)))
|
||||
self.bitmapDataStream = String(bitmapDataStream, readLen = CallableValue(lambda:(self.bitmapLength.value if (not self.flags.value & BitmapFlag.BITMAP_COMPRESSION or self.flags.value & BitmapFlag.NO_BITMAP_COMPRESSION_HDR) else self.bitmapComprHdr.cbCompMainBodySize.value)))
|
||||
self.bitmapDataStream = String(bitmapDataStream, readLen = UInt16Le(lambda:(self.bitmapLength.value if (not self.flags.value & BitmapFlag.BITMAP_COMPRESSION or self.flags.value & BitmapFlag.NO_BITMAP_COMPRESSION_HDR) else self.bitmapComprHdr.cbCompMainBodySize.value)))
|
||||
|
||||
class FastPathBitmapUpdateDataPDU(CompositeType):
|
||||
"""
|
||||
|
||||
@@ -179,9 +179,6 @@ class Client(PDULayer):
|
||||
|
||||
for cap in pdu.pduMessage.capabilitySets._array:
|
||||
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()
|
||||
#send synchronize
|
||||
@@ -287,14 +284,11 @@ class Client(PDULayer):
|
||||
@param dataPDU: DataPDU object
|
||||
"""
|
||||
if dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
|
||||
#ignore 0 error code because is not an error code
|
||||
if dataPDU.pduData.errorInfo.value == 0:
|
||||
return
|
||||
errorMessage = "Unknown code %s"%hex(dataPDU.pduData.errorInfo.value)
|
||||
if data.ErrorInfo._MESSAGES_.has_key(dataPDU.pduData.errorInfo):
|
||||
errorMessage = data.ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo]
|
||||
errorMessage = data.ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo]
|
||||
|
||||
log.error("INFO PDU : %s"%errorMessage)
|
||||
|
||||
elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SHUTDOWN_DENIED:
|
||||
#may be an event to ask to user
|
||||
self._transport.close()
|
||||
@@ -318,7 +312,7 @@ class Client(PDULayer):
|
||||
generalCapability = self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability
|
||||
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS
|
||||
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:
|
||||
generalCapability.extraFlags.value |= caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED
|
||||
|
||||
@@ -419,9 +413,6 @@ class Server(PDULayer):
|
||||
#find use full flag
|
||||
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)
|
||||
|
||||
def recvClientSynchronizePDU(self, s):
|
||||
@@ -534,7 +525,7 @@ class Server(PDULayer):
|
||||
generalCapability = self._serverCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability
|
||||
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS
|
||||
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.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX
|
||||
|
||||
@@ -27,17 +27,7 @@ import pdu.layer
|
||||
import pdu.data
|
||||
import pdu.caps
|
||||
import rdpy.core.log as log
|
||||
import tpkt, x224, sec
|
||||
from t125 import mcs, gcc
|
||||
from nla import cssp, ntlm
|
||||
|
||||
class SecurityLevel(object):
|
||||
"""
|
||||
@summary: RDP security level
|
||||
"""
|
||||
RDP_LEVEL_RDP = 0
|
||||
RDP_LEVEL_SSL = 1
|
||||
RDP_LEVEL_NLA = 2
|
||||
import tpkt, x224, mcs, gcc, sec
|
||||
|
||||
class RDPClientController(pdu.layer.PDUClientListener):
|
||||
"""
|
||||
@@ -67,7 +57,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
@return: return Protocol layer for twisted
|
||||
In case of RDP TPKT is the Raw layer
|
||||
"""
|
||||
return cssp.CSSP(self._tpktLayer, ntlm.NTLMv2(self._secLayer._info.domain.value, self._secLayer._info.userName.value, self._secLayer._info.password.value))
|
||||
return self._tpktLayer
|
||||
|
||||
def getColorDepth(self):
|
||||
"""
|
||||
@@ -147,14 +137,12 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
def setSecurityLevel(self, level):
|
||||
"""
|
||||
@summary: Request basic security
|
||||
@param level: {SecurityLevel}
|
||||
@param level: {str} (ssl | rdp)
|
||||
"""
|
||||
if level == SecurityLevel.RDP_LEVEL_RDP:
|
||||
if level == "rdp":
|
||||
self._x224Layer._requestedProtocol = x224.Protocols.PROTOCOL_RDP
|
||||
elif level == SecurityLevel.RDP_LEVEL_SSL:
|
||||
elif level == "ssl":
|
||||
self._x224Layer._requestedProtocol = x224.Protocols.PROTOCOL_SSL
|
||||
elif level == SecurityLevel.RDP_LEVEL_NLA:
|
||||
self._x224Layer._requestedProtocol = x224.Protocols.PROTOCOL_SSL | x224.Protocols.PROTOCOL_HYBRID
|
||||
|
||||
def addClientObserver(self, observer):
|
||||
"""
|
||||
@@ -359,7 +347,6 @@ class RDPServerController(pdu.layer.PDUServerListener):
|
||||
self._x224Layer = x224.Server(self._mcsLayer, privateKeyFileName, certificateFileName, False)
|
||||
#transport packet (protocol layer)
|
||||
self._tpktLayer = tpkt.TPKT(self._x224Layer)
|
||||
|
||||
#fastpath stack
|
||||
self._pduLayer.initFastPath(self._secLayer)
|
||||
self._secLayer.initFastPath(self._tpktLayer)
|
||||
@@ -520,9 +507,8 @@ class ClientFactory(layer.RawLayerClientFactory):
|
||||
@summary: Factory of Client RDP protocol
|
||||
@param reason: twisted reason
|
||||
"""
|
||||
def connectionLost(self, csspLayer, reason):
|
||||
def connectionLost(self, tpktLayer, reason):
|
||||
#retrieve controller
|
||||
tpktLayer = csspLayer._layer
|
||||
x224Layer = tpktLayer._presentation
|
||||
mcsLayer = x224Layer._presentation
|
||||
secLayer = mcsLayer._channels[mcs.Channel.MCS_GLOBAL_CHANNEL]
|
||||
|
||||
@@ -22,9 +22,8 @@ RDP Standard security layer
|
||||
"""
|
||||
|
||||
import sha, md5
|
||||
import lic, tpkt
|
||||
from t125 import gcc, mcs
|
||||
from rdpy.core.type import CompositeType, CallableValue, Stream, UInt32Le, UInt16Le, String, sizeof, UInt8
|
||||
import gcc, lic, tpkt, mcs
|
||||
from rdpy.core.type import CompositeType, Stream, UInt32Le, UInt16Le, String, sizeof, UInt8
|
||||
from rdpy.core.layer import LayerAutomata, IStreamSender
|
||||
from rdpy.core.error import InvalidExpectedDataException
|
||||
from rdpy.core import log
|
||||
@@ -165,12 +164,12 @@ def macData(macSaltKey, data):
|
||||
md5Digest = md5.new()
|
||||
|
||||
#encode length
|
||||
dataLength = Stream()
|
||||
dataLength.writeType(UInt32Le(len(data)))
|
||||
s = Stream()
|
||||
s.writeType(UInt32Le(len(data)))
|
||||
|
||||
sha1Digest.update(macSaltKey)
|
||||
sha1Digest.update("\x36" * 40)
|
||||
sha1Digest.update(dataLength.getvalue())
|
||||
sha1Digest.update(s.getvalue())
|
||||
sha1Digest.update(data)
|
||||
|
||||
sha1Sig = sha1Digest.digest()
|
||||
@@ -181,38 +180,6 @@ def macData(macSaltKey, data):
|
||||
|
||||
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):
|
||||
"""
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240792.aspx
|
||||
@@ -309,8 +276,8 @@ class ClientSecurityExchangePDU(CompositeType):
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.length = UInt32Le(lambda:(sizeof(self) - 4))
|
||||
self.encryptedClientRandom = String(readLen = CallableValue(lambda:(self.length.value - 8)))
|
||||
self.padding = String("\x00" * 8, readLen = CallableValue(8))
|
||||
self.encryptedClientRandom = String(readLen = UInt8(lambda:(self.length.value - 8)))
|
||||
self.padding = String("\x00" * 8, readLen = UInt8(8))
|
||||
|
||||
class RDPInfo(CompositeType):
|
||||
"""
|
||||
@@ -330,13 +297,13 @@ class RDPInfo(CompositeType):
|
||||
self.cbAlternateShell = UInt16Le(lambda:sizeof(self.alternateShell) - 2)
|
||||
self.cbWorkingDir = UInt16Le(lambda:sizeof(self.workingDir) - 2)
|
||||
#microsoft domain
|
||||
self.domain = String(readLen = CallableValue(lambda:self.cbDomain.value + 2), unicode = True)
|
||||
self.userName = String(readLen = CallableValue(lambda:self.cbUserName.value + 2), unicode = True)
|
||||
self.password = String(readLen = CallableValue(lambda:self.cbPassword.value + 2), unicode = True)
|
||||
self.domain = String(readLen = UInt16Le(lambda:self.cbDomain.value + 2), unicode = True)
|
||||
self.userName = String(readLen = UInt16Le(lambda:self.cbUserName.value + 2), unicode = True)
|
||||
self.password = String(readLen = UInt16Le(lambda:self.cbPassword.value + 2), unicode = True)
|
||||
#shell execute at start of session
|
||||
self.alternateShell = String(readLen = CallableValue(lambda:self.cbAlternateShell.value + 2), unicode = True)
|
||||
self.alternateShell = String(readLen = UInt16Le(lambda:self.cbAlternateShell.value + 2), unicode = True)
|
||||
#working directory for session
|
||||
self.workingDir = String(readLen = CallableValue(lambda:self.cbWorkingDir.value + 2), unicode = True)
|
||||
self.workingDir = String(readLen = UInt16Le(lambda:self.cbWorkingDir.value + 2), unicode = True)
|
||||
self.extendedInfo = RDPExtendedInfo(conditional = extendedInfoConditional)
|
||||
|
||||
class RDPExtendedInfo(CompositeType):
|
||||
@@ -375,9 +342,6 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
||||
#True if classic encryption is enable
|
||||
self._enableEncryption = False
|
||||
|
||||
#Enable Secure Mac generation
|
||||
self._enableSecureCheckSum = False
|
||||
|
||||
#initialise decrypt and encrypt keys
|
||||
self._macKey = None
|
||||
self._initialDecrytKey = None
|
||||
@@ -394,11 +358,10 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
||||
self._encryptRc4 = None
|
||||
|
||||
|
||||
def readEncryptedPayload(self, s, saltedMacGeneration):
|
||||
def readEncryptedPayload(self, s):
|
||||
"""
|
||||
@summary: decrypt basic RDP security payload
|
||||
@param s: {Stream} encrypted stream
|
||||
@param saltedMacGeneration: {bool} use salted mac generation
|
||||
@return: {Stream} decrypted
|
||||
"""
|
||||
#if update is needed
|
||||
@@ -409,28 +372,24 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
||||
self._decryptRc4 = rc4.RC4Key(self._currentDecrytKey)
|
||||
self._nbDecryptedPacket = 0
|
||||
|
||||
signature = String(readLen = CallableValue(8))
|
||||
signature = String(readLen = UInt8(8))
|
||||
encryptedPayload = String()
|
||||
s.readType((signature, encryptedPayload))
|
||||
decrypted = rc4.crypt(self._decryptRc4, encryptedPayload.value)
|
||||
|
||||
#ckeck signature
|
||||
if not saltedMacGeneration and macData(self._macKey, decrypted)[:8] != signature.value:
|
||||
raise InvalidExpectedDataException("bad signature")
|
||||
|
||||
if saltedMacGeneration and macSaltedData(self._macKey, decrypted, self._nbDecryptedPacket)[:8] != signature.value:
|
||||
raise InvalidExpectedDataException("bad signature")
|
||||
if macData(self._macKey, decrypted)[:8] != signature.value:
|
||||
raise InvalidExpectedDataException("Bad packet signature")
|
||||
|
||||
#count
|
||||
self._nbDecryptedPacket += 1
|
||||
|
||||
return Stream(decrypted)
|
||||
|
||||
def writeEncryptedPayload(self, data, saltedMacGeneration):
|
||||
def writeEncryptedPayload(self, data):
|
||||
"""
|
||||
@summary: sign and crypt data
|
||||
@param data: {Type} raw stream
|
||||
@param saltedMacGeneration: {bool} use salted mac generation
|
||||
@param s: {Stream} raw stream
|
||||
@return: {Tuple} (signature, encryptedData)
|
||||
"""
|
||||
if self._nbEncryptedPacket == 4096:
|
||||
@@ -441,14 +400,9 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
||||
self._nbEncryptedPacket = 0
|
||||
|
||||
self._nbEncryptedPacket += 1
|
||||
|
||||
s = Stream()
|
||||
s.writeType(data)
|
||||
|
||||
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())))
|
||||
return (String(macData(self._macKey, s.getvalue())[:8]), String(rc4.crypt(self._encryptRc4, s.getvalue())))
|
||||
|
||||
def recv(self, data):
|
||||
"""
|
||||
@@ -465,7 +419,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
||||
data.readType((securityFlag, securityFlagHi))
|
||||
|
||||
if securityFlag.value & SecurityFlag.SEC_ENCRYPT:
|
||||
data = self.readEncryptedPayload(data, securityFlag.value & SecurityFlag.SEC_SECURE_CHECKSUM)
|
||||
data = self.readEncryptedPayload(data)
|
||||
|
||||
self._presentation.recv(data)
|
||||
|
||||
@@ -479,12 +433,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
||||
self._transport.send(data)
|
||||
return
|
||||
|
||||
flag = SecurityFlag.SEC_ENCRYPT
|
||||
|
||||
if self._enableSecureCheckSum:
|
||||
flag |= SecurityFlag.SEC_SECURE_CHECKSUM
|
||||
|
||||
self.sendFlagged(flag, data)
|
||||
self.sendFlagged(SecurityFlag.SEC_ENCRYPT, data)
|
||||
|
||||
def sendFlagged(self, flag, data):
|
||||
"""
|
||||
@@ -495,7 +444,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
||||
@param data: {Type | Tuple}
|
||||
"""
|
||||
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))
|
||||
|
||||
def recvFastPath(self, secFlag, fastPathS):
|
||||
@@ -505,7 +454,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
||||
@param fastPathS: {Stream}
|
||||
"""
|
||||
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)
|
||||
|
||||
@@ -523,11 +472,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
||||
"""
|
||||
if self._enableEncryption:
|
||||
secFlag |= tpkt.SecFlags.FASTPATH_OUTPUT_ENCRYPTED
|
||||
|
||||
if self._enableSecureCheckSum:
|
||||
secFlag |= tpkt.SecFlags.FASTPATH_OUTPUT_SECURE_CHECKSUM
|
||||
|
||||
fastPathS = self.writeEncryptedPayload(fastPathS, self._enableSecureCheckSum)
|
||||
fastPathS = self.writeEncryptedPayload(fastPathS)
|
||||
|
||||
self._fastPathTransport.sendFastPath(secFlag, fastPathS)
|
||||
|
||||
@@ -716,7 +661,7 @@ class Server(SecLayer):
|
||||
raise InvalidExpectedDataException("Waiting info packet")
|
||||
|
||||
if securityFlag.value & SecurityFlag.SEC_ENCRYPT:
|
||||
s = self.readEncryptedPayload(s, securityFlag.value & SecurityFlag.SEC_SECURE_CHECKSUM)
|
||||
s = self.readEncryptedPayload(s)
|
||||
|
||||
s.readType(self._info)
|
||||
#next state send error license
|
||||
|
||||
@@ -204,21 +204,7 @@ class TPKT(RawLayer, IFastPathSender):
|
||||
|
||||
def sendFastPath(self, secFlag, fastPathS):
|
||||
"""
|
||||
@param fastPathS: {Type | Tuple} type transform to stream and send as fastpath
|
||||
@param fastPathS: type transform to stream and send as fastpath
|
||||
@param secFlag: {integer} Security flag for fastpath packet
|
||||
"""
|
||||
RawLayer.send(self, (UInt8(Action.FASTPATH_ACTION_FASTPATH | ((secFlag & 0x3) << 6)), UInt16Be((sizeof(fastPathS) + 3) | 0x8000), fastPathS))
|
||||
|
||||
def startTLS(self, sslContext):
|
||||
"""
|
||||
@summary: start TLS protocol
|
||||
@param sslContext: {ssl.ClientContextFactory | ssl.DefaultOpenSSLContextFactory} context use for TLS protocol
|
||||
"""
|
||||
self.transport.startTLS(sslContext)
|
||||
|
||||
def startNLA(self, sslContext, callback):
|
||||
"""
|
||||
@summary: use to start NLA (NTLM over SSL) protocol
|
||||
must be called after startTLS function
|
||||
"""
|
||||
self.transport.startNLA(sslContext, callback)
|
||||
RawLayer.send(self, (UInt8(Action.FASTPATH_ACTION_FASTPATH | ((secFlag & 0x3) << 6)), UInt16Be((sizeof(fastPathS) + 3) | 0x8000), fastPathS))
|
||||
@@ -50,7 +50,6 @@ class NegociationType(object):
|
||||
class Protocols(object):
|
||||
"""
|
||||
@summary: Protocols available for x224 layer
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc240500.aspx
|
||||
"""
|
||||
PROTOCOL_RDP = 0x00000000
|
||||
PROTOCOL_SSL = 0x00000001
|
||||
@@ -133,7 +132,7 @@ class X224Layer(LayerAutomata, IStreamSender):
|
||||
"""
|
||||
LayerAutomata.__init__(self, presentation)
|
||||
#client requested selectedProtocol
|
||||
self._requestedProtocol = Protocols.PROTOCOL_SSL | Protocols.PROTOCOL_HYBRID
|
||||
self._requestedProtocol = Protocols.PROTOCOL_SSL
|
||||
#server selected selectedProtocol
|
||||
self._selectedProtocol = Protocols.PROTOCOL_SSL
|
||||
|
||||
@@ -205,32 +204,19 @@ class Client(X224Layer):
|
||||
self._selectedProtocol = Protocols.PROTOCOL_RDP
|
||||
|
||||
#NLA protocol doesn't support in actual version of RDPY
|
||||
if self._selectedProtocol in [ Protocols.PROTOCOL_HYBRID_EX ]:
|
||||
raise InvalidExpectedDataException("RDPY doesn't support PROTOCOL_HYBRID_EX security Layer")
|
||||
if self._selectedProtocol in [ Protocols.PROTOCOL_HYBRID, Protocols.PROTOCOL_HYBRID_EX ]:
|
||||
raise InvalidExpectedDataException("RDPY doesn't support NLA security Layer")
|
||||
|
||||
if self._selectedProtocol == Protocols.PROTOCOL_SSL:
|
||||
log.debug("*" * 10 + " select SSL layer " + "*" * 10)
|
||||
#_transport is TPKT and transport is TCP layer of twisted
|
||||
self._transport.transport.startTLS(ClientTLSContext())
|
||||
|
||||
#now i'm ready to receive data
|
||||
self.setNextState(self.recvData)
|
||||
|
||||
if self._selectedProtocol == Protocols.PROTOCOL_RDP:
|
||||
log.warning("*" * 43)
|
||||
log.warning("*" + " " * 10 + "RDP Security selected" + " " * 10 + "*")
|
||||
log.warning("*" * 43)
|
||||
#connection is done send to presentation
|
||||
self._presentation.connect()
|
||||
|
||||
elif self._selectedProtocol == Protocols.PROTOCOL_SSL:
|
||||
log.info("*" * 43)
|
||||
log.info("*" + " " * 10 + "SSL Security selected" + " " * 10 + "*")
|
||||
log.info("*" * 43)
|
||||
self._transport.startTLS(ClientTLSContext())
|
||||
#connection is done send to presentation
|
||||
self._presentation.connect()
|
||||
|
||||
elif self._selectedProtocol == Protocols.PROTOCOL_HYBRID:
|
||||
log.info("*" * 43)
|
||||
log.info("*" + " " * 10 + "NLA Security selected" + " " * 10 + "*")
|
||||
log.info("*" * 43)
|
||||
self._transport.startNLA(ClientTLSContext(), lambda:self._presentation.connect())
|
||||
#connection is done send to presentation
|
||||
self._presentation.connect()
|
||||
|
||||
class Server(X224Layer):
|
||||
"""
|
||||
@@ -303,7 +289,7 @@ class Server(X224Layer):
|
||||
if self._selectedProtocol == Protocols.PROTOCOL_SSL:
|
||||
log.debug("*" * 10 + " select SSL layer " + "*" * 10)
|
||||
#_transport is TPKT and transport is TCP layer of twisted
|
||||
self._transport.startTLS(ServerTLSContext(self._serverPrivateKeyFileName, self._serverCertificateFileName))
|
||||
self._transport.transport.startTLS(ServerTLSContext(self._serverPrivateKeyFileName, self._serverCertificateFileName))
|
||||
|
||||
#connection is done send to presentation
|
||||
self.setNextState(self.recvData)
|
||||
|
||||
@@ -148,10 +148,13 @@ def extractRSAKey(certificate):
|
||||
"""
|
||||
#http://www.alvestrand.no/objectid/1.2.840.113549.1.1.1.html
|
||||
|
||||
binaryTuple = certificate.getComponentByName('tbsCertificate').getComponentByName('subjectPublicKeyInfo').getComponentByName('subjectPublicKey')
|
||||
l = int("".join([str(i) for i in binaryTuple]), 2)
|
||||
return extractRSAKeyFromASN1(hex(l)[2:-1].decode('hex'))
|
||||
#extract binary data
|
||||
l = 0L
|
||||
for b in certificate.getComponentByName('tbsCertificate').getComponentByName('subjectPublicKeyInfo').getComponentByName('subjectPublicKey'):
|
||||
l = (l << 1) | b
|
||||
|
||||
rsaKey = decoder.decode(hex(l)[2:-1].decode('hex'), asn1Spec=RSAPublicKey())[0]
|
||||
return rsaKey.getComponentByName('modulus')._value , rsaKey.getComponentByName('publicExponent')._value
|
||||
|
||||
def extractRSAKeyFromASN1(subjectPublicKey):
|
||||
rsaKey = decoder.decode(subjectPublicKey, asn1Spec=RSAPublicKey())[0]
|
||||
return rsaKey.getComponentByName('modulus')._value , rsaKey.getComponentByName('publicExponent')._value
|
||||
|
||||
|
||||
16
setup.py
16
setup.py
@@ -4,12 +4,20 @@ import setuptools
|
||||
from distutils.core import setup, Extension
|
||||
|
||||
setup(name='rdpy',
|
||||
version='1.3.1',
|
||||
version='1.2.1',
|
||||
description='Remote Desktop Protocol in Python',
|
||||
long_description="""
|
||||
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 provide RDP and VNC binaries : RDP Man In The Middle proxy which record session, RDP Honeypot, RDP screenshoter, RDP client, VNC client, VNC screenshoter, RSS Player
|
||||
RDPY provide RDP and VNC binaries :
|
||||
\t-RDP Man In The Middle proxy which record session
|
||||
\t-RDP Honeypot
|
||||
\t-RDP screenshoter
|
||||
\t-RDP client
|
||||
\t-VNC client
|
||||
\t-VNC screenshoter
|
||||
\t-RSS Player
|
||||
""",
|
||||
author='Sylvain Peyrefitte',
|
||||
author_email='citronneur@gmail.com',
|
||||
@@ -21,8 +29,6 @@ setup(name='rdpy',
|
||||
'rdpy.protocol',
|
||||
'rdpy.protocol.rdp',
|
||||
'rdpy.protocol.rdp.pdu',
|
||||
'rdpy.protocol.rdp.nla',
|
||||
'rdpy.protocol.rdp.t125',
|
||||
'rdpy.protocol.rfb',
|
||||
'rdpy.ui'
|
||||
],
|
||||
|
||||
@@ -26,7 +26,7 @@ import os, sys
|
||||
sys.path.insert(1, os.path.join(sys.path[0], '..'))
|
||||
|
||||
import unittest
|
||||
import rdpy.protocol.rdp.t125.ber as ber
|
||||
import rdpy.protocol.rdp.ber as ber
|
||||
import rdpy.core.type as type
|
||||
import rdpy.core.error as error
|
||||
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
unit test for rdpy.protocol.rdp.nla.cssp and ntlm module
|
||||
"""
|
||||
import unittest
|
||||
import os, sys
|
||||
# Change path so we find rdpy
|
||||
sys.path.insert(1, os.path.join(sys.path[0], '..'))
|
||||
|
||||
from rdpy.protocol.rdp.nla import cssp, ntlm
|
||||
from rdpy.security import rc4
|
||||
|
||||
pubKeyHex = """
|
||||
MIGJAoGBAJ6VtUEDxTPqKWUrZe8wcd1zuzA77Mpyz73g+C
|
||||
H/ppd2oQi10saVgdK6cRBKrCU0N6DD\nV/DqH4yE63vmbF
|
||||
AmH7dBCljTgIc9C0HZvFQ6D3cUefW5pDjrEwg1rr+zF1ri
|
||||
WIk5xCJ/FleQCK+R\nO5XIU9DAjhmK8xC8yMdC+xLeLV6D
|
||||
AgMBAAE=
|
||||
"""
|
||||
peer0_0 = """
|
||||
MC+gAwIBAqEoMCYwJKAiBCBOVExNU1NQAAEAAAA1gghgAA
|
||||
AAAAAAAAAAAAAAAAAAAA==
|
||||
"""
|
||||
peer1_0 = """
|
||||
MIIBCaADAgECoYIBADCB/TCB+qCB9wSB9E5UTE1TU1AAAg
|
||||
AAAA4ADgA4AAAANYKJYnPHQ6nn/Lv8\nAAAAAAAAAACuAK
|
||||
4ARgAAAAYBsR0AAAAPUwBJAFIAQQBEAEUATAACAA4AUwBJ
|
||||
AFIAQQBEAEUATAAB\nABYAVwBBAFYALQBHAEwAVwAtADAA
|
||||
MAA5AAQAGgBTAGkAcgBhAGQAZQBsAC4AbABvAGMAYQBsAA
|
||||
MA\nMgB3AGEAdgAtAGcAbAB3AC0AMAAwADkALgBTAGkAcg
|
||||
BhAGQAZQBsAC4AbABvAGMAYQBsAAUAGgBT\nAGkAcgBhAG
|
||||
QAZQBsAC4AbABvAGMAYQBsAAcACABWkzQyx1XQAQAAAAA=
|
||||
"""
|
||||
peer0_1 = """
|
||||
MIICD6ADAgECoYIBYjCCAV4wggFaoIIBVgSCAVJOVExNU1
|
||||
NQAAMAAAAYABgAUAAAANoA2gBoAAAA\nCAAIAEAAAAAIAA
|
||||
gASAAAAAAAAABQAAAAEAAQAEIBAAA1gghgYwBvAGMAbwB0
|
||||
AG8AdABvABqKwrxk\n2sAom6gUCFFt1rgpCdKZGTNwnlGg
|
||||
bsU5R/OelmrD/LLrx+ABAQAAAAAAAABFCDLHVdABKQnSmR
|
||||
kz\ncJ4AAAAAAgAOAFMASQBSAEEARABFAEwAAQAWAFcAQQ
|
||||
BWAC0ARwBMAFcALQAwADAAOQAEABoAUwBp\nAHIAYQBkAG
|
||||
UAbAAuAGwAbwBjAGEAbAADADIAdwBhAHYALQBnAGwAdwAt
|
||||
ADAAMAA5AC4AUwBpAHIA\nYQBkAGUAbAAuAGwAbwBjAGEA
|
||||
bAAFABoAUwBpAHIAYQBkAGUAbAAuAGwAbwBjAGEAbAAHAA
|
||||
gAVpM0\nMsdV0AEAAAAAv+z19mkBOu9b0Kv+P991MKOCAK
|
||||
AEggCcAQAAAMQOzZaPZ8DdAAAAAM+IvTDiU0pL\njUnU6a
|
||||
NjH+gZWeaIlqpQNYECmpElixwPj8aRRFVfTtkbw66U3gmo
|
||||
3YBkUoVK8tfHESkivuWtV2tP\n3KGuAFv/6GzbFYQYlA7r
|
||||
zZ1Bw072ps8s9cWeoNmAX6oiZmFW3j7LX3xkr7+nJoOoXI
|
||||
jzvorm5kz3\nldCo8Iwh+IZ3SSnj0/h4H1GR
|
||||
"""
|
||||
|
||||
class TestCsspNtlm(unittest.TestCase):
|
||||
"""
|
||||
@summary: test generate ntlmv2 over cssp authentication protocol
|
||||
"""
|
||||
def testCSSPNTLMAuthentication(self):
|
||||
negotiate_data_request = cssp.decodeDERTRequest(peer0_0.decode('base64'))
|
||||
challenge_data_request = cssp.decodeDERTRequest(peer1_0.decode('base64'))
|
||||
authenticate_data_request = cssp.decodeDERTRequest(peer0_1.decode('base64'))
|
||||
|
||||
negotiate_data = cssp.getNegoTokens(negotiate_data_request)[0]
|
||||
challenge_data = cssp.getNegoTokens(challenge_data_request)[0]
|
||||
authenticate_data = cssp.getNegoTokens(authenticate_data_request)[0]
|
||||
|
||||
negotiate = ntlm.NegotiateMessage()
|
||||
negotiate_data.readType(negotiate)
|
||||
|
||||
challenge = ntlm.ChallengeMessage()
|
||||
challenge_data.readType(challenge)
|
||||
|
||||
ServerChallenge = challenge.ServerChallenge.value
|
||||
ServerName = challenge.getTargetInfo()
|
||||
|
||||
authenticate = ntlm.AuthenticateMessage()
|
||||
authenticate_data.readType(authenticate)
|
||||
|
||||
NtChallengeResponseTemp = authenticate.getNtChallengeResponse()
|
||||
NTProofStr = NtChallengeResponseTemp[:16]
|
||||
temp = NtChallengeResponseTemp[16:]
|
||||
Timestamp = temp[8:16]
|
||||
ClientChallenge = temp[16:24]
|
||||
|
||||
EncryptedRandomSessionKey = authenticate.getEncryptedRandomSession()
|
||||
domain = "coco"
|
||||
user = "toto"
|
||||
password = "lolo"
|
||||
|
||||
ResponseKeyNT = ntlm.NTOWFv2(password, user, domain)
|
||||
ResponseKeyLM = ntlm.LMOWFv2(password, user, domain)
|
||||
NtChallengeResponse, LmChallengeResponse, SessionBaseKey = ntlm.ComputeResponsev2(ResponseKeyNT, ResponseKeyLM, ServerChallenge, ClientChallenge, Timestamp, ServerName)
|
||||
KeyExchangeKey = ntlm.KXKEYv2(SessionBaseKey, LmChallengeResponse, ServerChallenge)
|
||||
ExportedSessionKey = ntlm.RC4K(KeyExchangeKey, EncryptedRandomSessionKey)
|
||||
|
||||
domain, user = domain, user
|
||||
if challenge.NegotiateFlags.value & ntlm.Negotiate.NTLMSSP_NEGOTIATE_UNICODE:
|
||||
domain, user = ntlm.UNICODE(domain), ntlm.UNICODE(user)
|
||||
|
||||
ClientSigningKey = ntlm.SIGNKEY(ExportedSessionKey, True)
|
||||
ServerSigningKey = ntlm.SIGNKEY(ExportedSessionKey, False)
|
||||
ClientSealingKey = ntlm.SEALKEY(ExportedSessionKey, True)
|
||||
ServerSealingKey = ntlm.SEALKEY(ExportedSessionKey, False)
|
||||
|
||||
interface = ntlm.NTLMv2SecurityInterface(rc4.RC4Key(ClientSealingKey), rc4.RC4Key(ServerSealingKey), ClientSigningKey, ServerSigningKey)
|
||||
|
||||
EncryptedPubKeySrc = cssp.getPubKeyAuth(authenticate_data_request)
|
||||
EncryptedPubKeyDst = interface.GSS_WrapEx(pubKeyHex.decode('base64'))
|
||||
|
||||
self.assertTrue(EncryptedPubKeySrc == EncryptedPubKeyDst, "Public key must be equals")
|
||||
|
||||
@@ -109,17 +109,6 @@ class TestLic(unittest.TestCase):
|
||||
s.pos = 0
|
||||
s.readType(lic.LicPacket(lic.ClientNewLicenseRequest()))
|
||||
self._state = True
|
||||
def getGCCServerSettings(self):
|
||||
class A:
|
||||
def __init__(self):
|
||||
self._is_readed = False
|
||||
class B:
|
||||
def __init__(self):
|
||||
self.serverCertificate = A()
|
||||
class C:
|
||||
def __init__(self):
|
||||
self.SC_SECURITY = B()
|
||||
return C()
|
||||
|
||||
t = Transport()
|
||||
l = lic.LicenseManager(t)
|
||||
|
||||
@@ -26,7 +26,7 @@ import os, sys
|
||||
sys.path.insert(1, os.path.join(sys.path[0], '..'))
|
||||
|
||||
import unittest
|
||||
import rdpy.protocol.rdp.t125.per as per
|
||||
import rdpy.protocol.rdp.per as per
|
||||
import rdpy.core.type as type
|
||||
import rdpy.core.error as error
|
||||
|
||||
|
||||
@@ -106,6 +106,19 @@ class X224Test(unittest.TestCase):
|
||||
layer.connect()
|
||||
|
||||
self.assertRaises(X224Test.X224_PASS, layer.recv, type.String('\x01\x02'))
|
||||
|
||||
def test_x224_client_recvConnectionConfirm_negotiation_bad_protocol(self):
|
||||
"""
|
||||
@summary: unit test for X224Client.recvConnectionConfirm and sendConnectionRequest function
|
||||
Server ask another protocol than SSL or RDP
|
||||
"""
|
||||
message = x224.ServerConnectionConfirm()
|
||||
message.protocolNeg.selectedProtocol.value = x224.Protocols.PROTOCOL_HYBRID
|
||||
s = type.Stream()
|
||||
s.writeType(message)
|
||||
s.pos = 0
|
||||
layer = x224.Client(None)
|
||||
self.assertRaises(error.InvalidExpectedDataException, layer.recvConnectionConfirm, s)
|
||||
|
||||
def test_x224_client_recvConnectionConfirm_negotiation_failure(self):
|
||||
"""
|
||||
@@ -128,10 +141,12 @@ class X224Test(unittest.TestCase):
|
||||
tls_begin = False
|
||||
presentation_connect = False
|
||||
class Transport(object):
|
||||
|
||||
def startTLS(self, context):
|
||||
global tls_begin
|
||||
tls_begin = True
|
||||
def __init__(self):
|
||||
class TLSTransport(object):
|
||||
def startTLS(self, context):
|
||||
global tls_begin
|
||||
tls_begin = True
|
||||
self.transport = TLSTransport()
|
||||
|
||||
class Presentation(object):
|
||||
def connect(self):
|
||||
@@ -199,9 +214,12 @@ class X224Test(unittest.TestCase):
|
||||
x224.ServerTLSContext = ServerTLSContext
|
||||
|
||||
class Transport(object):
|
||||
def startTLS(self, context):
|
||||
global tls
|
||||
tls = True
|
||||
def __init__(self):
|
||||
class TLS(object):
|
||||
def startTLS(self, context):
|
||||
global tls
|
||||
tls = True
|
||||
self.transport = TLS()
|
||||
|
||||
def send(self, data):
|
||||
if not isinstance(data, x224.ServerConnectionConfirm):
|
||||
|
||||
Reference in New Issue
Block a user