On Windows, named pipes are a form of interprocess communication (IPC) that allows processes to communicate with one another, both locally and across the network. Named pipes serve as a mechanism to transfer data between Windows components as well as third-party applications and services. Both locally as well as on a domain. From an offensive perspective, named pipes may leak some information that could be useful for reconnaissance purposes. Since named pipes can also be used (depending on configuration) to access services remotely – they could allow remote exploits (MS08-067).
In this post we will explore how named pipes can be listed remotely in offensive operations, for example via an implant running on a compromised Windows system.
Read more: Listing remote named pipes
Several tools already exist to list named pipes.
- To display locally bound named pipes you could use SysInternals’ PipeList.
- Bobby Cooke (@boku7) made the xPipe BOF to list local pipes and their DACLs.
- To list named pipes on a remote system you could use
smbclient.py
in impacket ornmap
.
From the above example list of named pipes shown remotely, we could learn multiple things: the Windows Search service is active (MsFteWds), terminal services/RDP sessions are active (TSVCPIPE), a Chromium-based browser is in use (mojo.*), some Adobe Creative Cloud services are available, the user makes use of an SSH agent, a PowerShell process is active, and WireShark is in use.
That’s a lot of information – in the usual configuration we can typically list these remote named pipes (e.g. with smbclient.py
) using regular domain credentials against domain-joined systems.
However, when you try to remotely enumerate named pipes using the existing Win32 APIs to perform reconnaissance against a remote system, things get a bit interesting. IPC$ is the magical share name used for interprocess communication. While you could use the built-in Win32 APIs to determine the existence of a remote named pipe with \serverIPC$pipename
, listing the \serverIPC$
“folder” results in an error.
Part of the reason is explained on the PipeList web page:
Did you know that the device driver that implements named pipes is actually a file system driver? In fact, the driver’s name is NPFS.SYS, for “Named Pipe File System”. What you might also find surprising is that its possible to obtain a directory listing of the named pipes defined on a system. This fact is not documented, nor is it possible to do this using the Win32 API. Directly using NtQueryDirectoryFile, the native function that the Win32 FindFile APIs rely on, makes it possible to list the pipes. The directory listing NPFS returns also indicates the maximum number of pipe instances set for each pipe and the number of active instances.
We can see what is going on with WireShark: when listing the IPC$ share of a remote system, we get a “Tree Connect Response” indicating that the Share Type is 0x02
(named pipe).
At this point, the directory listing already fails because the Share Type is not supported for file listing. A regular file share would be 0x01 (disk).
While we can locally use the Win32 APIs to list locally available named pipes, we cannot interact directly with the NtQueryDirectoryFile
API remotely. Tools like smbclient.py
still allow us to get a remote named pipe listing because they implement the entire SMB stack themselves and can manually call SMB functions like SMB2_FIND_FULL_DIRECTORY_INFO
– regardless of the Tree Connect Response. Apparently, this SMB function calls the same NtQueryDirectoryFile
API.
Unfortunately, while it seems to be not possible to list remote named pipes due to this from a Beacon Object File (BOF) in an implant that calls Win32 functions, we could still reimplement the SMB stack in BOF. And while that sounds like a lot of work, there are open-source implementations already available. We therefore built a small POC on top of SMBLibrary, an SMB library written in C#.
RemotePipeList
We’ve added a small tool to our C2 Tool Collection that uses SMBLibrary to list remotely available named pipes. Through inline assembly execution in Cobalt Strike / Stage1, we can then easily use this tool via an implant from the operator’s C2 framework.
The current code requires you to specify a username/password and always authenticates using NTLM. This could potentially be extended to support integrated authentication (and Kerberos).
We have added an aggressor script for Cobalt Strike and a new task for the Stage1 C2 server. Both can be invoked via the remotepipelist
command.
View the RemotePipeList tool on GitHub in the C2 Tool Collection.