Named pipes: the Windows attack surface you build yourself
by Claudiu Hulea · IT Management Consultant
Named pipes are a normal mechanism for communication between processes in Windows, used by services, agents and everyday applications. They are also a two-sided attack surface: one you build yourself into your own software, one an attacker uses to become SYSTEM. Both are legitimate uses of the operating system, and that is exactly why they go unnoticed.
What they are and why they matter
A named pipe is a communication channel between processes on the same machine, exposed by the operating system through a name of the form \\.\pipe\something. It is fast, native and everywhere: between a Windows service and its interface, between a background agent and a command-line utility, between the components of one application. In some configurations, a pipe can also be reachable remotely, over SMB, which widens the surface beyond the local machine.
The problem is that a pipe looks the same regardless of who is at the other end. Who connects first, who listens, who impersonates whom, these are all decisions you make in code or see only if you monitor them.
Face 1: vulnerabilities you build yourself
If you write a service that uses named pipes, you can introduce a few classes of defect yourself.
Pipe squatting. An attacker creates a pipe with the name your legitimate server is about to open, before the server starts. Your client then connects to the attacker’s process, not yours. It is possible because, on named pipes, a write right implicitly includes the right to create a new instance with the same name (FILE_CREATE_PIPE_INSTANCE). The code fix is direct: create the pipe with FILE_FLAG_FIRST_PIPE_INSTANCE, which fails if the name is already taken instead of silently joining a squatted pipe.
Confused deputy. A privileged service, for example one running as LocalSystem, receives requests over a pipe from less-privileged clients and executes them with its own rights. If you do not authorise each command separately, an ordinary client borrows the service’s power.
Badly handled impersonation. A pipe server can adopt the client’s security context. Done wrong, it becomes the very vector from the second face, below.
Denial of service. Connections held open, requests in bursts or payloads declared huge can exhaust the service’s resources.
Remote clients. A pipe exposed over SMB accepts connections from the network. If the service assumes it only ever talks to local processes, the assumption is false.
Face 2: the escalation primitive to SYSTEM
This is where named pipes move from “a bug in your service” to “a tool in the attacker’s kit”.
Named pipe impersonation is a classic privilege-escalation primitive to SYSTEM. An attacker already running code with the SeImpersonatePrivilege privilege, which many service accounts hold, creates a pipe and forces a privileged client, for example SYSTEM, to connect to it. It then calls ImpersonateNamedPipeClient, adopts the client’s token, duplicates it into a primary token and starts a process as SYSTEM.
The technique is not theoretical. It is the exploit family known as “Potato” and has long been built into offensive tooling: getsystem in Cobalt Strike, Metasploit, PoshC2. The precondition is an initial foothold that holds SeImpersonatePrivilege, so it is a post-compromise move, not the way in.
Named pipes as C2
The third use is covert communication. An implant can listen on a named pipe, providing a command channel with no open ports and no classic outbound beacon. The traffic is hard to tell apart from normal activity, because pipes are everywhere in Windows.
Combined with SMB, named pipes become a lateral-movement path: an implant on one machine talks to another over a pipe, and the channel looks like normal Windows administration. It is exactly the kind of communication an EDR sees poorly, because it has no clear signature to catch.
How to defend
Defence, like the attack, has two faces.
If you build a service with named pipes:
- Create the pipe with
FILE_FLAG_FIRST_PIPE_INSTANCE, so you do not join a squatted pipe. - Apply a restrictive security descriptor (DACL) that limits who can connect.
- Verify the client’s identity, the Windows account and the process, not just that someone connected.
- Authorise each command separately and separate validation from privileged execution.
- Validate input strictly and frame messages (message framing).
- Set connection limits, timeouts and resource controls.
- Explicitly reject remote clients, if the service is local.
If you defend a fleet:
- Least privilege on
SeImpersonatePrivilege. It is the key to the whole escalation primitive. Accounts that do not need it should not have it. - Monitor pipe creation and connection (Sysmon Event ID 17 and 18) and impersonation started from unexpected processes.
- Alert on pipe names known to C2 tools and on abnormal impersonation patterns. There are ready-made detection rules, for example in Elastic, for rogue named pipe impersonation.
What to take away
Named pipes are useful and legitimate, and that is exactly why they are a good attack surface: they look normal from both ends. The lesson is the same as at any trust boundary in the system. Do not assume who is at the other end, verify their identity, authorise each action and give each process only the privilege it needs. It is the same discipline as “verify the effect, not the configuration”, applied at the IPC level.
Want to know what services and what named pipes actually run across your estate, and who can connect to them? Get in touch and we start from a review.
Sources
Frequently asked questions
What is a named pipe and why is it an attack surface?
A named pipe is a channel for communication between processes on the same Windows machine, exposed through a name of the form \\.\pipe\something. It is fast, native and everywhere (services, agents, utilities), and in some configurations it is reachable remotely over SMB. It is an attack surface because it looks the same regardless of who is at the other end: who connects first, who listens and who impersonates whom are decisions you make in code or see only if you monitor them.
What is pipe squatting?
An attacker creates a pipe with the name your legitimate server is about to open, before the server starts, so your client connects to the attacker's process. It is possible because a write right on named pipes implicitly includes the right to create a new instance with the same name (FILE_CREATE_PIPE_INSTANCE). The code fix: create the pipe with FILE_FLAG_FIRST_PIPE_INSTANCE, which fails if the name is already taken.
How does a named pipe lead to SYSTEM?
Through named pipe impersonation, a classic privilege-escalation primitive. An attacker already running code with SeImpersonatePrivilege (which many service accounts hold) creates a pipe, forces a privileged client, for example SYSTEM, to connect, then calls ImpersonateNamedPipeClient, adopts the client's token, duplicates it and starts a process as SYSTEM. It is the "Potato" family, built into Cobalt Strike, Metasploit and PoshC2.
How do I defend?
If you write a service: create the pipe with FILE_FLAG_FIRST_PIPE_INSTANCE, apply a restrictive DACL, verify the client's identity, authorise each command separately and separate validation from privileged execution. If you defend a fleet: least privilege on SeImpersonatePrivilege, monitor pipe creation and connection (Sysmon Event ID 17 and 18), and alert on impersonation from unexpected processes and on pipe names known to C2 tools.