Skip to Main Content
May 30, 2023

PPID Spoofing: It’s Really this Easy to Fake Your Parent

Written by Scott Nusbaum

1 New Blog Series on Common Malware Tactics and Tricks

This will be the first post in a series of blogs covering some common malware tactics and tricks. The following list is of topics that will be discussed in these blogs. However, feel free to reach out if there is topic that is not on the list that you would like to read about.

  • PPID Spoofing
  • Process Hollowing
  • DLL Hollowing
  • Thread Queue APC Injections
  • Reflective DLL Injection
  • DLL Injection SetWindowsHookExA
  • Shellcode Injection by Mapping Sections
  • Shellcode Execution with CreateThreadpoolWait
  • Syscall Basics
  • Patching AMSI
  • Windows NamedPipes
  • API Hooking
  • API Hashing for Hiding Functions
  • Hooking Import Address Table (IAT)

Each post will cover the following questions about the focus topic:

  • What is it?
  • How does it work?
  • What do the attackers gain?
  • How can we identify and defend against it?
  • Code Demonstration in C# and C
  • Walk through Ghidra disassembly

1.1      What is PPID Spoofing?

Parent Process ID (PPID) Spoofing is a technique used to aid in hiding malicious code from identification. This technique will falsify the PPID of the current executable to be that of any arbitrary process accessible to the current user. By falsifying this PPID, it would appear to any investigator that the current execution belongs or has been started by another benign process. During Incident Response cases, one (1) of the first tasks is to identify if any executable is having an odd or out of place parent. PPID hinders this by making the executable appear to be started by an acceptable process. An example would be if an executable was started by an Excel.exe file, which would raise suspicions of the investigator and warrant further analysis. However, if the process was started by svchost.exe, it would most likely not be immediately seen as suspicious.

1.2      How does PPID Spoofing work?

Let’s discuss how the PPID Spoofing works on a high level for Windows systems, in later sections we will get into detailed code samples. The first thing that needs to be done is to identify the parent ID that we want to spoof. This should be done by knowing what is running on that target system and being able to identify what is normal. Once a target process has been identified and the potential parents PID is captured, then we need to setup a list of attributes for the process and its threads. This will then be modified by inserting the PID of the parent we would like to spoof. The final step is to create a new process with this updated attribute list.

1.3      What do the attackers gain?

The main purpose of PPID spoofing is to hide, this includes hiding the executable that implemented this technique and hiding the origin of its execution. We already saw the example of having an Office document as the parent to the executable as being a red flag. When the attackers gain access through a web vulnerability, they do not want the parent process being w3wp.exe, as this points directly to the web service as the source of the compromise and would want to protect that information (In case they lose access to their malware and need to gain access to the environment again).

1.4      How can we identify and defend against PPID Spoofing?

A limiting factor of PPID Spoofing is that it can only spoof PIDs that the current user has permissions to access/modify. So, if the current user is a low-level user, then they can only spoof one (1) of their own processes. On a live system, Event Tracing can be enabled to monitor the start-up of the process to detect PPID Spoofing. However, this doesn't help for an executable that is already running or has already executed and currently being analyzed through triage data. For more information on Event Tracing, see https://www.picussecurity.com/resource/blog/how-to-detect-parent-pid-ppid-spoofing-attacks. Elastic.co has written a great blog on how to query and set rules to detect the execution of programs that use PPID Spoofing.

1.5      Code Demonstration in C# and C

To demonstrate how to implement PPID Spoofing, we will discuss a sample written in C and another in C#.

We will start with the example in C. 

Line 1: Gets a handle to the process we want to be our new parent

MAXIMUM_ALLOWED(0x2000000) is the desired permissions, and the <PID> will be replaced with the PID of the parent process. This can be done differently by searching for processes by name, as performed in the C# sample below. Attackers can only gain access to a handle on the process that they have permissions to.

Line 2: Used to get the size of the Attribute list

Line 3: Allocates a new section of memory for the Attribute List

Line 4: Copies the Attribute List into the newly allocated memory

Line 5: Modifies the Attribute List to include the handle to the parent process obtained in Line 1

Line 6: Sets the Startup info size

Line 7: Launches a new instance of Notepad with the modified startup info that contains the new parent handle

1) HANDLE parentProcessHandle = OpenProcess(
                   MAXIMUM_ALLOWED,
                   false,
                   <PID>);
 2) InitializeProcThreadAttributeList(
                   NULL,
                   1,
                   0,
                   &attributeSize);
 3) si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(
                   GetProcessHeap(),
                   0,
                   attributeSize);
 4) InitializeProcThreadAttributeList(
                   si.lpAttributeList,
                   1,
                   0,
                   &attributeSize);
 5) UpdateProcThreadAttribute(
                   si.lpAttributeList,
                   0,
                   PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
                   &parentProcessHandle,
                   sizeof(HANDLE),
                   NULL,
                   NULL);
 6) si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
 7) CreateProcessA(
                   NULL,
                  (LPSTR)"notepad",
                  NULL,
                  NULL,
                  FALSE,
                  EXTENDED_STARTUPINFO_PRESENT,
                  NULL,
                  NULL,
                  &si.StartupInfo,
                  &pi);

In the C# example below, we will basically do the same steps described in Lines 1-7 (Most of the Windows API calls are the same, too). However, there is a lot more code involved and much of it is used to setup and define the structures needed to make the API calls.

For example, there is a class called Win32, which is not displayed due to its length, that is used to define structures needed by the windows API.

Line 8: Call the InitializeProcThreadAttributeList, just like we did in the C example which returns the size of the attribute list in the lpSize variable

Line 9: Allocates the memory for the Attribute List

Line 14: Populates the list of attributes

Lines 16 to 30: Varies from the C example above, in that the C# program attempts to locate a currently running process with the names of any of the following: "explorer", "Services", or "svchosts"

Line 23: Gets the parent processes handle using Process.GetProcessByname rather than the OpenProcess command from the C example

Lines 31 to 37: Check the Parent Handle is valid then copies its process ID and stores it into the attributes structure

Line 36: Update the list of attributes to include the handle to the parent process

Line 39: A new process is created with the new startup information, causing the new process to have the spoofed parent ID

1) startInfoEx.StartupInfo.cb = (uint)Marshal.SizeOf(startInfoEx);
 2)
 3) var processSecurity = new Win32.SECURITY_ATTRIBUTES();
 4) var threadSecurity = new Win32.SECURITY_ATTRIBUTES();
 5) processSecurity.nLength = Marshal.SizeOf(processSecurity);
 6) threadSecurity.nLength = Marshal.SizeOf(threadSecurity);
 7) var lpSize = IntPtr.Zero;
 8) Win32.InitializeProcThreadAttributeList(
    IntPtr.Zero,
    2,
    0,
    ref lpSize);
 9) startInfoEx.lpAttributeList = Marshal.AllocHGlobal(lpSize);
10) Win32.InitializeProcThreadAttributeList(
    startInfoEx.lpAttributeList,
    2,
    0,
    ref lpSize);
11)
12) Marshal.WriteIntPtr(
    lpValue,
    new IntPtr((long)Win32.BinarySignaturePolicy.BLOCK_NON_MICROSOFT_BINARIES_ALLOW_STORE));
13)
14) Win32.UpdateProcThreadAttribute(
    startInfoEx.lpAttributeList,
    0,
    (IntPtr)Win32.ProcThreadAttribute.MITIGATION_POLICY,
    lpValue,
    (IntPtr)IntPtr.Size,
    IntPtr.Zero,
    IntPtr.Zero
    );
15)
16) var parentHandle = IntPtr.Zero;
17) string[] processes = {"explorer", "services","svchosts"};
18) foreach (string process in processes)
19) {
20)    try
21)     {
22)         Console.WriteLine("trying Parent:: " + process);
23)         parentHandle = Process.GetProcessesByName(process)[0].Handle;
24)     }
25)     catch (Exception e)
26)     {
27)         continue;
28)     }
29)     break;
30) }
31) if (parentHandle != IntPtr.Zero)
32) {
33)     lpValue = Marshal.AllocHGlobal(IntPtr.Size);
34)     Marshal.WriteIntPtr(lpValue, parentHandle);
35)
36)     Win32.UpdateProcThreadAttribute(
    startInfoEx.lpAttributeList,
    0,
     (IntPtr)Win32.ProcThreadAttribute.PARENT_PROCESS,
    lpValue,
     (IntPtr)IntPtr.Size,
    IntPtr.Zero,
    IntPtr.Zero
    );
37) }
38)
39) Win32.CreateProcess(
    null,
    "notepad",
        ref processSecurity,
    ref threadSecurity,
    false,
    Win32.CreationFlags.ExtendedStartupInfoPresent | Win32.CreationFlags.CreateSuspended,
    IntPtr.Zero,
    null,
    ref startInfoEx,
    out processInfo
    );

 1.6      Reversing the code

The C code discussed earlier was compiled into a Windows 64 bit executable using MinGW, then disassembled and decompiled with Ghidra. As you can see below, the Ghidra generated source code is a very close match to the original.

Figure 1 - Ghidra Generated Code for C Program

Reversing most C# code is simple if you use the tool, dnSpy. There are methods to hide or corrupt the .exe so that dnSpy cannot decompile it but for the most part, attackers do not go to that extent.

To load the executable in dnSpy, simply drag and drop it onto the left pane. Once loaded, the pane will provide a tree listing of the components of the .exe.

Figure 2 - DNSpy's Executable Breakdown

The listing shows the Main(string[]) function. Clicking on this function will start the decompilation and the output will be display on the right pane. Scrolling down to line 802, dnSpy provides almost the same code as shown above.

Figure 3 - DNSpy's Decompilation of the C# Executable

1.7      Conclusion

The technique of PPID Spoofing is not very complicated and is useful to attackers wishing to obfuscate their attack process. As shown above, the implementation is fairly easy and although there are detections for this type of obfuscation, most of them are disabled by default or require custom rules to be written in order to detect these actions.