Hardening

SeImpersonate and Potato Attacks: Blocking Token Abuse

22 September 2026 9 min read

SeImpersonatePrivilege is the Windows user right that lets a process take on the security context of a client after that client authenticates against it. IIS application pool workers, SQL Server Reporting Services, and most COM and DCOM server processes carry it by default, because impersonation is how a request gets served as the calling user instead of as the service itself. That same right is the load-bearing primitive behind the Potato family of local privilege escalation tools: get code execution inside a service account that holds SeImpersonatePrivilege, coerce a SYSTEM-level token into arriving on the box, capture it, and impersonate your way from a low-value web shell to NT AUTHORITYSYSTEM. Closing this path is not a matter of waiting for the next patch, since new Potato variants keep surfacing years after Microsoft’s original mitigations. It is a matter of shrinking who holds the privilege and watching what happens when someone tries to use it anyway.

Why service accounts hold SeImpersonatePrivilege and what that buys an attacker

Open secpol.msc, go to Local Policies, User Rights Assignment, and look at “Impersonate a client after authentication.” By default it is assigned to Administrators, LOCAL SERVICE, NETWORK SERVICE, and the built-in SERVICE group. The last one is the detail most admins miss: any account, including a low-privileged custom domain account, picks up the SERVICE well-known SID the moment it logs on with logon type 5, meaning it is configured to run as a Windows service. It does not matter whether that service ever calls an impersonation API. The privilege arrives automatically with the logon type, not with a deliberate grant.

That default is what makes the Potato attacks reliable. With SeImpersonatePrivilege, a process can call ImpersonateNamedPipeClient after tricking a SYSTEM-level component into connecting to a named pipe it controls, then DuplicateTokenEx to turn the impersonation token into a primary token, then CreateProcessWithTokenW to spawn a new process running as SYSTEM. The attacker does not need to steal a password or crack a hash. They need one thing: a foothold in any process whose service account has the right, plus a way to coerce a privileged client into authenticating locally. RPC and COM activation supply that coercion, which is why the entire attack class is often summarized as “local RPC coercion plus token capture” rather than a single bug.

The potato family in one table: what each variant abuses and where it still works

Treat the table below as a reference for triage, not a shopping list. The mechanism column matters more than the name, because the underlying primitive, SeImpersonatePrivilege plus local token capture, is the same across every row.

Variant Mechanism abused Works on a current, fully patched host?
RottenPotato DCOM OXID resolver on TCP 135 tricked into authenticating to a local listener No, broken by DCOM activation hardening; historical reference only
JuicyPotato Same OXID resolver trick using specific CLSIDs and a configurable listener port No on current Server 2019/2022 builds; the CLSID list is version specific
RoguePotato A fake OXID resolver reached over a redirected port when 135 is filtered locally Only if outbound RPC from the host is not restricted
PrintSpoofer Print Spooler RPC named pipe (spoolss) coercion Yes, if the Spooler service is running
GodPotato RPCSS COM marshaling abuse Yes, works on fully patched Server 2019, 2022, and current client builds
EfsPotato / SharpEfsPotato MS-EFSR (Encrypting File System RPC) interface coercion Yes, EFSRPC cannot be disabled without breaking EFS

The pattern in the last column is the point. Microsoft’s DCOM activation changes killed the first two rows outright, which is real progress, but every row below it targets a different RPC interface that cannot simply be turned off, because Print Spooler, RPCSS, and EFSRPC all serve legitimate purposes on a general-purpose Windows server. Coercion techniques that fingerprint the specific RPC interface and running services against your patch level, the kind of check a vulnerability management platform such as SITEY runs during validation, are more useful here than a blanket “apply all updates” recommendation, because patching alone will not close the newer rows.

Which accounts genuinely need the privilege and which inherited it by accident

Enumerate before you touch anything. On each server, pull the service list and its logon accounts with Get-WmiObject win32_service | Select Name, StartName, and pull IIS app pool identities with Get-IISAppPool | Select Name, ProcessModel. Cross-reference against Scheduled Tasks configured to “run whether user is logged on or not,” since those also use a service-style logon. Doing this consistently requires knowing what is actually running on each box in the first place, which is the same data an asset inventory keeps current for you instead of relying on a spreadsheet from the last audit.

Genuine need looks like this: an IIS worker process serving Windows-authenticated content that has to pass the calling user’s identity to a backend, SQL Server Reporting or Analysis Services performing Kerberos delegation, or a custom application that explicitly calls impersonation APIs to enforce per-user authorization. Accidental inheritance looks like this: a backup agent, a monitoring collector, or a single-purpose batch job that was set up to “log on as a service” purely for convenience, runs under one static identity for its entire life, and never impersonates anyone. These are the accounts worth moving first, because they carry the exposure without using the capability.

The concrete fix for the accidental group is a logon-type change, not a privilege removal, since SeImpersonatePrivilege cannot be stripped from the SERVICE group without breaking every legitimate service on the box. Convert the job to a Scheduled Task running under “Run whether user is logged on or not” with logon type 4 (batch) instead of a Windows service with logon type 5. A standard, non-administrative domain account running as a scheduled task under batch logon does not receive the SERVICE SID and therefore does not inherit SeImpersonatePrivilege at all, which removes it from the Potato attack surface entirely rather than just reducing its usefulness.

Hardening IIS, SQL and custom service identities with least-privilege accounts and gMSA

You cannot remove SeImpersonatePrivilege from IIS worker processes or SQL Server without breaking Windows authentication passthrough and delegation, so hardening here is about reducing what a SYSTEM foothold on that box actually reaches, not about preventing SYSTEM in the first place.

  • Give each site or application its own application pool identity instead of sharing one identity across multiple apps, so a compromise in one app does not automatically expose secrets or connection strings belonging to another.
  • Use group Managed Service Accounts (gMSA) for SQL Server and for IIS pools that need domain resources, instead of a static domain account with a typed password. The gMSA’s password rotates automatically and is never stored in a way that survives credential dumping, so a SYSTEM-level compromise on that host does not hand the attacker a reusable domain credential.
  • Configure the SQL Server service account through SQL Server Configuration Manager, not the Services console, and grant only the rights the setup wizard actually requests, such as “Perform volume maintenance tasks” or “Lock pages in memory,” rather than local administrator membership as a shortcut.
  • Push a GPO-based baseline for “Impersonate a client after authentication” that lists only Administrators, LOCAL SERVICE, NETWORK SERVICE, SERVICE, and any explicitly approved gMSA, and use Restricted Groups or a scheduled comparison to catch drift from that baseline.

Complementary controls: RPC/COM restrictions, patch level, and app-level isolation

None of the following closes the SeImpersonatePrivilege gap on its own, but each one removes a coercion path or limits the blast radius once SYSTEM is reached.

  • Stop and disable Print Spooler on servers that do not print: Stop-Service Spooler; Set-Service Spooler -StartupType Disabled. This alone removes PrintSpoofer and other spoolss-based coercion from the table.
  • Restrict outbound and inter-segment TCP 135 (the RPC endpoint mapper) at the host firewall between servers that have no legitimate DCOM traffic to exchange. This is what closes RoguePotato’s fallback path once the direct OXID resolver trick is blocked.
  • Set the LAN Manager authentication level to “Send NTLMv2 response only, refuse LM and NTLM” (registry value LmCompatibilityLevel set to 5) and enforce SMB signing. This does not stop local token impersonation, but it reduces the value of the same coercion primitive when it is aimed at NTLM relay instead.
  • Treat patch level as a floor, not a ceiling. Current builds close the OXID resolver based variants, but GodPotato and the EFSRPC-based tools work against fully patched Server 2019 and 2022, so patch management alone is not the finish line for this attack class.
  • Keep internet-facing web roles off the same host as domain administration tools, saved RDP sessions, or cached scheduled task credentials, so a SYSTEM compromise on the web tier does not hand over material useful for lateral movement. Where the platform supports it, enable Credential Guard so that even a SYSTEM-level attacker cannot extract reusable domain secrets from LSASS after the fact.

Auditing privilege assignments across servers on a schedule

A one-time cleanup drifts within a quarter, because new services, new scheduled tasks, and new application pools get created after the audit ends. Export the current assignment with secedit /export /cfg baseline.inf /areas USER_RIGHTS and store it as your known-good baseline per server role. To check for drift across a fleet, run something close to this over WinRM:

Invoke-Command -ComputerName (Get-Content serverlist.txt) -ScriptBlock { secedit /export /cfg “$env:tempsec.cfg” /areas USER_RIGHTS; Select-String -Path “$env:tempsec.cfg” -Pattern ‘SeImpersonatePrivilege’ }

Compare the returned SID list against the baseline and flag anything beyond Administrators, LOCAL SERVICE, NETWORK SERVICE, SERVICE, and your explicitly approved gMSA accounts. Any new entry should trigger a review before the next change window, not get discovered a quarter later during a compliance audit. Run the check weekly at minimum, and immediately after provisioning a new host from a golden image, since the image’s own baseline is what most hosts will inherit going forward.

This is also where the audit and the remediation have to stay connected. Disabling Spooler or moving a job from a service logon to a scheduled task is only a hardening step until it is verified on the actual host, not just marked closed in a ticket. Platforms built for continuous vulnerability and configuration checks, SITEY among them, can run this privilege-assignment diff as a recurring scan across the fleet and use automated retest to confirm the fix held on the specific host, instead of relying on someone re-running the script by hand or trusting that the change ticket was executed as written. The mechanism matters more than the tool: a check that only runs once at audit time will always miss the service that got added the following month.

For environments mapped to ISO 27001 or SOC 2, this privilege list doubles as evidence for access control objectives around least privilege and periodic access review. Compliance mapping that ties a specific finding to a specific control gives you an artifact you already need for the audit, rather than a spreadsheet built from scratch each cycle.

About SITEY

SITEY is an autonomous vulnerability management platform. It discovers, validates, prioritizes, remediates and re-tests vulnerabilities through an eight-phase automated pipeline, unifying output from 17 integrated scanners. SITEY is self-hosted: it runs in your own infrastructure and your findings are stored there. Outbound connections are limited to licence activation and the optional services you enable, such as an AI provider, CVE enrichment and patch catalogues. Pricing is 599 USD per month or 5,999 USD for a perpetual lifetime license. See pricing or how the platform works.

SITEY closes the loop, not just the report.Discover, validate, fix and verify in your own infrastructure.

See pricing