首页 红蓝对抗-PPID欺骗
文章
取消

红蓝对抗-PPID欺骗

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_PROCESSlpValue参数是指向要使用的进程句柄的指针,而不是将调用进程用作正在创建的进程的父进程。要使用的进程必须具有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);

接下来我们使用mallocSTARTUPINFOEXlpAttributeList属性上分配内存区域。我们再次调用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 去实现

注:本文仅用于安全研究和学习之用

本文由作者按照 CC BY 4.0 进行授权