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.

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 or nmap.
Блог script runner
Example remote listing of named pipes on a Windows system

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.

Блог script runner
Checking the existence of a named pipe works. Listing all named pipes doesn’t.

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.

Блог script runner
Listing of named pipes via IPC$ fails
Блог script runner
Share type of IPC$ is not Disk (0x02) but Named pipe (0x01)

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.

Блог script runner
Using smbclient.py to list named pipes
Блог script runner
Wireshark capture of smbclient.py listing named pipes

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.

Блог script runner
RemotePipeList tool as part of Outflank’s C2 Tool Collection

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.

:/>  Включение центра обновлений на Windows 10: что делать, если не работает служба

View the RemotePipeList tool on GitHub in the C2 Tool Collection.