天天看點

windows 檢視程序_淺談 windows 命名管道

很多時候,在一些文章中,工具利用中,都會提到管道(pipe)。那麼,什麼是管道呢?管道能做什麼呢?本文以 windows 管道為主,邊學習邊整理,希望可以給其他感興趣的人提供幫助。如有不到之處,或是描述錯誤的地方請大家多多包涵,多多指點。

一、管道簡述

管道并不是什麼新鮮事物,它是一項古老的技術,可以在很多作業系統(Unix、Linux、Windows 等)中找到,其本質是是用于程序間通信的共享記憶體區域,确切的的說應該是線程間的通信方法(IPC)。

顧名思義,管道是一個有兩端的對象。一個程序向管道寫入資訊,而另外一個程序從管道讀取資訊。程序可以從這個對象的一個端口寫資料,從另一個端口讀資料。建立管道的程序稱為管道伺服器(Pipe Server),而連接配接到這個管道的程序稱為管道用戶端(Pipe Client)。

在 Windows 系統中,存在兩種類型的管道: “匿名管道”(Anonymous pipes)和“命名管道”(Named pipes)。匿名管道是基于字元和半雙工的(即單向);命名管道則強大的多,它是面向消息和全雙工的,同時還允許網絡通信,用于建立用戶端/伺服器系統。

這兩種管道的主要差別:

命名管道:可用于網絡通信;可通過名稱引用;支援多用戶端連接配接;支援雙向通信;支援異步重疊 I/O 。

匿名管道:單向通信,隻能本地使用。

由于匿名管道單向通信,且隻能在本地使用的特性,一般用于程式輸入輸出的重定向,如一些後門程式擷取 cmd 内容等等,在實際攻擊過程中利用不過,是以就不過多展開讨論,有興趣可以自行檢索相關資訊。

二、命名管道

1、定義與特點

命名管道是一個具有名稱,可在同一台計算機的不同程序之間或在跨越一個網絡的不同計算機的不同程序之間,支援可靠的、單向或雙向的資料通信管道。

命名管道的所有執行個體擁有相同的名稱,但是每個執行個體都有其自己的緩沖區和句柄,用來為不同用戶端提供獨立的管道。

任何程序都可以通路命名管道,并接受安全權限的檢查,通過命名管道使相關的或不相關的程序之間的通訊變得異常簡單。

用命名管道來設計跨計算機應用程式實際非常簡單,并不需要事先深入掌握底層網絡傳送協定(如 TCP、UDP、IP、IPX)的知識。這是由于命名管道利用了微軟網絡提供者(MSNP)重定向器通過同一個網絡在各程序間建立通信,這樣一來,應用程式便不必關心網絡協定的細節。

任何程序都可以成為服務端和用戶端雙重角色,這使得點對點雙向通訊成為可能。在這裡,管道服務端程序指的是建立命名管道的一端,而管道用戶端指的是連接配接到命名管道某個執行個體的一端。

總結一下:

1、命名管道的名稱在本系統中是唯一的。

2、命名管道可以被任意符合權限要求的程序通路。

3、命名管道隻能在本地建立。

4、命名管道是雙向的,是以兩個程序可以通過同一管道進行互動。

5、多個獨立的管道執行個體可以用一個名稱來命名。例如幾個用戶端可以使用名稱相同的管道與同一個伺服器進行并發通信。

6、命名管道的用戶端可以是本地程序(本地通路:

\.\pipe\PipeName

)或者是遠端程序(通路遠端: 

\ServerName\pipe\PipeName

)。

7、命名管道使用比匿名管道靈活,服務端、用戶端可以是任意程序,匿名管道一般情況下用于父子程序通訊。

2、檢視管道清單

在 windows 系統中,列出管道清單的方法有很多。這裡列舉幾種常見的檢視方式。

a、powershell

使用 powershell 列出管道清單需要區分版本,V3 以下版本的 powershell 隻能使用:

[System.IO.Directory]::GetFiles("\.\pipe\")
windows 檢視程式_淺談 windows 命名管道

V3 及以上版本的 powershell 還可以使用:

Get-ChildItem \.\pipe\
windows 檢視程式_淺談 windows 命名管道
b、chrome

使用 chrome 檢視管道清單,隻需在位址欄輸入

file://.//pipe//

注:部分系統可能不支援 chrome 檢視管道清單
windows 檢視程式_淺談 windows 命名管道
c、其他工具

可以使用

Process Explorer

Find-Find Handle or DLL

功能查找名為

\Device\NamedPipe

windows 檢視程式_淺談 windows 命名管道

或者還可以使用 Sysinternals 工具包中的 

pipelist.exe

 等工具。

d、遠端檢視

需要注意的是,這些方法僅支援本地檢視,無法遠端檢視。

A pipe server can provide the pipe name to its pipe clients, so they can connect to the pipe. The pipe client discovers the pipe name from some persistent source, such as a registry entry, a file, or another application. Otherwise, the clients must know the pipe name at compile time.

從微軟官方提供的說明文檔,我們可以看出,想要知道管道名就需要具有一定的本地操作權限,而在正常情況下,管道用戶端是無法直接擷取管道清單的,如果想要通路就必須提前獲得管道名稱。

當然,由于固定管道名稱的存在,還是可以通過其他方式擷取一部分管道名稱。

例如使用 Metasploit 中的

pipe_auditor

和 

pipe_dcerpc_auditor

 兩個子產品,就可以通過碰撞确認哪些命名管道可通過 SMB 使用。

windows 檢視程式_淺談 windows 命名管道
windows 檢視程式_淺談 windows 命名管道

常見的可能會被利用到的管道名有以下這些:

netlogonlsarpcsamrbrowseratsvcDAV RPC SERVICEepmappereventlogInitShutdownkeysvclsassLSM_API_servicentsvcsplugplayprotected_storagerouterSapiServerPipeS-1-5-5-0-70123scerpcsrvsvctapsrvtrkwksW32TIME_ALTwkssvcPIPE_EVENTROOT\CIMV2SCM EVENT PROVIDERdb2remotecmd
           

3、建立與通路

在 windows 中命名管道的通信方式是:

1.建立命名管道 --> 2.連接配接命名管道 --> 3.讀寫命名管道
a、建立

管道伺服器無法在另一台計算機上建立管道,是以 CreateNamedPipe 必須使用句點

.

作為伺服器名稱,如以下示例所示。

\\.\pipe\PipeName

管道名稱字元串可以包含反斜杠以外的任何字元,包括數字和特殊字元。整個管道名稱字元串最多可以包含 256 個字元。管道名稱不區分大小寫。

服務端的整個建立過程如下:

(一)服務端程序調用 CreateNamedPipe 函數來建立一個有名稱的命名管道,在建立命名管道的時候必須指定一個命名管道名稱(pipe name)。

因為 Windows 允許同一個本地的命名管道名稱有多個命名管道執行個體,是以,伺服器程序在調用 CreateNamedPipe 函數時必須指定最大允許的執行個體數(0 -255),如果 CreateNamedPipe 函數成功傳回後,伺服器程序得到一個指向一個命名管道執行個體的句柄。

(二)然後,伺服器程序就可以調用 ConnectNamedPipe 來等待客戶的連接配接請求,這個 ConnectNamedPipe 既支援同步形式,又支援異步形式,若伺服器程序以同步形式調用 ConnectNamedPipe 函數,(同步方式也就是如果沒有得到用戶端的連接配接請求,則會一直等到有用戶端的連接配接請求)那麼,當該函數傳回時,用戶端與伺服器之間的命名管道連接配接也就已經建立起來了。

(三)在已經建立了連接配接的命名管道執行個體中,服務端程序就會得到一個指向該管道執行個體的句柄,這個句柄稱之為服務端句柄。

管道的通路方式相當于指定管道服務端句柄的讀寫通路,下表列出了可以使用 CreateNamedPipe 指定的每種通路方式的等效正常通路權限:

通路方式 通路權限
PIPE_ACCESS_INBOUND GENERIC_READ
PIPE_ACCESS_OUTBOUND GENERIC_WRITE
PIPE_ACCESS_DUPLEX GENERIC_READ | GENERIC_WRITE

如果管道伺服器使用 PIPE_ACCESS_INBOUND 建立管道,則該管道對于管道伺服器是隻讀的,對于管道用戶端是隻寫的。

如果管道伺服器使用 PIPE_ACCESS_OUTBOUND 建立管道,則該管道對于管道伺服器是隻寫的,對于管道用戶端是隻讀的。

用 PIPE_ACCESS_DUPLEX 建立的管道對于管道伺服器和管道用戶端都是可以讀/寫的。

同時,管道用戶端使用 CreateFile 函數連接配接到命名管道時必須在 dwDesiredAccess 參數中指定一個和管道服務端(建立管道時指定的通路模式)相相容的通路模式。

例如,當管道服務端建立管道時指定了 PIPE_ACCESS_OUTBOUND 通路模式,那麼,管道用戶端就必須指定 GENERIC_READ 通路模式。

更多内容内容可以參考,微軟官方說明:

https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipe-open-modes

在不過分追求底層邏輯的情況下,建立管道可以使用多種語言實作,包括 C、C++、C# 以及 powershell 等。

Powershell

$PipeSecurity = New-Object System.IO.Pipes.PipeSecurity$AccessRule = New-Object System.IO.Pipes.PipeAccessRule( "Everyone", "ReadWrite", "Allow" )$PipeSecurity.AddAccessRule($AccessRule) //設定權限$pipe = New-Object System.IO.Pipes.NamedPipeServerStream($pipename,"InOut",10, "Byte", "None", 1024, 1024, $PipeSecurity)//建立命名管道$pipe.WaitForConnection()$pipeReader = new-object System.IO.StreamReader($pipe)$Null = $pipereader.ReadToEnd() //讀取資料
           

以下為其他語言的建立方法:

C++

SECURITY_ATTRIBUTES sa ={0};    SECURITY_DESCRIPTOR sd={0};    InitializeSecurityDescriptor( &sd,SECURITY_DESCRIPTOR_REVISION);    SetSecurityDescriptorDacl(&sd,TRUE,NULL,FALSE);    sa.bInheritHandle =false;    sa.lpSecurityDescriptor =&sd;    sa.nLength =sizeof(sa);    //設定安全描述符為任意使用者均可通路    hPipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\Pipe"),                            PIPE_ACCESS_DUPLEX,                            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,   // FILE_FLAG_FIRST_PIPE_INSTANCE is not needed but forces CreateNamedPipe(..) to fail if the pipe already exists...                            1,                            1024 * 16,                            1024 * 16,                            NMPWAIT_USE_DEFAULT_WAIT,                            &sa);    while (hPipe != INVALID_HANDLE_VALUE)    {        if (ConnectNamedPipe(hPipe, NULL) != FALSE)        {            ....        }        DisconnectNamedPipe(hPipe);    }
           

C#

var server = new NamedPipeServerStream("PipesOfPiece");server.WaitForConnection();while (true){       ...}
           

以參考代碼 pipeserverimpersonate 為例,在建立命名管道的時候可以通過不同參數具體指定所需要的權限與功能

https://github.com/decoder-it/pipeserverimpersonate) 

執行參考代碼,管道已經被建立。

windows 檢視程式_淺談 windows 命名管道
windows 檢視程式_淺談 windows 命名管道

b、通路

用戶端通路(連接配接)服務端的過程如下:

用戶端程序調用 CreateFile 函數連接配接到一個正在等待連接配接的命名管道上,在這裡用戶端需要指定将要連接配接的命名管道的名稱,當 CreateFile 成功傳回後,客戶程序就得到了一個指向已經建立連接配接的命名管道執行個體的句柄,到這裡,伺服器程序的 ConnectNamedPipe 也就完成了其建立連接配接的任務。

簡單一點,可以通過指令行利用重定向符号直接把内容寫入到命名管道中 

echo “test” > \\.\pipe\dummypipe

通過 C# 類 NamedPipeClientStream 實作通路命名管道

NamedPipeClientStream pipeClient =new NamedPipeClientStream(".", "testpipe", PipeDirection.In))//System.Security.Principal.TokenImpersonationLevel.Delegation添加此參數可以允許服務端模拟用戶端powershell同樣調用NamedPipeClientStream實作通路命名管道 C++通路命名管道   HANDLE hPipe = CreateFile(TEXT("\\\\.\\pipe\\test"),                       GENERIC_READ | GENERIC_WRITE,                       0,                       NULL,                       OPEN_EXISTING,                       0,                       NULL);    if (hPipe != INVALID_HANDLE_VALUE)    {        ....        CloseHandle(hPipe);    }
           

但是需要注意的是,雖然命名管道支援跨計算機跨網的通路連接配接,但是會受到通路控制清單(ACL)或者說本地政策限制。

在 windows server 2003 及以下的版本中,預設開啟了匿名管道通信,但是之上的系統版本中(包括 windows 7)預設禁止匿名管道通信。

windows server 2003 的預設本地政策,預設允許部分管道匿名通路。

windows 檢視程式_淺談 windows 命名管道

windows server 2008 的預設本地政策,完全禁止匿名通路管道。

windows 檢視程式_淺談 windows 命名管道

也就是說,在高版本中,或者說禁止匿名通路的系統中,如果想要實作遠端管道通路,與管道進行通信,需要一個有效的身份進行驗證。比如建立 smb 連接配接,或者 IPC 連接配接等。

舉一個例子:

首先,我們在遠端計算機開啟一個管道

windows 檢視程式_淺談 windows 命名管道

使用本地計算機嘗試往管道内寫入内容,此時會提示使用者名密碼不正确

windows 檢視程式_淺談 windows 命名管道

但是,如果使用 smb 進行身份驗證後,建立了有效的連接配接,此時就可以通路指定管道進行資料互動。這裡回顯找不到指定檔案是由于遠端計算機腳本的原因,雖然報錯,但是在遠端端确實檢測到有資料輸入。

windows 檢視程式_淺談 windows 命名管道
windows 檢視程式_淺談 windows 命名管道

這也正是 Metasploit 中某些工具無法正常使用的原因。

以 ms17_010_command 為例,對 server 2008 嘗試攻擊

windows 檢視程式_淺談 windows 命名管道

傳回的資訊中就會提示不能找到可通路的管道

Unable to find accessible named pipe!

。而直接攻擊 server 2003 就可以。

那麼,在設定 2008 匿名通路,或提供有效的身份驗證後,就可以執行成功。

windows 檢視程式_淺談 windows 命名管道
c、連接配接測試工具

這類工具有很多,一下是通過網上随意找的一個小測試工具,有興趣的話可以自己試驗一下。

Server:

#include "iostream"#include "windows.h"using namespace std;#define PIPE_NAME L"\\\\.\\Pipe\\test"void main(){  char buffer[1024];  DWORD ReadNum;   HANDLE hPipe = CreateNamedPipe(PIPE_NAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 0, 0, 1000, NULL);  if (hPipe == INVALID_HANDLE_VALUE)  {    cout << "建立命名管道失敗!" << endl;    CloseHandle(hPipe);    return;  }   if (ConnectNamedPipe(hPipe, NULL) == FALSE)  {    cout << "與客戶機連接配接失敗!" << endl;    CloseHandle(hPipe);    return;  }  cout << "與客戶機連接配接成功!" << endl;   while (1)  {    if (ReadFile(hPipe, buffer, 1024, &ReadNum, NULL) == FALSE)    {      cout << "讀取資料失敗!" << endl;      break;    }          buffer[ReadNum] = 0;    cout << "讀取資料:" << buffer << endl;  }   cout << "關閉管道!" << endl;  CloseHandle(hPipe);  system("pause");}
           

Client:

#include "iostream"#include "windows.h"#include "stdio.h"using namespace std;#define PIPE_NAME L"\\\\.\\Pipe\\test"void  main(){  char buffer[1024];  DWORD WriteNum;   if (WaitNamedPipe(PIPE_NAME, NMPWAIT_WAIT_FOREVER) == FALSE)  {    cout << "等待命名管道執行個體失敗!" << endl;    return;  }   HANDLE hPipe = CreateFile(PIPE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);  if (hPipe == INVALID_HANDLE_VALUE)  {    cout << "建立命名管道失敗!" << endl;    CloseHandle(hPipe);    return;  }  cout << "與伺服器連接配接成功!" << endl;  while (1)  {    gets(buffer);//等待資料輸入    if (WriteFile(hPipe, buffer, strlen(buffer), &WriteNum, NULL) == FALSE)    {      cout << "資料寫入管道失敗!" << endl;      break;    }  }    cout << "關閉管道!" << endl;  CloseHandle(hPipe);  system("pause");}
           

實作效果:

windows 檢視程式_淺談 windows 命名管道

4、利用分析

在了解了命名管道的這些特性之後,就可以來看看命名管道有哪些具體利用了,常見的命名管道利用有以下三種,當然,如上文所說,在很多漏洞利用腳本當中也會有所涉及。

a、繞過防火牆

這裡說的防火牆,是指 windows 系統自帶的防火牆,類似于 UAC,需要使用者點選允許通路才可放行。

windows 檢視程式_淺談 windows 命名管道

當嘗試使用 

Bind()

 綁定一個 TCP Socket 時,Defender 就會自動彈窗提示是否允許此程式進行網絡連接配接,在高權限下,通過修改防火牆規則,可以輕松的繞過這一限制,但是,當權限不足時,就需要另外想辦法了。

這個時候我們還有另外的辦法就是利用命名管道,命名管道網絡通信使用了未加密的 SMB 協定(端口 445)或 DCE\RPC(端口 135)。在 Windows 中,通常預設允許 SMB 協定出入站(如果當年沒有因為 WannaCry 主動做政策限制的情況下),是以,如果有什麼功能或機制可以用于與外部機器進行通信的,SMB 協定無疑是一種很好的選擇。是以我們可以基于命名管道與外部機器進行通信,進而建立控制通道。

b、模拟令牌(system 權限)

這也是命名管道中常見的一種方法,一般可以用來提權操作,Metasploit 中的 getsystem 也就是這個原理,官方給出的内容為:

Technique 1 creates a named pipe from Meterpreter. It also creates and runs a service that runs cmd.exe /c echo “some data” >\.\pipe[random pipe here]. When the spawned cmd.exe connects to Meterpreter’s named pipe, Meterpreter has the opportunity to impersonate that security context. Impersonation of clients is a named pipes feature. The context of the service is SYSTEM, so when you impersonate it, you become SYSTEM.

大體意思也就是說,msf 會建立一個命名管道,然後建立一個服務去運作

cmd.exe /c echo “some data” >\\.\pipe\[random pipe here]

,當 cmd 連接配接到 Meterpreter 的明明管道的時候,因為服務是 system 權限,msf 也就得到了一個 system 的 shell。

使用之前的 ps 腳本也可以完成此操作。

具體過程可以參考以下兩篇文章:

https://www.anquanke.com/post/id/190207#h3-7
https://decoder.cloud/2019/03/06/windows-named-pipes-impersonation/

但是需要強調的是,該功能隻能作為本地使用,模拟令牌産生的使用者程序無法用于任何遠端認證。模拟用戶端産生程序,是通過提取目前程序 token 産生的,而 token 中隻存在 sid 和 acl 等資訊,其中不包含認證所需要的密碼、hash,是以隻能用于本地權限認證。如果用于遠端認證就會出現權限鑒定出錯的情況 失敗的情況。

如果用戶端采用 SECURITYDELEGATION 權限連接配接服務端,則允許服務端任意模拟用戶端權限,包括本地和遠端認證,但是根據官網文檔,服務端要接受 SECURITYDELEGATION 權限的委派。

開啟委派後也可以實作委派級别的模拟。具體就不在這裡展開了。

c、C2 信道

命名管道還常被用作 C2 信道,通訊執行指令。

windows 檢視程式_淺談 windows 命名管道

如圖所示,每個終端将為每個直接連接配接的子終端提供一個命名管道伺服器和一個命名管道用戶端。伺服器監聽管道名稱,并等待用戶端的連接配接。用戶端連接配接到特定主機名和管道名稱的伺服器,進而建立命名管道。管道的每一個終端都有從另一個終端讀取和寫入的能力,即,将 

Payload

 運作(注入)後,建立了自定義命名管道(作服務端),等待連接配接即可,這一過程被稱為 " 綁定 "(Bind)連接配接。

這種連接配接方式很常見,如 Metasploit 和 Cobalt Strike 都有類似功能。

三、參考資料

https://decoder.cloud/2019/03/06/windows-named-pipes-impersonation/ 

https://docs.microsoft.com/en-us/windows/win32/ipc/pipes

http://www.blakewatts.com/namedpipepaper.html

windows 檢視程式_淺談 windows 命名管道

繼續閱讀