The Remote Desktop Protocol (RDP) is an increasing concern in cybersecurity. Ransomware groups are using it as a weak point to attack both the public and private sectors, generating losses of $7.5 billion in 2019. Between the first and fourth quarters of 2020, RDP attacks grew 768%. Entities like the Cybersecurity & Infrastructure Security Agency identified RDP as a focal point to protect in their 2020’s Ransomware Guide. And on the other side of the fence, penetration testing teams are using RDP regularly as an effective tool to carry out lateral movements inside the network, hijack sessions and capture hashes, etc.
The GoSecure Titan Labs team saw an opportunity to further explore the topic of hash capturing (which is a must in the arsenal of any offensive team). This blog will examine RDP security modes, how they work and how to put that into action to capture NetNTLMv2 hashes via the RDP protocol using PyRDP—a library created by GoSecure. This effort started with a project during the annual month-long Hacktoberfest which led to several improvements to PyRDP. Among those improvements, we made it easier for the user to capture NetNTLMv2 hashes.
Getting Started
We are going to cover a technique that is employed in offensive use cases to gain access to remote RDP machines by capturing NetNTLMv2 hashes during NTLMSSP authentication. To put this into practice, we will utilize PyRDP to carry out the capture of these hashes in two main authentication scenarios supported by RDP: with Network Level Authentication (NLA) enabled and without NLA. To understand more about these two scenarios, let’s begin by detailing the security modes available in the RDP protocol.
RDP Security Modes
RDP is a protocol commonly used for remotely administering a computer by utilizing terminal clients and servers over TCP/IP. Currently, it provides different types of security modes to encrypt communication between clients and the server:
- RDP Standard Security: all traffic is encrypted symmetrically depending on client’s support and server choice for encryption level (low, client-compatible, high, FIPS). This method is the least secure in case of a ‘monster-in-the-middle’ attack as the server controls the encryption method and can decide if encryption is not needed, thus disabling it completely.
- RDP Enhanced Security: implements external protocols to supply encryption and security mechanisms. Two of these mechanisms are: TLS (Transport Layer Security) since RDP 5.2 and CredSSP, which enables NLA (Network Level Authentication) in addition to the use of TLS, since RDP 6. RDP with TLS also warns the user that a monster-in-the-middle attack might occur if the server’s certificate is self-signed or untrusted, but it does not stop the client from accepting the risk.
When RDP uses Enhanced Security with NLA, the protocol employed to delegate a suitable method of authentication is the Credential Security Support Provider (CredSSP). These delegated methods of authentication are Kerberos or NTLMSSP, the latter being the authentication method used in order to capture NetNTLMv2 hashes which are used in challenge/authentication messages between client and server.
Dissecting authentication via Network Level Authentication
In order to understand NetNTLMv2 hash capture, this section will describe authentication via NLA and its structure in detail. To simplify this process, we are going to separate it into four stages:
- The TLS connection is established between the RDP client and server.
- CredSSP’s SPNEGO is performed for client and server to decide which mutual authentication protocol is going to be used: Kerberos or NTLMSSP.
- In this step the server’s public key is sent for validation and probes its authenticity. This is done in an attempt to avoid monster-in-the-middle (MITM) attacks.
- Once the connection is protected via TLS and SPNEGO, the client sends its credentials for authentication.
Our attention is going to be focused on this last step because it is where a hashed version of the credentials, in the case of using NTLMSSP protocol, are going to be sent by the client in the form of NTLM messages and will be intercepted. These messages are ASN.1 encoded TSRequest structures with authentication data sent in this order:
First, a NEGOTIATION message is sent from the client to start the NTLM authentication which is answered with a CHALLENGE message from the server carrying a “challenge”. Challenges, in this stage, are 64-bit values containing a nonce (a random sequence of bytes used to prevent replay attacks). Then, the client responds to the challenge with an AUTHENTICATION message which holds the credentials needed in order to complete the authentication process. This is the stage we want to reach to extract the challenge response from the client, in the form of a NTLMv2_RESPONSE structure, to relay or crack it.
The NTLMv2_RESPONSE structure is easy to understand and parse as it only contains a 16 bytes response (proof) and client challenge of variable size. The client’s challenge can be summarized as the LMv2 and NTv2 hash which are built using the NT hash, obtained from the Security Accounts Manager (SAM) or Active Directory (AD), and hash it along with the user and domain names using HMAC-MD5. All this form the NetNTLMv2 hash which is what is needed by password cracking tools like John The Ripper or hashcat.
Using PyRDP to capture NetNTLMv2 hashes
PyRDP is a library we developed to carry out monster-in-the-middle attacks and experiment with the RDP protocol. While in MITM mode, PyRDP has the ability to intercept NetNTLMv2 hashes even if it does not have the real server’s certificate and private key and NLA is enforced by the server. In this section, we will explore and describe two scenarios in which we can carry out a hash capture.
In the first scenario, we possess the certificate and private key of the server under attack. In this case, the interaction between the RDP client and server is going to be carried out with CredSSP support and PyRDP will be transmitting NTLMSSP messages both ways. The NetNTLMv2 capture is done after the RDP server sends the CHALLENGE message (here PyRDP extracts the server challenge value from the message), and the client responds with the hash which PyRDP logs and then sends to the RDP server to continue with the authentication process.
We recently implemented this second scenario: NLA is enforced by the server, but we don’t have the server’s certificate and private key. In this scenario, PyRDP will cut the connection with the original server and proceed to carry on the client’s connection and NTLMSSP authentication in order to perform the same NetNTLMv2 extraction as shown before. The trick: PyRDP will generate the CHALLENGE message after receiving the client’s NEGOTIATION message and send it. This way PyRDP has control of the challenge value and later will receive the AUTHENTICATION message.
To illustrate this attack, when PyRDP runs without NLA enabled (the default value, see --auth
flag to enable NLA attacks) and a client tries to connect to a server that enforces it, then this is what the logs will look like following the hash extraction from the AUTHENTICATION message:
[2021-11-10 22:52:11,394] - INFO - GLOBAL - pyrdp - MITM Server listening on 0.0.0.0:3389 [2021-11-10 22:52:22,167] - INFO - Karen105427 - pyrdp.mitm.connections.tcp - New client connected from 127.0.0.1:36936 [2021-11-10 22:52:22,167] - INFO - Karen105427 - pyrdp.mitm.connections.x224 - No cookie for this connection [2021-11-10 22:52:22,190] - INFO - Karen105427 - pyrdp.mitm.connections.tcp - Server connected [2021-11-10 22:52:22,216] - INFO - Karen105427 - pyrdp.mitm.connections.x224 - Server requires CredSSP. Closing connection with server and attempting to catch NTML hashes. [2021-11-10 22:52:28,243] - INFO - Karen105427 - pyrdp.mitm.connections.ntlmssp - NTLMSSP Negotiation [2021-11-10 22:52:28,343] - INFO - Karen105427 - pyrdp.mitm.connections.ntlmssp - [!] NTLMSSP Hash: admin:::937f60a48cea8943:f298d601927699c77aab319e7de5b9ac:010100000000000000debca285d6d7015f3d313dc29e380c0000000002000a00570049004e004e00540001000a00570049004e004e00540004000a00570049004e004e00540003000a00570049004e004e00540005000a00570049004e004e00540006000400020000000a00100000000000000000000000000000000000090022005400450052004d005300520056002f006c006f00630061006c0068006f007300740000000000000000000000000000000000000000000000000000000000 [2021-11-10 22:52:28,444] - INFO - Karen105427 - pyrdp.mitm.connections.tcp - Client connection closed. Connection was closed cleanly. [2021-11-10 22:52:28,448] - INFO - Karen105427 - pyrdp.mitm.connections.tcp - Connection report: report: 1.0, connectionTime: 6.277477025985718, totalInput: 0, totalOutput: 0, replayFilename: rdp_replay_20211110_22-52-22_166_Karen105427.pyrdp
Note that in this last scenario the connection between PyRDP and client is closed, once the hash is extracted, as the MITM process cannot complete the server’s connection since it is protected by NLA. You can use this new feature by running PyRDP out of our master branch or using a recent docker container. This functionality will be part of our upcoming 1.2.0 release.
Conclusion
In this article, we showed how NetNTLMv2 capture during RDP connections works and that PyRDP can be used as a practical offensive tool. Thanks to other projects like Responder that explored this possibility and the extreme extensibility (and flexibility) of PyRDP, this technique is easier to employ and practice. Stay tuned for more functionalities coming to PyRDP in our upcoming release! And be sure to follow us on Twitter and LinkedIn to get the latest updates from GoSecure Titan Labs.