Unlocking the Essential Eight: A complete guide for Australian organisations

Harnessing the power of Named Pipes

Technical

Unveiling Windows application instrumentation and exploitation

Published by Callum Ford, Security Testing and Assurance on 24 March 2025

 

Overview

Thick client penetration testing is a commonly requested testing service offered by security consultancies around the world. While methodologies for performing reviews against Microsoft Windows applications are plentiful and thorough for common attack vectors such as network traffic, DLL hijacking, reverse engineering, and insecure file permissions, not much information is readily available for attacks against non-HTTP Inter-Process Communication (IPC) methods.

While reviewing an application that utilised named pipes as a client-server communication medium, CyberCX found that although common attacks were identified and outlined in online blogs and documents, there was a lack of simplified tooling for interacting with this traffic.

This post provides a simple methodology for identifying, monitoring, and exploiting named pipes. It also offers some insight into how custom tooling can be made to instrument Windows applications. Examples of these tools are published on CyberCX’s Github account for free use in training or assessments.

 

Identifying Named Pipes

The identification process includes not only the creation and usage of named pipes, but also how they are configured by both the server and client instances. Two methods can be used to discern this information – static analysis (the examination of source code or compiled binaries/libraries without execution) and dynamic analysis (the examination of an application at runtime).

Static analysis

Static analysis with a focus on identifying named pipe usage and security parameters changes drastically depending on what language the application was written in and how it is compiled. When the application is written in C# and compiled in a manner that the user is provided with DLLs (or source is provided) identifying pipe usage and configuration is fairly straightforward. For compiled DLLs, tools such as the dnSpyEx (ElektroKill, 2023) fork of (wtfsck, 2020) or ILSpy can be used to view an interpretation of the C# source code.

The instantiation statements that are relevant to named pipes include NamedPipeServerStream() and NamedPipeClientStream() for servers and clients respectively (Microsoft, 2020).

Various parameters can be passed into these classes during instantiation that can expose the application to various attacks. These include the following:

NamedPipeServerStream()
  • Pipe name: (useful for server-based attacks).
  • Pipe direction: (determines how the server and client interact with the pipe: write only, read only, or full duplex).
  • Maximum number of server instances: (multiple instances can be used to exploit race conditions).
  • Transmission mode: (data can be written or read as either a stream of messages or bytes).
  • Pipe options: (other options related to how the pipe is handled by the server, for example Asynchronous)

Figure 1 Showing an example of instantiating NamedPipeServerStream.

 

NamedPipeClientStream()
  • Pipe and server names (defines the server and pipe the client is communicating with).
  • Specified pipe direction (how the client will interact with the pipe).
  • Pipe options (additional options for client pipe interaction).
  • Security impersonation level (can a malicious server leverage the client to perform actions as the client’s user?).
  • Inheritability mode (can the handle be inherited by a child process?).

Figure 2 Showing an example of instantiating NamedPipeClientStream.

 

Subsequent read and write operations are performed using a StreamString class and the pipe client and server objects, as shown in the Figure 1 and Figure 2 screenshots.

Other classes exist that can be used for finer grain named pipe Access Control List (ACL) and audit rules. These audit rules can be searched for within source code. These include:

  • PipeSecurity
  • PipeAccessRule
  • PipeAuditRule
  • PipesAclExtensions

These rules can also be identified easily using dynamic analysis.

Another method of using named pipes in C# and .NET is through native functions. An example is shown below in Figure 3 and Figure 4, and can also be seen using dnSpy (wtfsck, 2020).

Figure 3 Importing the CreateNamedPipe and ConnectNamedPIpe native functions in C#.

 

Figure 4 Using the CreateNamedPIpe native function to create a named pipe instance.

 

Dynamic analysis

Dynamic analysis is a debugging method that involves analysing an application during run time. Software exists that allows us to identify active pipes on the system and retrieve limited information relating to how an application has configured it.

Using SysInternals’ Process Explorer (Microsoft, 2023) tool, it is possible to identify whether the target application is accessing any named pipe instances through an inspection of their active file handles.

Figure 5 Navigating to the Handles display within Process Explorer.

 

Named pipes are shown in the format \Device\NamedPipe\… as shown in the example in Figure 6.

 

As named pipes are accessed in the same manner as standard files, information about in-place access control lists can be obtained using another fantastic SysInternals tool; accesschk.exe (Microsoft, 2022).

Run the following command using the previously identified pipe name (note the format differs with exception to the highlighted portion of the name):

The following is example output of the command:

Accesschk v6.15 - Reports effective permissions for securable objects
Copyright (C) 2006-2022 Mark Russinovich
Sysinternals - www.sysinternals.com

\\.\Pipe\test
  RW NT AUTHORITY\SYSTEM
  RW BUILTIN\Administrators
  RW DESKTOP-FOQHQEV\CCX
  R  Everyone
  R  NT AUTHORITY\ANONYMOUS LOGON

The output shows that this pipe can be read and written to by Administrators and the lower privileged CCX user. This information shows how this pipe instance restricts write access; however, it is readable by everyone, and could be vulnerable to sensitive information disclosure through direct monitoring  by a low privilege user.

pipelist.exe (Microsoft, 2021), another SysInternals offering, can be used to discern both the current number of active instances, and the maximum allowed based on the configuration of the first instance.

Run the following command specifying the previously identified pipe name:

pipelist64.exe | findstr test
test                                          1               -1

 

Each connection to a named pipe results in the creation of a new instance. This is listed in the first numeric value in the output (1). The latter value defines the limit of active instances, which in this case is indefinite (-1)

 

Monitoring named pipes

Monitoring pipe-related read and write operations performed by clients and servers is useful to determine what the pipe is used for, when it is being used, and what kind of data the target handles. This could result in the disclosure of sensitive data, expose vulnerable functionality (such as insecure deserialisation), or provide insight into proprietary IPC protocols.

One method of accessing this traffic is through the PeekNamedPipe Windows API function exposed by namedpipeapi.h. This function copies the contents of a pipe without clearing it, exposing its contents without interrupting program flow. This has some caveats in that it provides a snapshot of the pipe’s contents at the time but does not provide a representation of data flow to and from a server or client.

With an understanding of how named pipes are created and used at a Windows API level, it becomes easier to monitor this traffic using instrumentation libraries such as Frida (Ravnås, n.d.). Frida is a dynamic instrumentation toolkit (often used when reviewing mobile applications but also supports desktop OS runtimes), which enables reverse engineers and security researchers to hook into various function calls at runtime.

By hooking into CreateNamedPipe Windows API calls, we can expose the API call’s arguments, allowing us to perform all the checks included in the static analysis section without requiring direct access to source code or decompiled DLLs.

Written in-house at CyberCX, the Peep instrumentation tool (https://github.com/CyberCX-STA/Peep) observes the transmission of named pipe traffic. This script utilises the Frida instrumentation framework to hook a target application’s ReadFile and WriteFile API function calls, allowing the user to observe read and write operations, providing a representation of data flow. This can be used to monitor traffic to and from both pipe clients and servers, executing the target application on use or injecting directly into a running instance.

A few command examples are shown below:

frida path/to/target.exe -l path/to/peep.js
frida -p (process pid) -l path/to/peep.js



Figure 7 Example output of Peep for a simple named pipe client (left) and server (right). Note the matching read/write operations, showing the flow of data for each application.

 

 

When used to launch a pipe server, Peep hooks into CreateNamedPipe function calls and discloses useful data relating to the pipe, including its:

  • read/write/duplex mode
  • message/byte transmission mode
  • remote access permissions.

Figure 8 Showing example of useful pipe data.

 

Exploiting named pipes

Direct interaction

As non-HTTP IPC traffic is often overlooked from a security perspective, direct access to a pipe server could facilitate the discovery and exploitation of common client/server attacks, such as insecure deserialisation, SQL injection, code execution etc. This could lead to lateral compromise in remote servers or privilege escalation in locally hosted servers.

To observe this directly, we created a named pipe client (https://github.com/CyberCX-STA/ImpersonationPipeClient) to provide read and write access to arbitrary pipes. It allows users to configure the client using command line arguments or at runtime to suit different data transmission modes and impersonation options.

The commands are as follows:

Usage: client.exe {flag} {value} ...
     -v    interactive mode
     -h    hostname to connect to (Default value is ".".)
     -n    name of the target named pipe. (Required parameter)
     -t    transmission mode to connect to the pipe. (Default value is message)
               values:     message, byte
     -d    pipe direction. (Default value is duplex)
               values:     duplex, in, out
     -i    impersonation level. (Default value is none)
               values:     none, impersonation, anonymous, delegation, identification

Using just the –v flag, the tool will enter interactive mode, allowing the user to insert the values one at a time if preferred:

Figure 9 Example of the client’s interactive mode.

 

Using this tool, it is possible to simulate a legitimate client, send arbitrary request content, and view the server’s response.

Figure 10 Showing example output of sending and receiving using Peep. This screenshot is a continuation of the session from the screenshot above.

Note: the client currently only supports communication in UTF-8 and UTF-16. Support for communication in raw bytes is planned for a future release.

 

Client impersonation

“Impersonation is the ability of a thread to execute using different security information than the process that owns the thread. Typically, a thread in a server application impersonates a client. This allows the server thread to act on behalf of that client to access objects on the server or validate access to the client’s own objects.” (alvinashcraft, et al., 2021)

Named pipe servers often utilise impersonation to run their functionality from the security context of the client. This impersonation can lead to escalation of privileges in instances where a legitimate, high-privileged client is coerced into making a connection to a malicious pipe server. This server can utilise the ImpersonateNamedPipeClient API function to impersonate the client, create a thread token under the client’s security context, duplicate it, and reuse it to launch arbitrary processes in that security context.

To demonstrate this, we created a named pipe server https://github.com/CyberCX-STA/ImpersonationPipeServer that leverages this API call among others to perform this attack. This is a C# implementation of 0xcsandker’s sample code  (Sandker, InterProcessCommunication-Samples, 2021) (C# was chosen for implementation as it was easier to load in memory via PowerShell).

It should be noted that this attack requires several conditions for both the client application and the server’s host account. If the pipe client opens a connection to a local pipe \\.\pipe\test, it can only be impersonated if the client has also set the SECURITY_SQOS_PRESENT flag, and a dangerous impersonation level has been used (e.g. .

For remote pipe servers, it is possible to impersonate a client provided the SECURITY_SQOS_PRESENT flag is not set and the user account running the server has the SeImpersonatePrivilege and/or SeEnableDelegationPrivilege (Sandker, 2021). .

This is shown when connecting to the Impersonation pipe server using the following modified C++ example from Microsoft (DCtheGeek, et al., 2021).

LPCTSTR lpszPipename = TEXT("\\\\.\\pipe\\test");

if (argc > 1)
    lpvMessage = argv[1];
// Try to open a named pipe; wait for it, if necessary.
while (1)
{
    hPipe = CreateFile(
        lpszPipename,   // pipe name
        GENERIC_READ |  // read and write access
        GENERIC_WRITE,
        0,              // no sharing
        NULL,           // default security attributes
        OPEN_EXISTING,  // opens existing pipe
        0,              // default attributes
        NULL);          // no template file
...

Note that the CreateFile function call uses \\.\pipe to access the locally hosted pipe, and no SECURITY_SQOS_PRESENT flag has been provided. Figure 11 shows the error output this causes.

Figure 11 Showing the error of the example C++ client.

 

Now when the 127.0.0.1 IP address has been provided:

LPCTSTR lpszPipename = TEXT("\\\\127.0.0.1\\pipe\\test");

No error is returned, and the specified command is executed as shown in Figure 12.

 

Figure 12 Showing the edited example C++ client successfully executing.

 

Coercing named pipe clients

Superfluous pipe connectivity

Often in client/server setups, the client application may attempt to connect to a server via named pipe without verifying whether the server is running at the time. In these cases, it is possible to stand up a malicious impersonation named pipe server using the same name, forcing the client into initiating a connection.

Situations in which this can occur are instances in which the client is active and the server is not, such as:

  • The server has been exited or the process has been killed manually.
  • The server’s service has been stopped or disabled.
  • The server has been deleted or necessary files have been modified, preventing its launch.
  • The server crashes for any reason.

The former three in the above list can occur due to the absence of/insecure permissions, such as world-writeable directories or weak service permissions. The latter can be coerced through other vulnerabilities identified in the server application.

To identify superfluous pipe connectivity, execute or inject into the client process using the Peep instrumentation tool. All calls to CreateFile that specify pipe-like file paths will print a “Potential Pipe:” message in the Frida console.

Potential pipe connections that fit a pipe format but do not successfully create a file handle should be investigated as it may be a superfluous connection.

 

Instance creation race condition

This attack is similar to superfluous connectivity in that it involves standing up a malicious pipe server using the same name used by a target client/server pair. This differs in that it leverages the ability for a system to host multiple instances of named pipes that share a name and utilises timing to create an instance that supersedes the legitimate server.

Named pipe instances work in a First in First Out (FIFO) order, meaning a client that creates a named pipe has a higher priority than secondary instances. If the malicious pipe server is created prior to the legitimate server, it will have a higher priority when the client first initiates a connection. This can also work in reverse provided the legitimate server’s CreateNamedPipe call does not limit concurrent named pipe instances to one. If so, it may be possible to leverage server crashes or planned/unplanned restarts by queueing the malicious pipe server as an instance which will supersede the legitimate value on restart due to FIFO.

Weaknesses in the design of the target server that facilitate this attack include:

  • A greater maximum number of concurrent pipes than needed.
  • Predictable pipe names(Watts, 2002).
  • Creating a pipe without the FILE_FLAG_FIRST_PIPE_INSTANCE security flag.
  • A lack of verification to whether the pipe exists prior to creation.

Named pipes can be accessed by clients via the CreateFile Windows API and read from or written to via ReadFile and WriteFile respectively. These functions are also used when processing generic files, and often without specifying security flags such as SECURITY_SQOS_PRESENT.

If given the opportunity to specify arbitrary file paths into a client, it may be possible to coerce the application into creating an insecure file handle for a malicious pipe server. As specified above, if the provided file path is in the format \\127.0.0.1\pipe\test, the absence of a SECURITY_SQOS_PRESENT could allow the server to impersonate the client, leading to escalated privileges given that the client is running as a user with a heightened security context.

The C# class functions File.ReadAllText, StreamWriter, and FileStream run under a SecurityAnonymous impersonation level, giving a 1347 error. This prevents impersonation, so this attack is often targeted at bespoke file manipulation functions such as those making direct calls to the CreateFile Windows API.

Further research needs to be done to see how this behaviour is handled in other programming languages.

 

Operating System (OS) vulnerabilities

Several OS vulnerabilities exist that can be abused to elevate permissions using named pipe impersonation.

Example exploits for this are the “SpoolSample” (Christensen, 2018) or “Bad Potato” (BeichenDream, 2020) Proof of Concept (PoC) which leverages the MS-RPRN protocol over RPC to initiate a connection to a named pipe as NT Authority/SYSTEM. Exploitation is performed using the following MS-RPRN functions (Microsoft, 2021) as shown in the “Bad Potato” PoC:

RpcOpenPrinter(string.Format("\\\\{0}", Environment.MachineName), out rpcPrinterHandle, null, ref
dEVMODE_CONTAINER, 0); //Opens a print handle to the local machine

RpcRemoteFindFirstPrinterChangeNotificationEx(rpcPrinterHandle, 0x00000100, 0, string.Format("\\\\{0}/pipe/{1}", Environment.MachineName, pipeName), 0) //Uses the above
print handle and initiates a connection to the provided pipe in the \\MACHINENAME\pipe\GUID\pipe\spoolss format.

 

Recommendations to secure named pipes

The following security recommendations for named pipe implementations are based on the above research:

  • Treat named pipe communication as a trust boundary. As any client can interact with a named pipe, this content should not be considered trusted, and all data should be validated by the server application prior to its use in sensitive or dangerous functionality.
  • Protect the named pipe from instance race conditions by limiting concurrent pipes, utilising the FILE_FLAG_FIRST_PIPE_INSTANCE flag, and performing checks to ensure the named pipe does not already exist.
  • The principle of least privilege should be advised throughout the design process. If impersonation and delegation are not required for the application to function, do not utilise them in client connections. Additionally, ensure the named pipe has appropriate read and write access controls. This will prevent unauthorised users from accessing data within the pipe.
  • If you are integrating the client and server within an internal network, don’t neglect defence in depth measures, such as network ACLs, intrusion detection, and intrusion prevention.

 

Future research and development

Further research needs to be done to see how the above exploitation steps are affected when implemented in other languages and frameworks, as well as with the inclusion of the fine-grained ACL and Audit rules.

Additionally, the tools listed above have undergone limited use in real life scenarios, so some edge cases may introduce issues that will need to be addressed.

 


References

alvinashcraft, DCtheGeek, drewbatgit, mijacobs, msatranjr, & v-kents. (2021, 01 08). Client Impersonation (Authorization). Retrieved from Microsoft Learn: https://learn.microsoft.com/en-us/windows/win32/secauthz/client-impersonation

BeichenDream. (2020, 05 11). BadPotato. Retrieved from GitHub: https://github.com/BeichenDream/BadPotato

Christensen, L. (2018, 10 06). SpoolSample. Retrieved from GitHub: https://github.com/leechristensen/SpoolSample

DCtheGeek, drewbatgit, mijacobs, msatranjr, stevewhims, & v-kents. (2021, 01 08). Named Pipe Client. Retrieved from Microsoft Learn: https://learn.microsoft.com/en-us/windows/win32/ipc/named-pipe-client

ElektroKill. (2023, September 28). dnSpy. Retrieved from GitHub: https://github.com/dnSpyEx/dnSpy

Microsoft. (2020, 10 20). System.IO.Pipes Namespace. Retrieved from Microsoft Learn: https://learn.microsoft.com/en-us/dotnet/api/system.io.pipes

Microsoft. (2021, 04 07). 3.1.4.10.4 RpcRemoteFindFirstPrinterChangeNotificationEx (Opnum 65). Retrieved from Microsoft Learn: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/eb66b221-1c1f-4249-b8bc-c5befec2314d

Microsoft. (2021, 03 24). PipeList v1.02. Retrieved from Microsoft Learn: https://learn.microsoft.com/en-us/sysinternals/downloads/pipelist

Microsoft. (2022, 12 05). AccessChk v6.15. Retrieved from Microsoft Learn: https://learn.microsoft.com/en-us/sysinternals/downloads/accesschk

Microsoft. (2023, 03 30). Process Explorer v17.04. Retrieved from Microsoft Learn: https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer

Ravnås, O. A. (n.d.). A world-class dynamic instrumentation toolkit. Retrieved from Frida: https://frida.re/

Sandker, C. (2021, January 11). InterProcessCommunication-Samples. Retrieved from GitHub: https://github.com/csandker/InterProcessCommunication-Samples/blob/master/NamedPipes/CPP-NamedPipe-Basic-Client-Server/CPP-Basic-PipeServer/CPP-Basic-PipeServer.cpp

Sandker, C. (2021, 01 10). Offensive Windows IPC Internals 1: Named Pipes. Retrieved from csandker: https://csandker.io/2021/01/10/Offensive-Windows-IPC-1-NamedPipes.html#prerequisites

Watts, B. (2002, 04 01). Discovering and Exploiting Named Pipe Security Flaws for Fun and Profit. Retrieved from Blake Watts: https://www.blakewatts.com/blog/discovering-and-exploiting-named-pipe-security-flaws-for-fun-and-profit

wtfsck. (2020, 12 08). dnSpy. Retrieved from GitHub: https://github.com/dnSpy/dnSpy

 


 

Learn more about our Security Testing and Assurance team

 

 

Ready to get started?

Find out how CyberCX can help your organisation manage risk, respond to incidents and build cyber resilience.