Skip to content

Conversation

@azoxlpf
Copy link

@azoxlpf azoxlpf commented Jan 2, 2026

Hello !

I've been doing R&D on RDP relaying for a few weeks and have implemented an RDP relay server for ntlmrelayx. This will be useful in scenarios where an administrator sees an unusual machine with the RDP port open and tries to connect to verify what it is, or other scenarios I haven’t thought of yet.

What works

  • RDP → SMB: Successfully relays RDP authentication to SMB targets (when SMB signing is disabled)
  • RDP → HTTP: Successfully relays RDP authentication to HTTP targets (useful for ADCS attacks)

What doesn't work

  • RDP → LDAP: Not possible because the RDP client always requests signing in the NTLMSSP NEGOTIATE message.

  • SMB/HTTP → RDP (client): Not possible due to CredSSP's anti-relay protection. The CredSSP protocol includes a pubKeyAuth message exchange that acts as a proof-of-possession mechanism. During this exchange :

    • The RDP server sends its TLS public key to the client
    • The client must encrypt this public key using the NTLM session key (derived from the NT hash of the user's password)
    • The server decrypts the data using the same session key and verifies the public key matches

    In a relay scenario, we only have the NTLM messages (NEGOTIATE, CHALLENGE, AUTHENTICATE) but not the actual password or NT hash. The NTLM session key is computed from the NT hash, so without it, we cannot encrypt the pubKeyAuth response correctly. The server will reject the authentication. This is an intentional security feature of CredSSP designed to prevent relay attacks.

Screenshots

Authentication of an admin on the NTLMRelayx RDP server :

1

Relay of NTLM authentication to SMB on a server where SMB signing is not required :

2

Or alternatively, relay to ESC8 :

3

@NeffIsBack
Copy link
Contributor

Very nice work!

@azoxlpf azoxlpf changed the title Implements RDP Server Implements RDP Relay Server Jan 4, 2026
@gabrielg5 gabrielg5 added the in review This issue or pull request is being analyzed label Jan 6, 2026
@gabrielg5 gabrielg5 self-assigned this Jan 6, 2026
@Ko0ler
Copy link

Ko0ler commented Jan 7, 2026

GG

@AndreySolod
Copy link

I found small bug in RDP relay server:
If you try to connect to the same RDP server from the same dialog box a second time, the connection will fail because the client will send an RDP Cookie:
screenshot_1

Some simple fix this issue:
screenshot_2

Also, to relay to LDAP with --remove-mic flag you need a #2089 (in ldaprelayclient.py that removed SEAL flag).

@azoxlpf
Copy link
Author

azoxlpf commented Jan 10, 2026

I found small bug in RDP relay server: If you try to connect to the same RDP server from the same dialog box a second time, the connection will fail because the client will send an RDP Cookie: screenshot_1

Some simple fix this issue: screenshot_2

Also, to relay to LDAP with --remove-mic flag you need a #2089 (in ldaprelayclient.py that removed SEAL flag).

Thanks a lot for reporting this and for the PoC, it was very helpful !

The issue with the RDP cookie on the second connection from the same dialog is now fixed in my patch. For the --remove-mic LDAP relay part, I’ll wait for your PR to be merged rather than duplicating the SEAL-removal logic on my side.

Copy link
Collaborator

@gabrielg5 gabrielg5 left a comment

Choose a reason for hiding this comment

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

Hey hello @azoxlpf!
Thank you for this PR!! Adding one more guest to the Relay party :D that's great!

I'll be checking functionality, probably submitting a new comment later today with my outcome

Added some tiny details in the review pane regarding logging to keep it clean.
Also, think that would be better align the RDPRelayServer with the other relays (check the HTTP, ie). I mean, having a similar class structure like:

  • RDPRelayServer
    • class RDPServer
    • class RDPHandler
    • def init
    • def run

for better code / feature alignment across all the relay servers

  • ipv6 support
  • allow reusing address

Let me know your insights and if need any help

Enhancements ideas while checking the code:

  • Adding a parameter to specify the certificate? (use that one instead than the self signed autogenerated if set...)

Thanks @AndreySolod also for your reply here! A couple of doubts on that last changeset

  • Why are we managing the active clients like that? closing socket if receiving another req in less than 2 secs
  • The cr_tpdu['Type'] == 67 is matching the letter "C" (in "Cookie") as if CR_TPDU was not parsing right message containing cookie... perhaps just the second part of the condition is enough. Will take a more detailed look to this later on as well but wanted to share a first comment on this

Thank you!

@gabrielg5 gabrielg5 added the waiting for response Further information is needed from people who opened the issue or pull request label Jan 19, 2026
@azoxlpf
Copy link
Author

azoxlpf commented Jan 20, 2026

@gabrielg5 Thanks for the review !

I just pushed the requested changes:

  • Refactored the class structure to align with other relays (RDPServer, RDPHandler, run, init).

  • Added IPv6 support.

  • Enabled address reusing.

  • Fixed the cookie parsing logic as suggested.

Regarding the certificate, I'm not sure this is strictly necessary. The current auto-generated self-signed certificate works perfectly for our goal: decrypting the TLS handshake to extract NTLM messages (since we hold the private key). Adding an option for a custom certificate might just add complexity to the code without providing a significant benefit for this specific attack vector.

As for changes to how active clients are managed, this is because an RDP connection triggers two distinct authentication flows back-to-back (as shown in the screenshot). If we relay both, it duplicates the attack actions (e.g., dumping the SAM on an SMB target twice), which is bad for OPSEC. Closing the second connection prevents this.

image

Idea: I was actually thinking we could potentially exploit this to relay the second authentication to a second target (getting 2 relays from 1 connection). However, I'm unsure if I should implement that logic here in the RDP server or if it requires modifying ntlmrelayx core to handle multi-target dispatching.

Let me know your thoughts !

@gabrielg5 gabrielg5 removed the waiting for response Further information is needed from people who opened the issue or pull request label Jan 21, 2026
@gabrielg5
Copy link
Collaborator

Been talking that with other teammates as well
Think that what I'd do is treat the second connection as a normal one; not doing anything different for it. There are flags in the example that operators can set/unset to define behavior

If you have multiple targets, the second connection will be relayed to the next target in the list.
If you have a single target, it won't be relayed as "... no more targets left..."
If you wanna relay every received connection (despite already successfully relayed to a given target), can set the --keep-relaying flag

Does that make sense?

@AndreySolod
Copy link

AndreySolod commented Jan 21, 2026

A little bit about certificates: when a client uses NLA, he does NOT verify the validity (trust) of the certificate, he just uses the certificate in order to encrypt his data. This behavior differs from, for example, LDAPS, because in this case the client simply will not connect to the host with a certificate signed by an untrusted authentication authority.
When the client does NOT use NLA, it verifies the trust in the server's certificate, because in this case it connects using a login/password in clear text, which, for example, is well shown in the Seth tool.
When a client has connected using NLA, it can be forced to connect without NLA (as described in the article, due to an undocumented message). I was trying to modify the server in order to additionally try to get the credentials in the clear (after relaying them), but for now I could not fully understand how RDP would work in this case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in review This issue or pull request is being analyzed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants