Parent Process ID Spoofing

A method in which to arbitrarily set the parent process of a newly spawned process.


Why do we want to specify the parent of our process? Why do we care who spawned it? Using PPID spoofing, we enable ourselves to completely control the circumstances surrounding the processes we spawn, we can hide behind common parent-child relationships such as svchost.exe and runtimebroker.exe, we can escape suspicious relationships such and Word and PowerShell, and we can pick and choose exactly how our processes are viewed by the operating system.

Continuing this train of thought, why do we care about parent-child relationships within our processes? AV/EDR evasion is our biggest benefit in paying attention to them. Endpoint protection will typically block our processes from running if they are spawned under abnormal circumstances, e.g. Microsoft Word spawning PowerShell, as is done in macro-based payloads. If we can instead use our macro to spawn PowerShell from explorer.exe, we can be perceived as a much more legitimate process, as if a user had typed PowerShell into the search bar.

Implementation

This method can be broken down into three distinct points of knowledge: [Process ID], [Parent Process ID], and [PPID Spoofing].

Process ID (PID)

Every running process in modern Windows environments is assigned a unique number called a PID or Process ID. This number allows processes to be specifically identified and targeted by other systems such as debuggers since multiple processes with the same name can be spawned.

A quick way to visually find a PID is to use task manager on Windows 10.

A PID can also be found fairly easily programmatically with C++, credit to hlldz on GitHub for the base code used throughout.

First, we take a snapshot of the currently running processes.

HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

Next, we check if the first entry of the process list has been copied to the buffer, meaning our snapshot has been taken successfully.

if (Process32First(snapshot, &process)) {
        // Loop here
    }

Then, within the if statement, we loop through the snapshot comparing the name of the process with our desired name.

do {
    if (!wcscmp(process.szExeFile, L"notepad.exe"))
         break;
   } while (Process32Next(snapshot, &process));

Once we find the correct process, we break from the loop and return the PID from within the process object.

return process.th32ProcessID;

Finally, we can combine these code snippets into a single function to call whenever we need to find a PID.

DWORD getProcessID() {

    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    PROCESSENTRY32 process = { 0 };
    process.dwSize = sizeof(process);

    if (Process32First(snapshot, &process)) {
        do {
            if (!wcscmp(process.szExeFile, L"notepad.exe"))
                break;
        } while (Process32Next(snapshot, &process));
    }

    CloseHandle(snapshot);
    return process.th32ProcessID;
}

Parent Process ID (PPID)

After understanding how the Process ID works, the Parent Process ID is an incredibly simple continuation of that system. A PPID is simply the PID of the process that spawned the child process in question. For instance, when Notepad is launched in a typical scenario it will be spawned using Explorer.exe, making the Explorer process Notepad’s parent.

The Parent Process ID is a slightly more difficult attribute to identify but can be found using WMIC.

wmic process get processid,parentprocessid,executablepath|find "PID"

We can then take the provided PPID, and look through task manager to determine the parent process’s name.

The PPID can also be identified with a handy tool called Process Hacker.

PPID Spoofing

Finally, what everything has been leading up to, how to spoof the PPID of a newly spawned process. PPID spoofing is a built-in Windows utility so we can use the tools provided to us by the operating system.

You may be asking, “Why would Windows provide this functionality that is obviously perfect for Malware?”. And the answer is that it is actually a regularly used functionality by UAC (User Account Control). When a user requests an elevated process to be spawned, UAC takes over and spawns the elevated process with the SYSTEM user through a process such as svchost.exe rather than the true parent process such as the current user’s explorer.exe.

So how do we, a regular user, implement this process? In the remainder of the section, I will walk you through this process in C++.

The following code blocks below may seem complex, especially if you are unfamiliar with C++, but if you take some time to read the Microsoft Docs on the functions used, it becomes clear very quickly.

First, we call our getProcessID function, which we walked through earlier, and open a handle to our desired parent function. A handle simply put, is a pointer that allows access to a resource.

ph = OpenProcess(PROCESS_ALL_ACCESS, false, getProcessID())

Next, we initialize the attribute list to be used in process spawning and fill it with the desired information. In our case, we set the PROC_THREAD_ATTRIBUTE_PARENT_PROCESS flag which specifies that we will be passing a handle to an alternate parent process in the next parameter.

InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &st);
UpdateProcThreadAttribute(
       si.lpAttributeList,                         // ptr to attribute list
       0,                                          // No flags
       PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,       // Following parameter will be a handle to a process to use rather than calling process for proc creation
       &ph,                                        // Handle to process we would like to set as the "parent process"
       sizeof(ph),                                 // Size of prior attribute
       NULL, NULL                                  // Reserved, NULL req
);

Lastly, we create our new process. We must pass the following parameters to the CreateProcessA function, the executable we want to call, the EXTENDED_STARTUPINFO_PRESENT flag telling the system that we are including our own startup info, and a pointer to our startup info structure. The function will also return a handle to our newly created process into the final parameter.

    CreateProcessA(
        "c:\\Windows\\System32\\notepad.exe",           // Module to be called (Path)
        NULL,                                           // Command line Arguments
        NULL,                                           // Process handle not inheritable
        NULL,                                           // Thread handle not inheritable
        TRUE,                                           // Set handle inheritance to FALSE
        EXTENDED_STARTUPINFO_PRESENT,                   // Creation flag: Tells it the process is created with the extended startup info
        NULL,                                           // Use parent's environment block
        NULL,                                           // Use parent's starting directory 
        reinterpret_cast<LPSTARTUPINFOA>(&si),          // Pointer to STARTUPINFOEXA structure
        &pi
    );

For the full code sample with comments, visit my GitHub.

With this code, we can now call Notepad.exe from our desired parent process. For this example, I will call Notepad.exe from OneDrive.exe

Tracking PPID Spoofing and Process Injection

I will be continuing this topic next week with a post focused on tracking PPID spoofing with ETW (Event Tracing for Windows) and injecting a payload into our newly spawned (and spoofed) process.