-
Notifications
You must be signed in to change notification settings - Fork 248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
nla redirection: use certificate of original server #424
base: main
Are you sure you want to change the base?
Conversation
At first, this looks good. I'm wondering about certificate caching though, I take a deeper look later. Scheduling for our next release. |
The patch crashes for me:
However if I connect directly to the non-NLA host it works:
|
Spent the last 3 hours on this and couldn't manage a fix. This is happening deep in Twisted land. Something from the X224MITM layer side must notify the RDPMITM side before we kill the original connection and we go grab the cert. However, this doesn't work synchronously and I couldn't manage to introduce a new async state that wouldn't continue the X224MITM code and thus kill the connection and start a new one. I should probably refactor and design/document a state machine. I'm not going to do this now. I want to release next week so I'll unfortunately defer this patch unless you manage to fix it. |
I played with this a little bit more this morning and I don't understand how could the synchronous code you proposed worked. The high-level This simple test: import ssl
hostname = "127.0.0.1"
port = 13389
pem = ssl.get_server_certificate((hostname, port)) Will yield a:
And at first I thought it was due to automatic cert validation but then I modified it to: import socket
import ssl
hostname = "127.0.0.1"
port = 13389
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.verify_mode = ssl.CERT_NONE
context.check_hostname = False
with socket.create_connection((hostname, port)) as sock:
with context.wrap_socket(sock) as sslsock:
der_cert = sslsock.getpeercert(True)
# from binary DER format to PEM
pem_cert = ssl.DER_cert_to_PEM_cert(der_cert)
print(pem_cert) and still get the same exception. However, an If your initial patch worked in your setup, I wonder what could be different?... |
That's really weird, in my setup your code snippet to retrieve the PEM works without issues:
The code works on my machine(:tm:) (as seen in the line
Also, while testing I had no problems at all. In fact, the reason for using the synchronous My virtual setup is as follows:
The clients (Win 10 and Kali) and the servers are in separate IPv4 subnets (servers: 192.168.251.0/24, clients: 192.168.254.0/24). The Windows 10 client is then ARP spoofed by Kali using bettercap. |
I just doublechecked and still get the EOF problem. What is your OpenSSL version? I believe python's ssl module is influenced by it. Here is mine:
|
I have the same version:
I looked up a NLA enforcing RDP server with a hopefully static IP on shodan. Can you also check against that? I have the following output:
|
I tried the same commands on an older CentOS system:
Edit: it also works on segfault.net's disposable root servers:
|
Yes, it seems to work but why wouldn't it work on a Windows 10 environment when pyrdp/xfreerdp/mstsc.exe does? It is not reliable enough to be integrated. Here are results from two connection attempts on the same machine from the same terminal just a couple of seconds apart: Python ssl:
xfreerdp:
|
Just for clarification and so I can replicate your setup: what are you running on port 13389? I mean is it Windows listening on that port or some kind of port forwarding? |
It's a VirtualBox host port forward to 3389 on the guest: Ok, I was mistaken about Windows 10. It works on Windows 10. It's on Windows 11 that it doesn't work. |
9e9449c
to
e44fcc1
Compare
I'm sorry but I cannot reproduce your error message (
|
Connecting to a Windows 10 server with the longer form of the test code (available here) it works:
Connecting to Windows 11 stops working:
I'm not sure what changed with Windows 11 but clearly the default way of fetching the certificate no longer works with it. However, since PyRDP can intercept Windows 11 connections, I started digging into how we do connect and found out we are using OpenSSL's API instead of Python's ssl module. I adapted the example code from above and now it works! import socket
import OpenSSL
from OpenSSL import SSL
hostname = "127.0.0.1"
port = 13389
# taken from: https://gist.github.com/shanemhansen/3853468
def verify_cb(conn, cert, errnum, depth, ok):
# This obviously has to be updated
print('Got certificate: %s' % cert.get_subject())
print(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert).decode('ascii'))
#don't ever do this in production.
#this force verifies all certs.
return 1
# Taken from: pyrdp/core/ssl.py's ClientTLSContext
context = SSL.Context(SSL.SSLv23_METHOD)
context.set_options(SSL.OP_DONT_INSERT_EMPTY_FRAGMENTS)
context.set_options(SSL.OP_TLS_BLOCK_PADDING_BUG)
context.set_options(SSL.OP_NO_TLSv1_3)
# install callback
context.set_verify(SSL.VERIFY_NONE, verify_cb)
# Set up client
sock = SSL.Connection(context, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
sock.connect((hostname, port))
while True:
try:
buf = sock.recv(4096)
except SSL.SysCallError:
break
if not buf:
break Here's the output:
If your patch could be adapted to use |
e44fcc1
to
57f76c3
Compare
I tried to implement it with the handshake done in OpenSSL and it works for me. Can you please test? |
Hi @obilodeau, what do you think about the latest change? |
fixes #423