In this blog post we are going to discuss the details of a vulnerability in Windows Remote Desktop Services, which we recently uncovered. We reported the vulnerability to Microsoft in a coordinated disclosure process. Microsoft has released a fix in the latest security update and the vulnerability is now identified as CVE-2022-21893.
This vulnerability enables any standard unprivileged user connected to a remote machine via remote desktop to gain file system access to the client machines of other connected users, to view and modify clipboard data of other connected users, and to impersonate the identity of other users logged on to the machine using smart cards. This could lead to data privacy issues, lateral movement and privilege escalation.
The latest versions of Windows (client and server editions) are affected by this vulnerability, and it goes back at least to Windows Server 2012 R2, so we can say that the majority of Windows versions in use today are affected.
Let’s dive in.
Start with Some Background: RDP Virtual Channels
The Remote Desktop Protocol (RDP) splits a single connection into multiple logical connections called virtual channels for handling different types of data. Some channels are responsible for the core functionality of RDP, such as graphical and input data, while other channels handle protocol extensions, such as clipboard, drive and printer redirection. There is also an API for working with virtual channels which allows writing an application that communicates with RDP clients over custom virtual channels. This blog post contains a great introduction to the RDP protocol.
While some channels are handled by the Remote Desktop Services (RDS) service, which is implemented as a DLL hosted in svchost.exe, others are handled by other processes. For example, the clipboard redirection channel (CLIPRDR) is handled by the rdpclip.exe process which executes separately for each connected user, running under its privileges. The virtual channel data is passed between these processes and the RDS service over Windows named pipes.
Short Introduction to Windows Named Pipes
Named pipes are one of the most common ways for interprocess communication in Windows and work in a client/server model. The named pipe server is created using the CreateNamedPipe function, and clients connect to it using the CreateFile function. Both sides specify the name of the pipe, which is in the following format: \\.\pipe\name (for the server or for a client that connects to a local named pipe) or \\hostname\pipe\name (for a client that connects to a remote named pipe). Once the connection is established, the client and the server use the WriteFile and ReadFile functions to exchange data.
One of the key concepts for understanding this vulnerability and the named pipes attack surface in general is pipe server instances. It is common to have one server process that handles multiple clients. This is achieved by creating multiple pipe server instances, meaning that the server process will call CreateNamedPipe multiple times with the same pipe name. Each time it will get a new server instance. When a client connects to a named pipe server, it connects to one instance. If there are multiple instances available, the client will connect to the one that was created first (FIFO ordering).
Since each call to CreateNamedPipe is independent, it’s also possible for different processes, possibly malicious, to create pipe server instances of the same name. Combining this with the FIFO behavior, we can start to see how this can lead to several issues. So how can we prevent other processes from creating server instances for our named pipes? Well, a named pipe is a securable object; it has a security descriptor that contains access control lists. One of the parameters of CreateNamedPipe is a security attributes object that contains the security descriptor. So whenever using named pipes, it’s important to think about the permissions – who is allowed to access this pipe and how – and pass the security attributes object accordingly. It’s also important to verify that no other process has created a pipe server instance with the same name before we did, unless it’s a valid use case. In that case, the security descriptor would be set. This can be achieved by specifying FILE_FLAG_FIRST_PIPE_INSTANCE in the call to CreateNamedPipe. You can find more information about named pipes and their attack surface in this detailed blog post.
Start Putting it Together: Virtual Channel Named Pipes
As we mentioned, processes that handle virtual channels use named pipes for passing the virtual channel data to and from the RDS service. The name of this pipe is “TSVCPIPE-” followed by a GUID.
Figure 1: Virtual channel communication between rdpclip.exe, RDS and the RDP client
The GUID is generated once by the service and used for all sessions. Restart the service, and you will get a new GUID. While the service is running all sessions will always have pipe instances with the same GUID. So once connected via RDP, we can just list the open pipes on the machine, which is something that every user can do, and see the pipe with its full name.
Figure 2: Output of Sysinternals’ pipelist utility showing the TSVCPIPE pipe
A process can create pipe server instances with the name of an existing pipe server if the security descriptor of the first instance allows it. It turns out that the TSVCPIPE security descriptor allows any user to create pipe server instances of the same name. Moreover, the data is sent over the pipes in clear text and without any integrity checks.
The Basic Attack
The vulnerability makes the following attack scenario possible:
- An attacker connects to a remote machine via RDP
- The attacker lists the open named pipes and finds the full name of the TSVCPIPE pipe
- The attacker creates a pipe server instance with the same name and waits for a new connection
- Once a new connection arrives, RDS creates its own pipe server instance for the session and a pipe client that will attempt to connect to it
- Because of the FIFO, the pipe client will connect to the attacker pipe server instance instead of the one created by the RDS service
- The attacker connects as a client to the real RDS pipe server instance
- The attacker holds both ends of the connection; they can act as man-in-the-middle, passing the data back and forth, viewing and (optionally) modifying it
Figure 3: MiTM process intercepting the TSVCPIPE communication
We have implemented a tool that performs these steps to create a man-in-the-middle that prints the data passing through the pipes. As you can see in the following demonstration video, among other things we are able to see clipboard data. This can be images, files or text that might contain personal data or sensitive data such as passwords, which is often the case in RDP sessions.
Video 1: Clipboard Interception
Taking it a Step Further: Access Other Users’ Redirected Drives
The initial tool just prints out raw data. This is great for demonstrating the issue but going over the raw data without knowing what we are looking for is tedious and impractical. We would like to have something that does more than that. As we said in the beginning, there are several channels that use these pipes, each with its own protocol. We have decided to target the device redirection channel (RDPDR). Our goal is to access the victim’s redirected drives and folders in a convenient way.
The RDPDR Channel
The RDPDR channel is used for redirecting devices such as drives and smart cards from the client machine to the remote session. On the remote machine, the channel is handled by a filesystem driver that communicates with RDS over the TSVCPIPE pipe. Here’s a brief introduction to the protocol.
Figure 4: RDPDR initialization sequence from the protocol specification
The server starts the protocol sequence with a Server Announce Request. The client then responds with a Client Announce Reply and a Client Name Request, passing the client computer name. The client and server then exchange capabilities. Afterward, the client sends a Client Device List Announce Request, passing a list of all devices they want to redirect. The server responds with a Server Device Announce Response for each one of the devices. Once the connection is established, the client and server start exchanging data by sending Device I/O Requests. At each point, the server can send a new Server Announce Request, which will re-initiate the protocol sequence.
Attacking the RDPDR Pipe
To recall, the victim’s pipe client running in the RDPDR filesystem driver is connected to the pipe server in our MiTM process, while we have a pipe client connected to the victim’s pipe server in the RDS service. We could just send our own crafted I/O requests from our pipe client to the victim’s pipe server, which will be forwarded to the client machine over the RDP connection. This seems possible but will require implementing almost the entire protocol. We chose to do something else: As the attacker is also connected via RDP, they have their own pipe instances. If we could make our (the attacker) pipe instances go through our MiTM tool, we could just connect the pipe client running in the filesystem driver for the attacker session to the pipe server running in RDS for the victim session. This way, the attacker should be able to see the victim’s drives (in file explorer, for example) as if they were connected to their client machine.
Figure 5: Change Handles
Since the attacker connects to the machine and only then runs the MiTM process, their pipes are not connected to it. So how can we get the attacker pipes to connect to the MiTM process? That’s easy. We could, for example, start a new session for the attacker, but we chose to use the session reconnection feature of Remote Desktop. If a user disconnects from a session, the session is maintained for a certain period, and if the same user connects to the machine again, it will reconnect to the same session. While the session is in disconnected state, the processes continue to run. So, we only need to close the RDP client and connect with the same user again to reconnect to the same session. Upon reconnection, the pipes will be recreated but this time the MiTM process will catch them, allowing us to change the pipe handles (the objects that point to the actual pipe objects in the Windows kernel. As part of the connection, the filesystem driver of the attacker session will start the RDPDR protocol initialization sequence, sending a Server Announce Request. We will forward this request to the victim, which will make it re-initialize the protocol – but this time with the attacker session. We have successfully implemented this technique. Here’s a video demonstrating the attack:
Video 2: Drive Redirection
The Grand Finale: Hijacking Smart Cards
Smart card redirection also uses the RDPDR channel; it is just another device. This means that if a user connects using a smart card (or just redirects their smart card to use it from within the session), the attacker could also take over the user’s smart card and use it as if it were connected to their machine. When the victim enters their smart card PIN number, an IO control request is sent to the smart card over the channel with the PIN number in clear text, so the attacker can see it. The attacker can now connect to any resource, on the same machine or on other machines, using the victim’s smart card and PIN number, effectively impersonating the victim’s security context. In case the victim logs in with a privileged account, this leads to privilege escalation. The following video demonstrates the smart card attack:
Video 3: Smart Card Redirection
This vulnerability shows an example of an unconventional attack vector targeting RDP. Instead of tapping into the input side of the server/client as one usually does, we abused the RDP server internal mechanism as an entry point.
We chose to focus on drive and smart card redirection. We believe that we could also apply the same technique to other types of devices, protocols and channels, such as printers, audio, USB devices and authentication redirection (Remote Credential Guard).
As mentioned above, we reported the issue to Microsoft and they have released a patch to fix the issue. We strongly recommend applying this patch (almost all Windows versions are affected). Also, developers of applications that use custom virtual channels should check whether they are vulnerable and conduct their own security assessment.
For more information on Microsoft’s patch, please visit https://msrc.microsoft.com/update-guide/vulnerability/CVE-2022-21893.
08/19/2021 – Vulnerability reported to Microsoft with the initial POC
08/25/2021 – Microsoft acknowledged the reported behavior
09/01/2021 – Microsoft shared their severity assessment. The vulnerability is rated “Important”
09/12/2021 – Updated Microsoft regarding the RDPDR scenario and sent a new POC
12/29/2021 – Microsoft assigned CVE-2022-21893
01/11/2022 – Microsoft released a patch fixing the issue
We would like to share our appreciation to Microsoft Security Response Center for their cooperation.