Guide to Named Pipes and Hunting for Cobalt Strike Pipes

Intro to Named Pipes

The way that helped me start to understand pipes is to think of them as like type of network socket that is created. It can be used to send and receive information between processes or even hosts.

As a rudimentary example, you can query the current pipes on your host:

Get-ChildItem \\.\pipe\

Now lets try creating one. Below is a basic script to create a named pipe using PowerShell:

try {
$pipeName = "bad_pipe"
$pipe = New-Object system.IO.Pipes.NamedPipeServerStream($pipeName)
Write-Host "Listening on \\.\pipe\$pipeName"
$sr = new-object System.IO.StreamReader($pipe);
$msg= $sr.ReadLine()
Write-Host "I received a message: ", $msg
catch {
Write-Host "Pipe Creation Failed..."
return 0

Once running, it will listen for and data sent to it and write it to console. We can quickly test this by redirecting the stdout of a simple command:

echo "Sending data to pipe" >\\.\pipe\bad_pipe

See it in action here:

How Cobalt Strike uses Named Pipes

There is heaps of existing research on how Cobalt Strike utilises named pipes:

In this blog, Raphael Mudge (the creator of Cobalt Strike), notes some of the default pipe names. You can also customise the names of these pipes using Malleable C2 profiles.

See a sample of regexes for pipe names I put together from default and custom profiles below:

Using Velociraptor to Search for Malicious Named Pipes

When a process uses a named pipe, it creates a handle. Below is a sample of VQL that will walk through all running processes and pull the handles of the process. It will then search for any handles that match the regex bad_pipe.

LET pipeRegex = 'bad_pipe'
LET processes = SELECT Pid AS ProcPid, Name AS ProcName, Exe
FROM pslist()
WHERE ProcPid > 0
SELECT * FROM foreach(
SELECT ProcPid, ProcName, Exe, Type, Name, Handle
FROM handles(pid=ProcPid)
WHERE Name =~ pipeRegex

Using the example created before, I left the named pipe open and ran the VQL above in a notebook which returned the following result:

It recorded what process was using the pipe as well as the pipe name! Using the regex of some of the default named pipes lets put all this to the test.

In Cobalt Strike, the interface for creating a new SMB listener the default pipe name was msagent_f8 which matches what we learnt before. I ran jump psexec_psh to laterally move to a different host.

If we jump into Velociraptor, I created an artefact to search for any handles that match the regex outlined previously. You can see we have the process details as well as the pipe name of the SMB beacon.

This was a good start and found named pipes such as the SMB beacon that stay open for a long period of time, but it doesn’t catch the transient named pipes.

Of course, if you are lucky enough to have Sysmon deployed to the network already, you can easily monitor for these same named pipes as shown below:

Edit: I’m currently researching the possibility of monitoring named pipes with ETW and using Velociraptor further.






Love podcasts or audiobooks? Learn on the go with our new app.

Learn to code by making a 2D space shooter game.

Zotero Tutorial — 1

This is title

Kiali: Manage, visualize, validate and troubleshoot your Service mesh!

Elasticsearch queries — part 03 — term level queries

Flog: A fake log generator for common log formats

How we designed Snook’s software team

Laravel 5.7 — Blade Layout Files

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store


More from Medium

Musings about further Log4Shell Attack Vectors

Israels Edge

Cybersecurity Rewind 2021

The Future of Cyber Attacks