Named Pipes
Named pipes are Windows IPC endpoints. A privileged service exposing a pipe with permissive DACLs (Everyone or Authenticated Users granted write) and trusting the client side becomes a privilege escalation vector. Enumerate pipes, check their DACLs, identify writable ones owned by privileged processes, and exploit the trust the service places in its clients.
# Enumerate active pipespipelist.exe /accepteula # Sysinternalsgci \\.\pipe\ # PowerShell equivalent
# Check DACL on specific pipeaccesschk.exe -accepteula -w \\.\Pipe\NAME -v
# Find all writable pipes (operator-priority filter)accesschk.exe -accepteula -w \pipe\ -v | findstr /B "RW"
# Inspect what owns the pipeGet-Process -Id <PID> # cross-reference pipe ownerSuccess indicator: a pipe is RW accessible to Everyone or Authenticated Users, owned by a SYSTEM-level process, and the service speaks a protocol that accepts attacker-controlled input.
Why named pipes matter
Section titled “Why named pipes matter”Named pipes are how Windows processes talk to each other on the same machine (and across machines, though that’s less common). Every named pipe has:
- A name -
\\.\pipe\<name>accessed via the special\\.\device path. Examples:\\.\pipe\lsass,\\.\pipe\WindscribeService. - A server side - The process that created the pipe and reads/writes from it.
- Zero or more client connections - Each connection creates a new pipe instance sharing the name but with independent data buffers.
- A DACL - Specifies which security principals can read, write, or modify the pipe.
The privilege escalation angle: a pipe is a programmable communication channel between a high-privileged process and whatever clients its DACL permits. If a SYSTEM-level service creates a pipe writable by Everyone, every standard user can send messages to that service. If the service trusts the messages and acts on them with its SYSTEM token, attackers control SYSTEM behavior.
This is fundamentally a service-design vulnerability, not a Windows vulnerability. The OS provides the IPC mechanism correctly; the service vendor chose the wrong DACL.
Enumerating pipes
Section titled “Enumerating pipes”PipeList (Sysinternals)
Section titled “PipeList (Sysinternals)”C:\> pipelist.exe /accepteula
PipeList v1.02 - Lists open named pipesCopyright (C) 2005-2016 Mark RussinovichSysinternals - www.sysinternals.com
Pipe Name Instances Max Instances--------- --------- -------------InitShutdown 3 -1lsass 4 -1ntsvcs 3 -1scerpc 3 -1Winsock2\CatalogChangeListener-340-0 1 1epmapper 3 -1LSM_API_service 3 -1atsvc 3 -1eventlog 3 -1spoolss 3 -1wkssvc 4 -1trkwks 3 -1vmware-usbarbpipe 5 -1srvsvc 4 -1ROUTER 3 -1vmware-authdpipe 1 1Output columns: pipe name, number of currently-connected client instances, and maximum allowed instances (-1 means unlimited).
The first dozen entries are Windows built-ins (lsass, eventlog, spoolss, srvsvc, etc.) - these have correct DACLs by design; don’t waste time on them. The interesting entries are everything else - vmware-usbarbpipe, vmware-authdpipe, WindscribeService, and any other third-party pipes.
PowerShell
Section titled “PowerShell”Without Sysinternals, the same listing is available through PowerShell:
PS> gci \\.\pipe\
Directory: \\.\pipe
Mode LastWriteTime Length Name---- ------------- ------ ---------- 12/31/1600 4:00 PM 3 InitShutdown------ 12/31/1600 4:00 PM 4 lsass------ 12/31/1600 4:00 PM 3 ntsvcs------ 12/31/1600 4:00 PM 3 scerpcThe Length column shows the number of instances. The LastWriteTime is a Windows artifact (always 1600/12/31 - pipes don’t track this meaningfully).
For just the names without the noise:
PS> [System.IO.Directory]::GetFiles("\\.\pipe\") | ForEach-Object { Split-Path $_ -Leaf }Identifying third-party pipes
Section titled “Identifying third-party pipes”Filter for non-standard entries - anything not in the standard Windows set:
PS> $standard = @('InitShutdown','lsass','ntsvcs','scerpc','epmapper','LSM_API_service','atsvc','eventlog','spoolss','wkssvc','trkwks','srvsvc','ROUTER','PIPE_EVENTROOT\CIMV2SCM EVENT PROVIDER','Winsock2','MsFteWds','SQLLocal')PS> [System.IO.Directory]::GetFiles("\\.\pipe\") | ForEach-Object { $name = Split-Path $_ -Leaf if ($standard -notcontains $name -and $name -notmatch '^Winsock2|^MsFte|^SQLLocal') { $name }}This produces a smaller list of pipes worth investigating. The standard-pipe filter list isn’t exhaustive - environments have different baselines - but the principle is to focus enumeration on non-Microsoft pipes first.
Reviewing DACLs
Section titled “Reviewing DACLs”A pipe’s DACL determines who can read from it, write to it, and modify its security descriptor. The DACL is what makes a pipe an escalation vector or not.
accesschk on a specific pipe
Section titled “accesschk on a specific pipe”C:\> accesschk.exe /accepteula \\.\Pipe\lsass -v
Accesschk v6.12 - Reports effective permissions for securable objectsCopyright (C) 2006-2017 Mark RussinovichSysinternals - www.sysinternals.com
\\.\Pipe\lsass Untrusted Mandatory Level [No-Write-Up] RW Everyone FILE_READ_ATTRIBUTES FILE_READ_DATA FILE_READ_EA FILE_WRITE_ATTRIBUTES FILE_WRITE_DATA FILE_WRITE_EA SYNCHRONIZE READ_CONTROL RW NT AUTHORITY\ANONYMOUS LOGON FILE_READ_ATTRIBUTES FILE_READ_DATA ... RW BUILTIN\Administrators FILE_ALL_ACCESSOutput format:
- Pipe path at top
- Mandatory integrity level -
Untrusted Mandatory Level [No-Write-Up]means even unauthenticated callers can connect, but they can’t escalate their integrity through the pipe - Each ACE prefixed with
RW(read+write),R(read-only), orW(write-only), followed by the principal and granted permissions
For lsass, Everyone has RW but only with specific limited permissions - this is normal for the LSA RPC interface. The check is whether the useful permissions for sending commands (typically FILE_WRITE_DATA and FILE_WRITE_EA) are granted to non-privileged groups.
Bulk DACL review - find all writable pipes
Section titled “Bulk DACL review - find all writable pipes”The operationally useful command - find every pipe writable by the current user:
C:\> accesschk.exe -accepteula -w \pipe\ -vFlags:
-accepteula- Skip the EULA prompt-w- Show only objects the current user has write access to\pipe\- Target all named pipes (note: leading backslash, not\\.\pipe\)-v- Verbose, show specific permissions
Output is large; filter for RW lines with non-Administrator principals:
C:\> accesschk.exe -accepteula -w \pipe\ -v | findstr /B "RW" | findstr /V "Administrators SYSTEM TrustedInstaller"This narrows to pipes writable by less-privileged principals. Each match needs investigation: what process owns the pipe, what protocol does it speak, what does it accept from clients.
Identifying pipe ownership
Section titled “Identifying pipe ownership”A pipe’s name doesn’t always reveal who owns it. Cross-reference using Process Monitor or by inspecting handles:
C:\> handle.exe -accepteula -a \pipe\WindscribeService
Handle v5.0 - Handle viewerCopyright (C) 1997-2022 Mark RussinovichSysinternals - www.sysinternals.com
WindscribeService.exe pid: 2156 type: File 224: \Device\NamedPipe\WindscribeServicehandle.exe (Sysinternals) shows that the pipe is owned by WindscribeService.exe (PID 2156). Then check what context that process runs as:
PS> Get-Process -Id 2156 -IncludeUserName
Handles WS(K) CPU(s) Id UserName ProcessName------- ----- ------ -- -------- ----------- 245 8456 1.23 2156 NT AUTHORITY\SYSTEM WindscribeServiceNT AUTHORITY\SYSTEM confirms this is a SYSTEM-level process - a writable pipe to it is the escalation vector.
Get-Process -IncludeUserName requires elevation on most systems. From an unprivileged context, use tasklist /v instead:
C:\> tasklist /fi "PID eq 2156" /vThe User Name column will appear in the output.
The writable-pipe attack pattern
Section titled “The writable-pipe attack pattern”Once a pipe is identified as writable by a non-privileged principal and owned by a privileged process, the actual exploitation depends on what the service’s protocol does. The pattern:
- Identify the service - vendor product, version, name of the binary. Critical for finding documented CVEs and protocol details.
- Understand the protocol - reverse-engineer or find published documentation of what messages the service accepts. This is the heavy lifting.
- Find an actionable command - does the protocol allow file writes, command execution, arbitrary registry changes, service install? If so, identify the format.
- Send the message - write the crafted message to the pipe via standard file I/O (named pipes are accessed like files).
- The service acts on the message with its SYSTEM token - and now your attacker-supplied data has run with SYSTEM privileges.
The Windscribe pattern (CVE-2018-11479)
Section titled “The Windscribe pattern (CVE-2018-11479)”The canonical published example is the Windscribe VPN named pipe vulnerability. Walking through it as a methodology illustration:
1. Enumerate pipes, find Windscribe in the list:
C:\> pipelist.exe /accepteula | findstr -i windscribeWindscribeService 1 -12. Check the DACL:
C:\> accesschk.exe -accepteula -w \pipe\WindscribeService -v
\\.\Pipe\WindscribeService Medium Mandatory Level (Default) [No-Write-Up] RW Everyone FILE_ALL_ACCESSEveryone has FILE_ALL_ACCESS - full read/write/modify. This is the indicator.
3. Identify ownership:
Handle.exe or Process Explorer shows WindscribeService.exe (PID varies) running as NT AUTHORITY\SYSTEM.
4. Understand the protocol:
The Windscribe service speaks a JSON-formatted protocol over the pipe to its UI client. One supported command is “execute helper” which lets the client run a program with arbitrary arguments. The service trusts the client because - by intent - the pipe is between the GUI and the service. But the DACL allows anyone to be that client.
5. Send a crafted message:
The PoC sends a JSON request asking the service to execute cmd.exe (or any binary) with attacker-chosen arguments. The service executes the command as SYSTEM. Net effect: arbitrary command execution as SYSTEM from a standard user context.
The published PoC is in C# at the Exploit-DB link; running it on a vulnerable Windscribe install yields SYSTEM. The patch (in Windscribe 1.83+) fixes the DACL.
What to look for in any pipe-based service
Section titled “What to look for in any pipe-based service”The Windscribe pattern generalizes. When you find a writable pipe owned by a privileged process:
- Is there a public CVE or write-up? Search the service name + “named pipe” + “privilege escalation”.
- What does the GUI client do? If the product has a UI, the UI talks to the service over the pipe. Reverse engineering the UI’s pipe usage reveals the protocol.
- What’s the message format? JSON, length-prefixed binary, line-oriented text, .NET BinaryFormatter, MSRPC, custom? Each has different attack patterns.
- What commands does the service support? Look for command names containing
Execute,Run,Launch,Install,Update,Service,Process,Spawn,File,Write,Copy. - Are arguments sanitized? Often: not really. Path-confusion attacks, argument injection, and “we trusted the GUI” assumptions are common.
Reverse-engineering the protocol
Section titled “Reverse-engineering the protocol”When no CVE exists and you need to figure out the protocol yourself:
- Procmon + the GUI client - Run the GUI, watch its pipe operations. Process Monitor’s
Operation: WriteFileevents on the pipe show every message sent. - Wireshark on
\Device\NPF_NamedPipe- Some Wireshark installations have a named-pipe capture plugin. - Inject into the GUI process - Hook
WriteFileandReadFilecalls; log all data going through. - Static analysis - Decompile the service binary with Ghidra or IDA, find the function reading from the pipe, trace what it does with the data.
- Strings on the binary - Often command names appear as plaintext strings, giving hints about the protocol vocabulary.
Detection by defenders
Section titled “Detection by defenders”| Detection | What it sees |
|---|---|
| Sysmon event 17/18 (Pipe Created/Connected) | All pipe creates and connects, with PID and image |
Process Monitor filter on Process Name: yourtool.exe | Direct view of what your tool is doing |
| EDR pipe heuristics (CrowdStrike, SentinelOne) | Known-bad pipe names; unusual cross-PID pipe traffic |
WMI permanent event subscriptions watching Win32_NamedPipeFile | Custom defender setup |
For evasive operations, pipe-based exploitation is moderately noisy - Sysmon catches the pipe connect, and EDR may flag the data flowing across. For non-evasive engagements, the noise level is acceptable for the payoff.
Defender perspective - preventing this
Section titled “Defender perspective - preventing this”For completeness, the defender side of named-pipe abuse:
- Pipe DACLs should be principle-of-least-privilege - the GUI client should authenticate to the service over the pipe (not just connect via DACL trust)
- Services should not trust pipe clients - every message should be validated, authenticated, and parameter-checked
- Avoid impersonation traps -
SeImpersonatePrivilegeinteracts with pipe authentication; see SeImpersonate for the related token-theft pattern
Quick reference
Section titled “Quick reference”| Task | Pattern |
|---|---|
| List all pipes (Sysinternals) | pipelist.exe /accepteula |
| List all pipes (PowerShell) | gci \\.\pipe\ |
| Pipe names only | [System.IO.Directory]::GetFiles("\\.\pipe\") | ForEach-Object { Split-Path $_ -Leaf } |
| DACL of specific pipe | accesschk.exe -accepteula -w \\.\Pipe\NAME -v |
| All writable pipes | accesschk.exe -accepteula -w \pipe\ -v |
| Writable pipes (filtered) | accesschk.exe -accepteula -w \pipe\ -v | findstr /B "RW" |
| Find pipe owner | handle.exe -accepteula -a \pipe\NAME |
| Process user context | Get-Process -Id PID -IncludeUserName or tasklist /v /fi "PID eq PID" |
| Standard pipes to ignore | lsass, ntsvcs, scerpc, epmapper, eventlog, spoolss, wkssvc, srvsvc, atsvc, etc. |
| Indicators of vulnerability | RW Everyone or RW Authenticated Users on a pipe owned by SYSTEM-context service |
| Protocol RE tools | Procmon, Wireshark NPF, Ghidra, IDA Pro |
| Canonical CVE example | CVE-2018-11479 (Windscribe), CVE-2020-7280 (McAfee), CVE-2020-26878 (Ansible) |
For the token-impersonation angle that’s adjacent to pipes (Potato-family attacks abuse named pipes plus SeImpersonatePrivilege), see SeImpersonate. For service-binary abuse where pipes aren’t involved but the service is, see the OS attacks round (weak service ACLs).