Skip to content
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

WIP: Fake RDP server when NLA is enforced #426

Open
wants to merge 22 commits into
base: main
Choose a base branch
from

Conversation

spameier
Copy link
Contributor

@spameier spameier commented Dec 13, 2022

As part of a project at school, I extended the existing redirection functionality with a locally hosted RDP server. It works as follows:

  1. A local RDP server is with a fake login screen and no authentication is launched (A Tkinter GUI with freerdp-shadow-cli inside Xephyr).
  2. Once a user tries to connect to a NLA enforcing RDP server, the traffic is redirected to the fake login screen.
  3. Once the user enters credentials, a rdp client (xfreerdp) is launched, replacing the fake login screen.
  4. The user now sees the remote server through fake RDP server.

So far it's work in progress and has a lot of rough edges, but feedback would be very much appreciated.

Here's a quick demo:

@spameier spameier force-pushed the fake-server branch 2 times, most recently from 9f54176 to ecd5d15 Compare December 14, 2022 14:43
Copy link
Collaborator

@obilodeau obilodeau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very interesting and in a direction I had never thought of. Great work! I would love to see it in action if you can provide a recorded session.

Missing:

  • Impact on dependencies outside of python (X11, TK, Fonts, freerdp-shadow-cli, xfreerdp)
  • Documentation
  • Handle TODOs and FIXMEs

Comment on lines 223 to 238
if self.state.isRedirected():
self.log.info(
"Fetching certificate of the original host %(host)s:%(port)d because of NLA redirection",
{
"host": self.state.config.targetHost,
"port": self.state.config.targetPort,
},
)
pem = ssl.get_server_certificate(
(self.state.config.targetHost, self.state.config.targetPort)
)
cert = crypto.load_certificate(crypto.FILETYPE_PEM, pem)
else:
cert = self.server.tcp.transport.getPeerCertificate()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are issues with this code that are being looked at in #424

pyrdp/mitm/FakeServer.py Outdated Show resolved Hide resolved
pyrdp/mitm/FakeServer.py Outdated Show resolved Hide resolved
self.entry_password = Entry(
self.root,
show="•",
font=("Segoe UI", 20),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this add special dependencies?

Copy link
Contributor Author

@spameier spameier Dec 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! I'm not sure honestly, it's certainly not on my Kali VM and I never exactly looked at it. Also I'm not a fonts expert 😄.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm switching to DejaVu Sans but that's not available on Windows I think. I also didn't find a way to only specify a font family (e. g. Sans Serif)..

pyrdp/mitm/FakeServer.py Outdated Show resolved Hide resolved
@obilodeau obilodeau added this to the v1.3.0 milestone Dec 16, 2022
@spameier
Copy link
Contributor Author

spameier commented Dec 17, 2022

I would love to see it in action if you can provide a recorded session.

Here's a quick demo:

@obilodeau
Copy link
Collaborator

Thanks for the videos. It is a very interesting approach!

There are some things that I think would need to change. Among them, the clearest is the GUI popping up during the interception. I think the fake server should be headless with the option of using the pyrdp-player to listen to the connection just like with pyrdp when it is operating regularly. The reasoning is that a common attack scenario is to run this in a cloud instance or a Raspberry Pi and intercept many connections.

There are also CI build errors:
image

Not sure exactly how without checking but logging is pretty standard throughout pyrdp. It is either a singleton that can be pulled from everywhere or you can fetch it from an object easy to access. I don't think it is stored in the state.

@spameier
Copy link
Contributor Author

Thanks for your feedback!

I think the fake server should be headless with the option of using the pyrdp-player to listen to the connection just like with pyrdp when it is operating regularly.

Yes, of course. Using Xephyr with the GUI is useful while testing and for demonstration purposes. Luckily pyvirtualdisplay already supports the headless Xvfb as a backend. I don't think it will give much trouble to change that and will look into it in the future.

The CI is currently failing because I added the getLog callback to RDPMITMState. I think that should be solved differently anyway.

@spameier

This comment was marked as outdated.

otherwise checks whether fake server was configured or not fail
This reduces the error message an mstsc client sees from two to one.

before:
- The server name on the certificate is incorrect
- The certificate is not from a trusted certifying authority

after:
- The certificate is not from a trusted certifying authority
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants