Updated 2026-06-27 By Dimitry Iacoviuc ← Back to Blog

Windows Privilege Escalation: Parent PID Spoofing with SeDebugPrivilege

When the textbook techniques fail, understanding Windows internals saves the day.


Introduction

During a recent penetration testing engagement, I encountered a hardened Windows machine where none of the standard privilege escalation techniques worked. No writable services, no unquoted paths, no Potato-friendly SeImpersonatePrivilege, no stored credentials, no kernel exploits. The box was locked down tight.

What I did have was a low-privileged domain user account with one unusual privilege: SeDebugPrivilege. Not a local admin. Not a member of any privileged group. Just one extra privilege — quietly assigned through Group Policy — that most pentesters would glance past in the whoami /priv output. That single privilege, combined with an understanding of how Windows process creation works, gave me a SYSTEM shell in under a minute.

This article covers the technique known as Parent PID Spoofing — what it is, why it works, and how to use it in practice. This technique is well-documented in offensive security research but is often absent from mainstream certification courses, making it a valuable addition to any pentester's toolkit.


The Setup

After gaining initial access to the target, I had a shell as a regular domain user — not a local administrator. The standard privilege escalation playbook had been exhausted: no writable services, no unquoted paths, no SeImpersonatePrivilege for Potato exploits, no stored credentials, no kernel exploits. WinPEAS showed nothing actionable. The box appeared locked down.

Out of habit, I ran the one command that should always be checked early:

C:\> whoami /priv

Privilege Name                  Description                    State
=============================== ============================== ========
SeDebugPrivilege                Debug programs                 Enabled
SeChangeNotifyPrivilege         Bypass traverse checking       Enabled
SeIncreaseWorkingSetPrivilege   Increase a process working set Enabled

SeDebugPrivilege — on a non-admin account. This was a misconfiguration. Someone had assigned debug privileges to this user or a group it belongs to through Local Security Policy or Group Policy, probably for "development" or "troubleshooting" purposes. They likely didn't realize they had just handed out the keys to SYSTEM.


Understanding SeDebugPrivilege

Windows uses a privilege model to control what actions a process can perform, independent of standard file/object permissions. SeDebugPrivilege is one of the most powerful privileges in the system. Its intended purpose is to allow developers to debug system processes, but its security implications go far beyond debugging.

When a process holds SeDebugPrivilege, it can:

  • Open a handle to any process on the system, regardless of the process owner's security context
  • Read and write the memory of any process, including SYSTEM processes
  • Inject code into any running process
  • Access tokens of any running process

In practical terms, SeDebugPrivilege gives you the ability to interact with SYSTEM-level processes as if you were SYSTEM yourself. The kernel's normal access control checks on process handles are effectively bypassed.

Who Has SeDebugPrivilege?

By default, only members of the local Administrators group are assigned SeDebugPrivilege. For administrators, there's an additional nuance involving User Account Control (UAC): the privilege is only active after UAC elevation ("Run as Administrator"). In a non-elevated admin session, SeDebugPrivilege exists but is disabled.

However — and this is the critical finding from my engagement — SeDebugPrivilege can be assigned to any user or group through Local Security Policy or Group Policy:

Local Security Policy → Local Policies → User Rights Assignment → "Debug programs"

When a non-administrator account has SeDebugPrivilege, there is no UAC barrier. The privilege is simply present and enabled in every session. No elevation required. No password prompt. The user opens a command prompt and has full debug access to every process on the system.

This is the scenario I encountered: a regular domain user with SeDebugPrivilege granted through policy. It's more common than you might expect — development teams, help desk accounts, and service accounts are sometimes given this privilege by administrators who don't understand its implications. In Active Directory environments, a single Group Policy can push this misconfiguration to hundreds of machines.

Why This Is Worse Than It Sounds

Administrators seeing "Debug programs" in a policy setting might assume it only allows attaching a debugger — a developer convenience with limited security impact. In reality, SeDebugPrivilege bypasses the kernel's process access control entirely. It is functionally equivalent to granting SYSTEM access to anyone who knows how to use it. There is no legitimate reason for a non-administrator account to hold this privilege in a production environment.


The Technique: Parent PID Spoofing

How Windows Process Creation Works

When a new process is created in Windows, it inherits certain properties from its parent process. Normally, the parent is the process that called CreateProcess — if you type notepad.exe in cmd.exe, then cmd.exe is the parent and notepad.exe is the child.

However, Windows provides an extended attribute called PROC_THREAD_ATTRIBUTE_PARENT_PROCESS that can be passed through the STARTUPINFOEX structure when calling CreateProcessA/W. This attribute allows the caller to specify an arbitrary parent process. The new child process will then inherit security properties from the specified parent instead of the actual calling process.

This is a legitimate, documented Windows API feature. It was designed for scenarios like the Windows Error Reporting service, which needs to create processes in the context of the crashing application.

The Attack

The attack follows from these two facts:

  1. SeDebugPrivilege allows us to open a handle to any process, including those running as SYSTEM
  2. CreateProcess with PROC_THREAD_ATTRIBUTE_PARENT_PROCESS creates a child that inherits the parent's token

Combining them:

  1. Enumerate processes running as NT AUTHORITY\SYSTEM
  2. Open a handle to one of these processes (SeDebugPrivilege makes this possible)
  3. Call CreateProcess specifying that SYSTEM process as the parent
  4. The new process inherits the SYSTEM token
  5. We now have a SYSTEM shell

The entire attack can be performed with a single PowerShell script and takes less than a second to execute.


Practical Exploitation

Finding SYSTEM Processes

First, identify processes running as SYSTEM and note their PIDs:

# PowerShell — list SYSTEM processes:
Get-Process | Where-Object { $_.SessionId -eq 0 } | Format-Table Name, Id -AutoSize

# Or from CMD:
tasklist /FI "USERNAME eq NT AUTHORITY\SYSTEM"

Good target processes — these typically run as SYSTEM and are not Protected Process Light (PPL):

ProcessTypical PID RangeNotes
winlogon.exeLowManages logon sessions. Reliable target.
services.exeLowService Control Manager. Usually works.
svchost.exeVariousMultiple instances as SYSTEM. Many to choose from.
spoolsv.exeMediumPrint Spooler. Works if Spooler is running.
lsass.exeLowWorks on older systems. PPL-protected on newer ones.

Processes to Avoid

Some SYSTEM processes are protected by Protected Process Light (PPL), a kernel-level protection that prevents even SeDebugPrivilege holders from opening full-access handles:

ProcessWhy It's Protected
csrss.exeCore subsystem, always PPL
smss.exeSession Manager, always PPL
MsMpEng.exeWindows Defender
lsass.exeProtected if RunAsPPL is enabled via registry
svchost.exe (some)Certain service hosts may have PPL

If OpenProcess fails with "Access Denied" on a SYSTEM process despite having SeDebugPrivilege, that process is likely PPL-protected. Simply try another PID.

Using psgetsys.ps1

The psgetsys.ps1 script by Andrea Pierini (decoder-it) implements the Parent PID Spoofing technique in pure PowerShell. It calls the Windows API to create a process with a spoofed parent.

# Load the script:
. .\psgetsys.ps1

# Spawn cmd.exe as SYSTEM using winlogon.exe (PID example: 624) as parent:
ImpersonateFromParentPid -ppid 624 -command "C:\Windows\System32\cmd.exe" -cmdargs ""

If the call succeeds, a new cmd.exe window opens running as SYSTEM:

[+] Got Handle for ppid: 624
[+] Updated proc attribute list
[+] Starting C:\Windows\System32\cmd.exe ...True - pid: 5488 - Last error: 0

What If the First PID Doesn't Work?

During my engagement, the first three processes I tried were PPL-protected or otherwise inaccessible. The fourth — a svchost.exe instance — worked. The approach is:

  1. List all SYSTEM processes
  2. Try winlogon.exe first (most reliable)
  3. If it fails, try services.exe
  4. If it fails, try different svchost.exe instances
  5. If it fails, try spoolsv.exe

In practice, at least one of these will work on any Windows system.

Alternative: Direct Token Manipulation

For situations where psgetsys.ps1 isn't available, the same concept can be achieved through other tools:

Mimikatz (if already on the target):

mimikatz# privilege::debug
mimikatz# token::elevate
mimikatz# sekurlsa::logonpasswords

Meterpreter (if Metasploit is in play):

meterpreter> steal_token PID

Custom C# or C code using OpenProcess, OpenProcessToken, DuplicateTokenEx, and CreateProcessWithTokenW. Multiple public implementations exist on GitHub.


SeDebugPrivilege vs SeImpersonatePrivilege

Most pentesters are familiar with the SeImpersonatePrivilege escalation path through the "Potato" family of exploits. SeDebugPrivilege escalation is less commonly discussed but equally powerful. Here's how they compare:

AspectSeImpersonatePrivilegeSeDebugPrivilege
Common onIIS app pools, SQL servicesLocal admins (after UAC), misconfigured non-admin accounts
TechniquePotato exploits (coerce SYSTEM auth)Parent PID spoofing (inherit SYSTEM token)
Requires admin?No (service accounts have it)No (if assigned via policy to non-admins)
DependencySpecific Potato for specific OS versionUniversal — works on any Windows version
External toolsGodPotato, PrintSpoofer, JuicyPotatopsgetsys.ps1 (pure PowerShell, ~50 lines)
Failure modesPrint Spooler disabled, COM blocked, .NET mismatchPPL-protected processes (try another PID)
SpeedFast (seconds)Fast (seconds)
DetectionModerate (COM/named pipe activity)Low (standard process creation API)

In offensive engagements, checking both privileges should be standard:

whoami /priv → SeImpersonatePrivilege?  → Potato exploits
whoami /priv → SeDebugPrivilege?        → Parent PID spoofing

Detection and Defense

For Blue Teams

Parent PID spoofing creates a visible anomaly: a process whose parent doesn't match the expected process tree. For example, cmd.exe appearing as a child of winlogon.exe is unusual and detectable.

Detection strategies:

  • Process tree monitoring: Flag processes whose parent PID doesn't match the expected execution chain. cmd.exe or powershell.exe spawned from winlogon.exe or services.exe is a strong indicator.
  • ETW (Event Tracing for Windows): Monitor for CreateProcess calls with PROC_THREAD_ATTRIBUTE_PARENT_PROCESS set.
  • Sysmon Event ID 1: Process creation events include the ParentProcessId field. Cross-reference with expected parent-child relationships.
  • Credential Guard: Reduces the value of token theft by isolating credential material in a virtualization-based security (VBS) container.

Mitigation

  • Minimize SeDebugPrivilege assignments: Audit Local Security Policy and GPOs. Only accounts that genuinely require debug access should have this privilege.
  • Enable PPL for LSASS: Set RunAsPPL in the registry to protect lsass.exe from handle-based attacks.
  • Use Protected Users group: Members of this AD group have additional protections against credential theft.
  • Enforce UAC properly: Don't disable UAC on workstations. The filtered token model prevents SeDebugPrivilege from being active in non-elevated contexts.

Key Takeaways

  1. Always check whoami /priv early — even on non-admin accounts. SeDebugPrivilege on a regular user is an immediate, guaranteed path to SYSTEM. Don't skip this check just because you're not an administrator.

  2. SeDebugPrivilege on a non-admin account is a critical misconfiguration — it means someone assigned "Debug programs" through policy without understanding the implications. In a real engagement, this should be flagged as a high-severity finding in your report.

  3. Parent PID Spoofing is universal — unlike Potato exploits which vary by OS version, this technique works on any Windows version from Vista through Server 2025. The only variable is which SYSTEM process isn't PPL-protected.

  4. PPL is the only real obstacle — and it's easily bypassed by targeting unprotected SYSTEM processes. Most systems have several available. If the first PID fails, try the next one.

  5. The technique is fast and quiet — no network traffic, no COM activation, no named pipe creation. A single CreateProcess call with an extended attribute. Many EDR solutions don't flag it.

  6. Always check both SeImpersonate and SeDebug — they offer parallel escalation paths. If one fails, the other might succeed. On a hardened machine where Potato exploits are blocked, SeDebugPrivilege with Parent PID spoofing can be the only way up.


References


Published on the Funway Interactive Blog — June 2026

About the author
Dimitry Iacoviuc
Founder & Principal Engineer

Dimitry Iacoviuc is the founder of Funway Interactive SRL — a professional software engineer and security expert with 25 years of battle-tested experience across software engineering, 3D web, and video streaming. He leads penetration testing and security engagements for clients worldwide. Based in Chisinau, Moldova.

← Previous
Find-HijackDLL: Discovering DLL Hijacking Without Procmon

Ready to work with Funway Interactive?

Get in Touch More Posts