I’ve mentioned previously the vulnerability to squatting attacks of WCF services using the standard NetNamedPipe binding in .NET version 3.5, and promised to show how it might be done. Let’s review the nature of the vulnerability.
First, the attacker needs to know the name of the named pipe object used by the service’s endpoint, and my previous posts have discussed how this can be discovered if the service URL is known.
Second, the attacker calls the Windows APIs CreateNamedPipe and ConnectNamedPipe to open a server side handle to the pipe and start listening on it. The CreateNamedPipe call succeeds because the pipe’s ACL grants FILE_CREATE_PIPE_INSTANCE permissions to EVERYONE.
The attacker then just waits until a client of the service connects to the attacker’s instance of the pipe rather than one of the instances on which the service itself is listening. Once he has “captured” a client, the attacker may exploit it in a number of ways, such as:
- impersonate the client’s windows security identity
- steal data from messages sent by the client
- spoof the behaviour of the service to mislead the client
- insert itself as a man-in-the-middle to spy on traffic between the client and the real service
The classes provided in the System.IO.Pipes namespace make the first of these very easy to do in C#:
using (NamedPipeServerStream pipeServer = new NamedPipeServerStream(pipeName, PipeDirection.InOut, -1)) // Calls CreateNamedPipe
pipeServer.WaitForConnection(); // Calls ConnectNamedPipe
// We have to read at least the first byte sent by the client, before we can identify or impersonate him
// Steal the client's identity
This exploit is not possible in .NET4, provided the WCF service is hosted in a Windows Service, because in the .NET4 NetNamedPipeBinding the FILE_CREATE_PIPE_INSTANCE permission is now restricted by the pipe’s ACL to the Logon ID SID of the process hosting the service. Note, though, that if the service is self-hosted in a console application (or indeed any application) running in an Interactive logon session, any process running in that session will still be able to squat on the pipe using this exploit. This includes any processes started using the secondary logon service (the RunAs service) with a different identity to the interactively-logged on user. It also means that starting a self-hosted WCF service as a console application using RunAs to run the service in the context of a dedicated service account, provides no protection against squatting by other processes in the same Interactive logon session.
Exploiting impersonation in the manner shown above, without any further communication with the pipe, will disrupt the communication the client is expecting to see and will surface as an exception in the client process: either a timeout error or a pipe IO error, depending on how long the attacker code takes before closing the pipe instance. Taking the exploit beyond impersonation, and making it less visible to the client, involves understanding both the data stream sent by the client and the data the client expects to be sent back across the pipe. I will go on to look at this in future posts, since it is interesting not just in the context of this vulnerability, but more generally to anyone who has contemplated implementing a non-.NET client or service to communicate with a service/client using the WCF named pipe binding.