ShadowRDP

This page describes how to use the ShadowRDP feature to remotely access another user's console session using a beacon object file.

Outcome

Head over to this repo for the proof of concept: link

Introduction

ShadowRDP allows administrators to view as well as control the target user's desktop session remotely. This feature is based on the Windows Desktop Sharing API. It tends to be compared to HVNC, and I can see some overlap. Both allow the attackers to gain access to the target user desktop GUI. However, ShadowRDP does not require an existing foothold or payload to be running on the target system. Thus by using existing features it is possible to perform fileless lateral movement under the right conditions. My interest in this feature originated from a red team exercise in which we obtained access to a jump server where users would RDP into and connect to an internal dashboard accessible with different credentials. Although alternatives such as HVNC or RDP hijacking exist, it was still an appealing option that had not yet been recreated in such a way that a red team operator could easily use it during an assessment.

Previous research

Previous blogs covering the ShadowRDP functionality in great detail can be found here and here. I recommend reading them first to get an idea of what ShadowRDP is all about. This article differs in that the attacker no longer needs to use the client-side mstsc.exe application to perform the attack remotely, but can instead use a combination of a BOF and GUI application.

Requirements

The are six prerequisites that must be met in order to perform the attack. This number depends on the type of system you're facing and its configuration. These are as follows.

1. Windows version

The target machine Windows version must be 8.1 and later or Server 2012 R2 and later. This is due to a different implementation of shadowing in older versions, which can only be performed locally on the server. As a result, they lack the RPC endpoints needed to obtain an invitation to the desktop session.

2. Remote Desktop Protocol

RDP must be enabled. The port itself does not need to be accessible since the connection does not go over 3389 but a dynamic high port. This can be achieved by modifying the fDenyTSConnections registry value under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server to 0 using the reg_set BOF from TrustedSec.

3. Firewall

There are several firewall rules that must be enabled before the attack can begin. These can be remotely modified using the FwMgr COM object mentioned in Riccardo Ancarani's Troopers talk found here. This does require the RPC-TCP port to be open, however there are other methods that do not.

Port
Rulename
Description

445

File and Printer Sharing (SMB-In)

Used to obtain the invitation string to the target user desktop session.

49152-65535

Remote Desktop - Shadow (TCP-In)

These ports are for setting up the Shadow connection. Alternatively, the Rdpsa.exe application can be whitelisted.

4. Shadow registry key

To remote without consent, the Shadow registry value under HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services has to be set to one of the values below using the reg_set BOF from TrustedSec.

Value
Name
Description

0

Disable

Shadowing is disabled.

1

EnableInputNotify

Permission is asked first from the session being shadowed. The shadower is also permitted keyboard and mouse input.

2

EnableInputNoNotify

Permission is not asked first from the session being shadowed. The shadower is also permitted keyboard and mouse input.

3

EnableNoInputNotify

Permission is asked first from the session being shadowed. The shadower is not permitted keyboard and mouse input and MUST observe the shadowed session.

4

EnableNoInputNoNotify

Permission is not asked first from the session being shadowed. The shadower is not permitted keyboard and mouse input and MUST observe the shadowed session.

Requesting access to the remote session with consent will show the following prompt on the target user desktop. Because of the ominous question, I decided to hard-code the BOF to request access without consent. This means that unless you modify the bof, only values 2 and 4 will work.

5. Authorization

An account or session with local administrator privileges on the target host. The current logged-on user will be used to request the invitation.

6. Active desktop session

The most important requirement is that the target host must have an active desktop session. This can be verified using netero1010's Quser BOF which recreates the query user command.

Demystifying ShadowRDP

After reading about ShadowRDP and its capabilities, I wanted to know more about how it works and if it is possible to avoid using the built-in mstsc.exe executable. I was looking for a simple solution to reverse engineer the application in order to get an idea what it does under the hood. That's when I discovered API Monitor, which was perfect for my use case. All someone has to do is give it an executable and some arguments. It generates a list of WINAPI functions that are called during execution along with the arguments.

Searching through the output can be done using the find tool. I started by looking for the arguments that were passed to the application like the session id or the hostname of the target machine.

API Monitor's output can be quite large at times. That's when you'd use the API filtering tool to get rid of some of the unnecessary functions. This can also be done on an individual basis in the summary section by right-clicking the function call, hovering over exclude, and selecting API Name.

The first part of the puzzle was finding the SessEnvPublicRPC binding. This endpoint is used to create a Windows Desktop Sharing API invitation for the target session. The same API used to remotely control or monitor someone's session!

Making a proof of concept program is quite straight forward. It starts by using some boilerplate RPC code and compiling the IDL file of the RPC interface to get the source files. This can be accomplished by dragging it into your Visual Studio project and compiling it by right-clicking the file and selecting the compile option. Please keep in mind that some IDL files may have dependencies that must be included before they can be successfully compiled.

The RPCShadow2 function available in the SessEnvRpc source files allow the user to specify which level of access they want over the session and if the user will be notified or not. Because asking for consent could reveal your presence to the user behind the session, I have left that argument as a hard-coded value. The full list of arguments are as follows.

Parameter
Description

hBinding

The RPC binding handle to SessEnvPublicRpc.

TargetSessionId

The id of the target active console or RDP session.

eRequestControl

Specify whether you want a view-only or control connection. The values are 0 or 1 respectively.

eRequestPermission

Specify whether you want the user to consent to the shadowing of the session or forcefully connect to his session. The values are 0 or 1 respectively.

pePermission

User response to permission request. If the response is anything other than SHADOW_REQUEST_RESPONSE_ALLOW, the shadow session has been denied.

pszInvitation

The buffer used to hold the invitation. This is represented as a WCHAR pointer.

cchInvitation

The size, in WCHARs (16-bit Unicode), of the provided buffer in pszInvitation.

At this point I was able to obtain an invitation to the target session. This consist of a XML string which is also called a ticket sometimes depending on the Microsoft documentation page you visit.

The anatomy of the string can be found below. It includes an authorization value and all the details of the target session. If you'd like to learn more about it, please read chapter 2.2.2 of the MS-RAI protocol documentation available in the resources.

<E>
 <A KH="Protocol-specific Parameter" KH2="Protocol-specific Parameter" ID="Authorization
String Identifier" />
 <C>
 <T ID="Transport ID" SID="Session ID">
 <L P="Port" N="Server Name" />
 </T>
 </C>
</E>

Turning this into a BOF proved to be a little more challenging. The issue is that RPC functions throw exceptions instead of returning an error code when something fails. But thankfully the people at TrustedSec already researched this topic and provided the code required to catch these exceptions. You can read all about it here.

For the GUI part, I knew I had to use the IRDPSRAPIViewer COM object in order to connect to the remote session thanks again to API Monitor.

I stumbled upon previous work from Athenian and wap2k on the Rohitab forum. Their implementation required a server-side component which someone would have to run on the target machine to generate the invitation. After a few modifications, it was possible to remote into sessions using the generated XML string from the proof of concept.

Demo

The following video shows the Alice user on a Windows 10 system shadowing Bob's session on a Windows server 2016 machine. It includes the enumeration utilizing the Quser BOF as well as the attack itself. All requirements were met prior to the demo for the sake of keeping it short. (Coffloader was used during this demo because Cobalt Strike did not support RPC functions when using BOFs at the time of writing.)

Conclusion

In this article, I demonstrated how to use the ShadowRDP technique to perform lateral movement. Although the prerequisites are extensive, when confronted with the right circumstances, this attack vector can be the deciding factor in successfully completing a red team exercise.

Resources

Last updated