0X00 PPID欺骗介绍
在我们使用CreateProcess
API时,默认情况下,最后的进程将作为调用方的子进程生成。然而,PPID欺骗技术允许调用者更改派生进程的父进程。所以如果我们的beacon
运行在powershell
中,我们可以就将进程派生到一个完全不同的进程的子进程中,比如explorer.exe
。在默认情况下,大多数需要用户交互启动的程序都是由explorer.exe
生成的。
这将导致Sysmon
等应用程序记录新父级下的进程创建。当一个beacon
在一个不合法的进程中运行(例如横向移动或其他一些漏洞传递),并且进程创建事件会被杀软检测或被完全阻止,那么这个方法可以用来对抗这种检测。
0x01 进程、PID和PPID
进程:在 Windows
中,应用程序由一个或多个进程组成。简单来说,当前正在运行的程序的一部分称为进程。不同的应用程序可能会使用相同的进程(如cmd.exe
),并且为避免歧义,会分配一个整数来区分一个进程和另一个进程。该整数称为PID
。
PID:代表进程标识符 (PID
),它是正在运行的进程的数字表示。Windows
中通过 GetCurrentProcessID()
函数返回指定进程的 PID
。
父进程:父进程是可以派生多个子进程的进程。例如,命令explorer.exe /e,/root,"C:\WINDOWS\System32\cmd.exe"
将派生cmd.exe
作为父进程explorer.exe
的子进程。在代码中,父进程可以使用fork()
系统调用来派生子进程。
PPID:代表父进程标识符(PPID
),它是提供给父进程的数字表示形式。任何包含子进程的进程都存在父子关系。
0x02 技术实现
这个技术是在STARTUPINFOEX 结构中实现的,这个结构具有LPPROC_THEAD_ATTRIBUTE_LIST
属性。这允许我们向CreateProcess
调用传递附加属性。对于PPID欺骗,我们关注的PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
。lpValue
参数是指向要使用的进程句柄的指针,而不是将调用进程用作正在创建的进程的父进程。要使用的进程必须具有process_CREATE_process
访问权限。
下面用一段简短的代码来实现获取父进程PID和初始化
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <Windows.h>
#include <winternl.h>
int main(int argc, const char* argv[])
{
//获取父进程PID
DWORD parentPid = atoi(argv[1]);
//初始化
STARTUPINFOEX sie = { sizeof(sie) };
}
我们需要先知道所需的大小,然后分配一个内存区域来保存属性列表。这个列表可以有多个属性,但由于我们只对PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
感兴趣,因此大小为1。所以我们调用InitializeProcThreadAttributeList
并提供NULL目标
,但lpSize
变量将填充我们需要的大小。因为这个API返回bool
,所以这个调用也将始终返回FALSE
。
1
2
SIZE_T lpSize;
InitializeProcThreadAttributeList(NULL, 1, 0, &lpSize);
接下来我们使用malloc
在STARTUPINFOEX
的lpAttributeList
属性上分配内存区域。我们再次调用InitializeProcThreadAttributeList
,它应该返回TRUE
。
1
2
3
4
5
6
7
8
9
//为STARTUPINFOEX上的属性列表分配内存
sie.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)malloc(lpSize);
//再次调用InitializeProcThreadAttributeList,此时应返回TRUE
if (!InitializeProcThreadAttributeList(sie.lpAttributeList, 1, 0, &lpSize))
{
printf("InitializeProcThreadAttributeList failed. Error code: %d.\n", GetLastError());
return 0;
}
获取父进程的句柄并将其传递到UpdateProcThreadAttribute
的调用中。
1
2
3
4
5
6
7
8
9
//获取进程的句柄以充当父进程
HANDLE hParentProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, parentPid);
//调用UpdateProcThreadAttribute
if (!UpdateProcThreadAttribute(sie.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hParentProcess, sizeof(HANDLE), NULL, NULL))
{
printf("UpdateProcThreadAttribute failed. Error code: %d.\n", GetLastError());
return 0;
}
剩下要做的就是调用CreateProcess
,确保传递EXTENDED_STARTUPINFO_PRESENT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//调用CreateProcess并传递EXTENDED_STARTUPINFO_PRESENT
PROCESS_INFORMATION pi;
if (!CreateProcess(
L"C:\\Windows\\System32\\notepad.exe",
NULL,
0,
0,
FALSE,
EXTENDED_STARTUPINFO_PRESENT,
NULL,
L"C:\\Windows\\System32",
&sie.StartupInfo,
&pi))
{
printf("[-] CreateProcess failed. Error code: %d.\n", GetLastError());
return 0;
}
printf("[+] PID created: %d", pi.dwProcessId);
return 1;
0x03 实现效果
0x04 小结
本文介绍了PPID欺骗实现原理以及怎么用Windows API 去实现