Revisiting Remote Desktop Lateral Movement

It’s no secret that attackers are looking for new techniques to execute lateral movement. However, there are only a handful of publicly known techniques that are typically used. This post doesn’t highlight a new lateral movement technique but instead offers a new way to leverage a known method in your favorite Command and Control (C2) platform. I’ll show how to leverage the Remote Desktop Protocol (RDP) for the purposes of non-graphical authenticated remote command execution against a target. When using RDP for lateral movement, an operator will typically start a SOCKS proxy, use an RDP application/client, execute a payload, and close out the session. Doing this has always felt like unnecessary overhead to perform one action.

The idea of performing RDP lateral movement through an existing C2 channel without a SOCKS proxy or GUI RDP client always seemed possible but wasn’t really publicly talked about. While searching for existing work, I found several message boards and forums where people mentioned the idea and posted small code snippets but I never found a fully weaponized version. After compiling disparate information and bits of code and discussing the topic with coworkers, I had a starting point to work with.

Ultimately, I discovered Windows has a library mstscax.dll that gives access to any possible RDP action and is used by virtually all RDP clients, including Remote Desktop Connection or Royal TSX. This DLL is an ActiveX COM library for Microsoft Terminal Services. By leveraging this DLL, an operator can create a console application that performs authenticated remote command execution through the RDP without the need of a GUI client or SOCKS proxy.

Today, I’m releasing SharpRDP, a tool that is a .NET console application that can be used to perform authenticated command execution against a remote target for the purposes of lateral movement.

How it works

The terminal services library (mstscax.dll) has two different forms that can leveraged, the scriptable control that can be used by web client or scripts and the non-scriptable control that would be used in native or managed code. SharpRDP relies on the non-scriptable control of the COM library. The ActiveX importer aximp.exe, which is part of the .NET SDK, is required to generate the appropriate DLLs MSTSCLib.DLL and AxMSTSCLib.DLL from the mstscax.dll DLL. MSTSCLib.dll contains the managed definitions for the library while the AxMSTSCLib.dll contains the Windows Form control for the ActiveX classes. Both DLLs contain classes that are required to perform the actions needed for lateral movement.

Figure 1 — SharpRDP execution through CobaltStrike

Windows forms are used during terminal services connection object instantiation and because of this we create a Windows form object that is invisible to a user to which we are executing from. From this form, we can call methods to perform actions needed for all of the lateral movement steps such as:

  • connection

  • authentication

  • actions on target (command execution)

  • disconnection from the host

Each of these actions are registered as events and event handlers that determine the course of action to be taken.

There are two ways to authenticate, by either providing plain text credentials (likely the most common and usable scenario) or by current user context with restricted admin mode. Restricted admin mode is a Windows protection mechanism that performs a network type logon rather than interactive to prevent the caching of credentials when RDPing to a host. This has commonly been abused for pass the hash with RDP.

Once authenticated, the SharpRDP sends virtual keystrokes to the remote system via a method called SendKeys. Since SharpRDP currently only supports keystrokes, by default this will open up a Run dialog and enter a specified command. In addition to that, there is also the ability to run as a child process of Cmd.exe or PowerShell.exe that will be spawned from the Run dialog.

OPSEC ConsiderationWhen running anything from the Run dialog, a registry entry will be created at HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU showing what was entered into the box. For lateral movement purposes, after establishing a new agent, CleanRunMRU is recommended to clean up the command that was just executed.

In order to use this with a C2 tool, like Cobalt Strike, there were two small hurdles to overcome. First, there were two DLLs that needed to be compiled into the assembly. Since there are methods required in both the MSTSCLib.DLL and AxMSTSCLib.DLL DLLs, they are added as resources and compiled into the assembly. The app domain assembly resolve event is used to catch the failure of the DLLs not being in the same directory and loads the added resources. Second, the combined sizes of the DLLs are roughly a little less than 1.5MB in size and by compiling these DLLs into the assembly it makes it bigger than the 1MB Cobalt Strike limit on assembly sizes. To overcome this, both DLLs are compressed as resources and y are decompressed for usability when the assembly resolve event is called.

Use Cases

There are sometimes scenarios when RDP would be a preferred way to execute a lateral movement technique but may be difficult using a traditional RDP client GUI. One of the most common that comes to mind is trying to use an RDP client over high latency connections.

Second, with this we can mitigate a lot of the overhead that previously would go into using RDP, it is now as simple as running a single command through a C2 implant. For RDP in general we can use it for times when we do not have local administrative privileges on a system, but have RDP rights to that system, which can be seen from BloodHound (Figure 2) or your favorite reconnaissance tool.

Figure 2— BloodHound showing hosts the current user has RDP rights to

As previously mentioned, normally this tool will require plain text credentials unless the target host has restricted admin mode enabled (HKLM\ System\ CurrentControlSet\ Control\ Lsa DisableRestrictedAdmin set to 0), which will execute from the current user context. If credentials are not provided with the command it will fall back to attempting to authenticate to the current context with restricted admin mode.

While this tool has its uses, there are also some situations that it does not handle.

  1. Multi-factor authentication — there is no way currently to handle MFA authentication and SharpRDP will disconnect the session

  2. UAC Bypass — operators will need to find a way to escalate privileges or bypass UAC for the newly executed implant if they wish to operate from a privileged context

  3. File Movement — SharpRDP does not have the ability to copy or move files over the RDP protocol. If 3389 is the only port open on a host, you will not be able to drop a file on target with this tool

SharpRDP has been made public and can be found here.


Under the hood, this tool uses the same methods other RDP clients use, but there are still tool and technique specific indicators.

RunMRU Registry Keys — As previously mentioned, since SharpRDP relies on sending keystrokes to the target system and executing commands via the Windows Run dialog. When something is entered into the run dialog and ran, a registry entry is created at HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU showing executed command history. An operator failing to clean up that entry would leave a forensic indicator of what they executed.

Parent-Child Process Relationship — The parent process of any command that is executed via SharpRDP is Explorer.exe unless the command execution type is set to Cmd or PS then it is explorer -> cmd or explorer -> ps respectfully.

Windows Event Logs — Monitor Windows Event ID (EID) 4624 Type 10 or Type 3 (if NLA and or restricted admin is enabled) logons to target hosts for evidence of remote logon activity.

Non-standard Module Load — A more robust technique focused detection could look for mstscax.dll loaded into a process that is not mstsc.exe (or other baseline RDP application used in environment). This can be seen in Figure 3 where the process rundll32 has the DLL loaded into it.

Figure 3 — Rundll32 has mstscax.dll loaded into the rundll32 process

Looking for RDP BMC cache files in %LOCALAPPDATA%\Local\Microsoft\Terminal Server Client\Cache could show forensic evidence of what an attacker was doing.

Non-standard process use of TCP 3389 — Looking for processes that are not mstsc.exe (or pre-defined baseline RDP clients) that communicate over TCP port 3389. While some basic detection logic was given RDP can be a messy protocol to use from an attackers point of view for the forensic artifacts that are left. JPCert has some excellent information regarding RDP detection and forensic evidence.

Future Work

The Use Cases section highlighted some areas SharpRDP lacks in its current state and will be addressed in the future. In addition, there are a few areas that will be fixed (e.g., ensuring pass the hash works properly (given Restricted Admin Mode is enabled) so that plaintext or current context is not the only authentication method). I plan to actively maintain the project and any reported issues reported or requested enhancements will be fixed or taken into consideration.


Recent Posts

See All

Python 3 Network Packet Sniffer

A simple pure-Python network packet sniffer. Packets are disassembled as they arrive at a given network interface controller and their information is displayed on the screen. This application maintain