WARMUP — OS Internals & Privilege Escalation (Windows + Linux)

From zero to principal-level. Every concept built from the OS kernel up. Every escalation path paired with its detection. No live exploitation — all examples use synthetic metadata matching the lab fixtures.


Table of Contents


Chapter 1 — Why Privilege Escalation Is the Engagement's Hinge

What it is

Privilege escalation (privesc) is the transition from a low-privileged identity to a high-privileged one without receiving that privilege through the normal, authorized path.

On Windows the goal is NT AUTHORITY\SYSTEM (the local kernel identity) or a domain admin token. On Linux the goal is uid 0 (root).

Neither goal requires a memory-corruption exploit in modern environments. The overwhelming majority of real-world privesc — the kind an engagement like Operation Cedar Lattice finds — is misconfiguration: a service the OS launches as SYSTEM with a binary a low-privileged user can overwrite; a SUID bit on a binary that hands root to whoever runs it; a sudo rule so generous it includes a pager. The OS's own mechanisms do the work; the attacker just influences which code they run.

Why it is the hinge of the engagement

A foothold as a low-privileged user (www-data, a service account, a domain user) gives you:

  • Read access to files that principal can read
  • Network connectivity from that host
  • Ability to run code as that principal

That is a start, not a finish. Credential theft (LSASS, /etc/shadow, cloud metadata), persistence that survives reboots (service install, scheduled task), lateral movement with administrative access (WMI, PSRemoting, SSH), and access to protected stores all require elevated privilege on at least one host first. Phase 05's Active Directory attack paths begin from a SYSTEM token or DA credentials — which come from a privesc you execute in Phase 04.

The finding the client keeps is not "we got SYSTEM." It is: the specific misconfiguration, the ATT&CK technique it maps to, the precise telemetry it emits, and the detection rule that catches it. That is what turns a red-team win into a lasting defensive investment.


Chapter 2 — The Windows Security Model, from Zero

2.1 Processes, threads, and identity

Every user-mode program runs as a process, which is the OS unit of isolation: its own virtual address space, its own handle table, its own security context. Within a process run one or more threads, which share the process's address space and handle table.

A thread's security context is carried in two places:

  1. The primary access token on the process — the default identity for that process.
  2. An optional impersonation token on a specific thread — a temporarily assumed identity (used heavily by services like RPC/IIS to act as the calling user).

2.2 The access token — the security principal in practice

The access token is a kernel object (tagged TOKEN, type SeTokenObjectType). Every process has one. It contains:

FieldWhat it is
User SIDThe identity: S-1-5-21-<domain>-<RID> for domain accounts; S-1-5-18 for SYSTEM
Group SIDsAll groups the user belongs to, each with attributes (enabled/disabled/deny-only)
PrivilegesA table of named capability flags (SeImpersonatePrivilege, SeDebugPrivilege, SeBackupPrivilege, …) each in state enabled/disabled/removed
Default DACLThe DACL applied to objects this process creates without an explicit one
Integrity levelA mandatory label (Untrusted / Low / Medium / High / System)
Token typePrimary (full process identity) vs. Impersonation (thread-level, ranges up to Delegation level)
Origin / logon sessionWhich logon session created this token

The critical insight: privilege escalation on Windows is almost always about getting a token you did not earn legitimately — either by stealing a SYSTEM token, impersonating one, or injecting code into a process that carries one.

2.3 SIDs vs. Privileges — the most important distinction

A SID (S-1-5-21-…-500 for Administrator, S-1-5-18 for SYSTEM, S-1-5-32-544 for the Administrators group) is an identity. An ACE in a DACL grants or denies access to a SID. Changing your effective SID is what "becoming SYSTEM" means.

A privilege (SeImpersonatePrivilege, SeDebugPrivilege, …) is a capability — the right to perform a specific OS operation that bypasses the normal object-access check. Many privileges are granted to service accounts by the SCM as part of their job (IIS's app-pool identity gets SeImpersonatePrivilege so it can act as the connected user). A single dangerous privilege on a low-privileged account is a path to SYSTEM even without changing the user SID first.

2.4 Object security descriptors and the access check

Every securable Windows object (file, registry key, process, service, named pipe) carries a security descriptor:

  • Owner SID — who owns it
  • DACL — an ordered list of ACEs (Access Control Entries), each (SID, type ALLOW/DENY, access-mask)
  • SACL — the System ACL for auditing (generates Security event log entries on access)
  • Integrity label — the mandatory integrity level

When a thread opens an object, the kernel's SeAccessCheck function:

  1. Takes the thread's effective token.
  2. Iterates the DACL in order (DENY ACEs win over ALLOW).
  3. Checks whether the requested access mask (e.g. WRITE_DAC | SERVICE_CHANGE_CONFIG) is satisfied by ACEs matching the token's SIDs.
  4. Returns success or ACCESS_DENIED.

A weak DACL on a service or binary means a SID that should not have write access does — and overwriting the binary or reconfiguring the service is the escalation.

2.5 Integrity levels and Mandatory Integrity Control (MIC)

Every process and object carries an integrity level (IL), implemented as an additional SACL-like label:

LevelSIDTypical occupant
UntrustedS-1-16-0Sandboxed (e.g. Chrome renderer)
LowS-1-16-4096Internet Explorer Protected Mode
MediumS-1-16-8192Standard user logon
HighS-1-16-12288Administrator (after UAC elevation)
SystemS-1-16-16384SYSTEM, kernel drivers

The No-Write-Up policy: a process at a lower IL cannot write to an object at a higher IL, even if the DACL would allow it. A Medium-IL process cannot inject into a High-IL process via ordinary means.

UAC is NOT a security boundary. This is a formal Microsoft statement. UAC is a convenience mechanism — it forces admin users to acknowledge that they are elevating, but it does not prevent elevation. A process running Medium-IL with an admin token can launch a High-IL process (the UAC prompt); if the user approves (or an auto-elevation condition is met, as with many system binaries), it becomes High. Once High, it can interact with SYSTEM-level services. Microsoft's security boundary documentation explicitly says: "User Account Control is not a security boundary; it is a convenience prompt."

The interview implication: if you hold a "medium-integrity admin" process on a host, you are one auto-elevating bypass (or one prompt from the user) from High, then from SYSTEM. Do not treat it as a wall.

2.6 Services and the Service Control Manager

Services are long-running processes launched by the Service Control Manager (SCM), which runs as SYSTEM. The SCM reads service configuration from HKLM\SYSTEM\CurrentControlSet\Services\<name>:

  • ImagePath — the binary (or command line) to launch
  • ObjectName — the account to run as (LocalSystem, LocalService, NetworkService, or a domain user)
  • Service-level DACL stored in the Security subkey

Every service has an SDL-issued DACL controlling who may start/stop/query/change it. If a low-privileged principal holds SERVICE_CHANGE_CONFIG or WRITE_DAC on a service, they can redirect ImagePath to their binary and have it run as SYSTEM.


Chapter 3 — Windows Escalation Paths: Under the Hood + Detection

3.1 Unquoted Service Path (T1574.009)

What it is. When the SCM resolves a service ImagePath with spaces and no quotes — C:\Program Files\My App\svc.exe — it tries each space-separated prefix in order:

  1. C:\Program.exe
  2. C:\Program Files\My.exe
  3. C:\Program Files\My App\svc.exe

If a low-privileged user can write C:\Program.exe or C:\Program Files\My.exe (i.e., can create a file in C:\ or C:\Program Files\), the SCM will launch their binary as the service's ObjectName (often SYSTEM).

Precondition you must verify: write access to an intermediate path. An unquoted service path with no writable prefix is not exploitable. This is the most common reporting error.

Detection:

  • Sysmon EID 1 (ProcessCreate): a process spawned by services.exe with an image path in an unusual directory (C:\Program.exe, a non-standard location).
  • Security EID 7045 (New Service Installed): if the attacker installs a service to deliver the payload.
  • Security EID 4688 (New Process): with SubjectUserName = SYSTEM or the service account and a suspicious NewProcessName.

Sigma sketch:

logsource: {product: windows, category: process_creation}
detection:
  selection:
    ParentImage|endswith: '\services.exe'
    Image|startswith:
      - 'C:\Program.exe'
      - 'C:\Windows.exe'
  condition: selection

3.2 Weak Service Binary / DACL (T1543.003 / T1574.010)

Two variants:

  • Weak binary ACL: the service binary on disk (ImagePath) is writable by a low-privileged user. Replace it → SYSTEM executes attacker code on next start/restart.
  • Weak service DACL: a low-privileged principal holds SERVICE_CHANGE_CONFIG on the service object itself. They can change ImagePath in the registry (or via ChangeServiceConfig) to point to attacker code.

Under the hood. ChangeServiceConfig calls the SCM's RChangeServiceConfigW RPC; the SCM checks the caller's token against the service DACL. If the DACL is misconfigured (Everyone has SERVICE_CHANGE_CONFIG, or Authenticated Users has SERVICE_ALL_ACCESS), the check passes for any logged-in user.

Detection:

  • Security EID 4670 (Permissions on an object were changed): if the DACL itself is weakened.
  • Security EID 7040 (Service changed): configuration change on a service.
  • Sysmon EID 11 (FileCreate): a write to a service binary path by a non-SYSTEM user.
  • Sysmon EID 13 (RegistryValueSet): HKLM\SYSTEM\CurrentControlSet\Services\<name>\ImagePath modified by a non-admin user.

3.3 SeImpersonatePrivilege — The Potato Family (T1134.001 / T1134.002)

This is the most common Windows privesc in the wild, because IIS app-pool identities, SQL Server service accounts, and many other service identities ship with SeImpersonatePrivilege by design.

What SeImpersonatePrivilege means. It is the right to call ImpersonateNamedPipeClient, DuplicateTokenEx, or other token-duplication APIs on a token of equal or lower integrity level. The OS grants this to service accounts so they can act as the connecting user (the normal, intended use in RPC/IIS/SQL).

The exploit class — potato family (concept only).

  1. A low-privileged service account holds SeImpersonatePrivilege.
  2. The attacker coerces a SYSTEM-token-bearing process to authenticate to a locally-controlled named pipe (or COM object).
  3. The attacker calls ImpersonateNamedPipeClient on the pipe handle, stealing the SYSTEM token.
  4. With the impersonated SYSTEM token, they call CreateProcessWithTokenW to spawn a shell as SYSTEM.

Variants (PrintSpoofer, RoguePotato, GodPotato, JuicyPotato) differ in the coercion mechanism; the token-impersonation step is identical.

Detection:

  • Security EID 4673 (Sensitive Privilege Use): SeImpersonatePrivilege used outside the expected context (service account impersonating a SYSTEM token rather than a user token).
  • Security EID 4624 / 4648 (Logon): logon type 9 (NewCredentials — impersonation via ImpersonateLoggedOnUser) from an account that should not be doing impersonation.
  • Sysmon EID 1: a process created with CreateProcessWithTokenW from a cmd.exe or powershell.exe child of a service process.
  • Behavioral: service account spawning an interactive shell (cmd.exe, powershell.exe) — a service process that creates an interactive child is almost always suspicious.

3.4 SeDebugPrivilege (T1134.002)

SeDebugPrivilege grants the right to open any process, including SYSTEM processes, with full access (PROCESS_ALL_ACCESS), bypassing the object-level DACL check. The legitimate use: kernel debuggers and memory-analysis tools. In an attacker's hands on a medium-IL admin: open lsass.exe for credential dumping, or open a SYSTEM process and inject shellcode.

Detection:

  • Sysmon EID 10 (ProcessAccess): a non-system process opening lsass.exe or a SYSTEM-owned process with GrantedAccess masks indicating PROCESS_VM_READ / PROCESS_VM_WRITE.
  • Security EID 4673: SeDebugPrivilege use.

3.5 Weak Registry Autorun ACL (T1547.001)

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run and RunOnce execute programs at logon for all users, as the logon session's identity (often the next admin to log in = High-IL token). If the DACL on the key permits a low-privileged user to write to it, they can add an entry pointing to their payload.

Under the hood. HKLM requires admin rights by default, but organizations sometimes misconfigure group policy or post-install scripts create subkeys with overly permissive ACLs. Use Get-Acl / icacls / accesschk.exe against the key; if any non-admin SID holds SetValue or CreateSubKey, it is exploitable.

Detection:

  • Sysmon EID 13 (RegistryValueSet): a non-admin user writing to Run/RunOnce keys.
  • Security EID 4657 (Registry value was modified): same.

3.6 DLL Search-Order Hijacking (T1574.001)

How Windows resolves a DLL. When a process loads crypto.dll without a full path, it searches:

  1. Known DLLs (HKLM\SYSTEM\…\KnownDLLs) — pinned, cannot hijack.
  2. The process's executable directory.
  3. The system directory (C:\Windows\System32).
  4. The 16-bit system directory.
  5. The Windows directory (C:\Windows).
  6. The current working directory (CWD).
  7. Directories on %PATH% in order.

If a SYSTEM-level service loads libfoo.dll and a low-privileged user can write to any directory that appears in steps 2–7 before the legitimate copy, the OS loads the attacker's DLL. The DLL runs in the service's process context — SYSTEM.

Detection:

  • Sysmon EID 7 (ImageLoad): a DLL loaded from an unexpected path (%TEMP%, %APPDATA%, or a user-writable directory) by a SYSTEM-level process.
  • Sysmon EID 11 (FileCreate): a DLL written to a writable directory on the SYSTEM service's search path.

3.7 Writable Scheduled Task (T1053.005)

Scheduled tasks are stored in C:\Windows\System32\Tasks\ (XML task files) or in the registry. The Task Scheduler runs them as the configured principal — often SYSTEM or a high-privileged account. If:

  • The task XML file is writable to a low-privileged user, or
  • The binary or script the task runs is writable to a low-privileged user,

then modifying the task action or the target binary yields code execution as SYSTEM on the next task trigger.

Detection:

  • Security EID 4698 (Scheduled task was created) / 4702 (task modified): source identity that should not be creating privileged tasks.
  • Sysmon EID 11: write to C:\Windows\System32\Tasks\.
  • Sysmon EID 1: a SYSTEM task spawning unexpected children.

Chapter 4 — The Linux Security Model, from Zero

4.1 Identity: real, effective, and saved UIDs

Every process carries three uid (and three gid) values:

NameMeaning
ruid (real uid)Who you actually are (the uid of the user who launched the process)
euid (effective uid)What the kernel checks for permission decisions
suid (saved uid)A saved copy so a privileged process can drop and re-acquire privilege

The kernel's access check uses euid (and egid + supplementary groups). The goal of Linux privilege escalation is euid = 0.

4.2 SUID/SGID — the mechanism

A file with the SUID bit set (chmod u+s file or mode 4755) causes the OS, on execve, to set the new process's euid to the file owner's uid — regardless of who ran it. If root owns chmod and it has SUID set, anyone who runs it gets euid=0.

The kernel enforces this in do_execve()bprm_set_creds():

  1. The kernel reads the file's uid/gid and mode bits.
  2. If S_ISUID is set, bprm->cred->euid = file->uid.
  3. The new process inherits that credential.

GTFOBins is the curated catalog of Unix binaries that, when they have SUID or sudo access, produce a root shell via their built-in features (e.g., find with -exec sh, vim with :!sh, python3 -c "import os; os.setuid(0); os.system('/bin/sh')).

4.3 File capabilities — root decomposed

The capabilities system decomposes the root privilege set into ~40 independent flags, each granting a specific kernel permission without granting all of root. A binary can hold capabilities via extended attributes (security.capability xattr) without being SUID root.

Key capabilities for privesc:

CapabilityEffect
CAP_SETUIDCall setuid(0) — become root. A binary with cap_setuid is effectively root.
CAP_DAC_OVERRIDEOverride all discretionary access control (read/write/execute any file).
CAP_DAC_READ_SEARCHOverride read permission on files and directories.
CAP_SYS_PTRACETrace arbitrary processes — can read/write another process's memory.
CAP_NET_BIND_SERVICEBind to privileged ports (< 1024) — not directly a privesc path.
CAP_SYS_ADMINBroad: mount, clone namespaces, set hostname, etc. Often = root.

The kernel checks capabilities in capable() / ns_capable() based on the process's effective capability set. getcap /path/to/binary reads the xattr.

4.4 sudo — the authorized escalation mechanism that is often misconfigured

sudo allows a configured user to run specific commands as another user (typically root), authenticated. The policy is in /etc/sudoers and drop-in files in /etc/sudoers.d/. Syntax:

user  host = (runas) NOPASSWD: command

Dangerous patterns:

  • NOPASSWD: /usr/bin/vim — GTFOBins: :!sh from inside vim gives root shell.
  • NOPASSWD: /usr/bin/find — GTFOBins: find . -exec sh -p \;.
  • (ALL) NOPASSWD: ALL — unrestricted root. Common on misconfigured dev boxes.
  • NOPASSWD: /opt/scripts/*.sh — wildcard in command path: place /opt/scripts/evil.sh, run it via sudo.
  • env_keep += LD_PRELOAD — attacker sets LD_PRELOAD to a shared library that calls setuid(0) and setgid(0) before the real program initializes. Applies even to the otherwise-safe binary in the rule.

4.5 PATH hijacking and cron misconfigurations

PATH hijack. If a root-owned cron job or init script runs a command without an absolute path (e.g., backup instead of /usr/local/bin/backup) and a low-privileged user controls a directory that appears earlier in root's PATH than the real binary, placing a file named backup there causes root to run attacker code.

Writable cron script. If a root cron runs a script (/etc/cron.daily/logrotate.sh) and that script is writable by a low-privileged user, the user can append code to it and wait for the cron to fire.

Writable cron directory. If /etc/cron.d/ or /etc/cron.daily/ is world-writable (misconfigured install), placing a new cron file there achieves the same.

4.6 Namespaces and container-escape surface

Linux namespaces isolate kernel resources (PID, network, mount, UTS, IPC, user, cgroup). The user namespace is particularly relevant: it allows unprivileged processes to create a namespace in which they hold full capabilities, while appearing as a normal user outside it. A container runtime (Docker, runc) uses namespaces to isolate the container.

Container-escape paths (concept, not lab steps):

  • --privileged container: all capabilities granted, can mount the host filesystem.
  • Host PID/network namespace: direct access to host processes/interfaces.
  • Writable Docker socket (/var/run/docker.sock): run a container that mounts host / as root.
  • Kernel exploits (cve, not our focus): dirty-cow style uid-0 write in an older kernel.

The relevant privesc finding for this track is the socket and the --privileged flag, because both appear in real deployment configurations and both produce a root shell without a kernel exploit.


Chapter 5 — Linux Escalation Paths: Under the Hood + Detection

5.1 Dangerous SUID Binaries (T1548.001)

Finding. find / -perm -4000 -type f 2>/dev/null lists all SUID binaries. Cross-reference against GTFOBins. High-value findings: python3, bash, vim, find, perl, nmap, less, more, awk, tee.

Concept of exploit. bash -p — the -p flag preserves the setuid euid; most shell invocations explicitly drop it. python3 -c "import os; os.execl('/bin/sh', 'sh', '-p')" similarly preserves the elevated euid.

Detection:

  • auditd: execve syscall where euid=0 and ruid!=0 (setuid binary executed by non-root). Rule: -a always,exit -F arch=b64 -S execve -F euid=0 -F ruid!=0 -k suid_execution.
  • Correlate with the parent process's ruid to distinguish legitimate (e.g., sudo) from unexpected.

5.2 Dangerous Capabilities (T1548.001)

Finding. getcap -r / 2>/dev/null. A binary with cap_setuid+ep (effective + permitted) can setuid(0) freely.

Concept of exploit. Python3 with cap_setuid: python3 -c "import os; os.setuid(0); os.system('/bin/bash')". The binary calls setuid(0), the kernel checks the effective capability set (which includes CAP_SETUID), succeeds, and the process now has euid=0.

Detection:

  • auditd: setuid syscall with a0=0 (calling setuid(0)) from a process not expected to do so. -a always,exit -F arch=b64 -S setuid -F a0=0 -k setuid_root.
  • Alert on processes calling setuid(0) whose parent is not a known privilege-management binary (PAM, sudo).

5.3 sudo NOPASSWD Misconfigurations (T1548.003)

Finding. sudo -l — lists what the current user may run as root.

Key patterns and their exploit concepts:

  • NOPASSWD: /usr/bin/vim:!sh or !/bin/bash inside vim.
  • NOPASSWD: /usr/bin/findsudo find . -exec /bin/sh \; -quit.
  • env_keep += LD_PRELOAD + any NOPASSWD binary → sudo LD_PRELOAD=/tmp/evil.so <binary>.
  • Wildcard *.sh → create a path-matching file, run it.

Detection:

  • auditd: execve of sudo or su (-k privilege_escalation); correlate with the spawned child process — a shell spawned by a non-root user via sudo is worth alerting on if the sudo rule is restricted.
  • /var/log/auth.log / /var/log/secure: sudo: <user> : TTY=… ; COMMAND=… — all sudo invocations are logged here. A regex match on the command matching a GTFOBins pattern is a detection.
  • pam_tty_audit — record all keystrokes in a sudo session.

5.4 Writable Root PATH / Cron (T1053.003 / T1574.007)

Finding. Check cron files (/etc/cron*, /var/spool/cron/crontabs/root) for commands without absolute paths or scripts you can write to. Check pspy output on a running system to observe root-cron commands.

Detection:

  • auditd: inotify/fanotify on /etc/cron.d, /etc/crontab, /etc/cron.daily — write events from non-root UIDs are immediately suspicious.
  • Cron jobs that spawn shells under root's UID where the command matches a file that changed recently.

Chapter 6 — Telemetry Architecture: What the OS Logs and Why

Windows logging stack

Kernel ──► Security Event Log (via LSA audit subsystem)
            ├── 4688  New process created (includes command line if enabled)
            ├── 4673  Sensitive privilege used (SeImpersonate, SeDebug, …)
            ├── 4674  Operation attempted on privileged object
            ├── 4624  Logon (includes LogonType: 9 = impersonation)
            ├── 4698  Scheduled task created
            ├── 4702  Scheduled task modified
            ├── 7040  Service config changed
            └── 7045  New service installed

Sysmon driver (rings 0/3)
            ├── EID 1  ProcessCreate (parent/child + full command line)
            ├── EID 7  ImageLoad (DLL path + signer)
            ├── EID 10 ProcessAccess (GrantedAccess mask on target process)
            ├── EID 11 FileCreate
            ├── EID 12/13 RegistryEvent (key/value create/modify)
            └── EID 22 DNSQuery

Why command-line logging matters. Without it, you see that powershell.exe ran; with it (4688's ProcessCommandLine or Sysmon EID 1's CommandLine), you see what it ran. Most Windows privesc techniques are identifiable from command-line content + parent-child relationships.

Linux logging stack

Kernel syscall interface
    │
    ├── auditd: configured via audit rules (-a always,exit -S execve …)
    │           ├── execve (process launch with arguments + ruid/euid)
    │           ├── setuid/setgid (privilege change)
    │           ├── open/openat (file access)
    │           └── socket (network)
    │
    ├── /var/log/auth.log (PAM, sudo, su, SSH — auth events)
    ├── /var/log/syslog (service manager: systemd unit state changes)
    └── eBPF (modern): bpftrace / Falco / Tetragon at the syscall boundary
                       (higher fidelity, harder to tamper with)

Why auditd execve is foundational. Every privesc results in a privileged process being launched (euid=0). execve with euid=0 and ruid!=0 means a non-root user launched a root-privileged binary — that is the detection event, regardless of which technique achieved it.


Chapter 7 — Enumeration-to-Finding Methodology

The engagement methodology for this phase is a loop: enumerate → identify path → classify as finding (with ATT&CK + detection) → hardening recommendation.

Windows enumeration checklist

Run on the owned range against a seeded host; never against a target without authorization:

Services:
  sc qc <service>                  # ImagePath (check for unquoted paths with spaces)
  icacls <service-binary-path>     # writable by non-admin?
  accesschk.exe -uwcqv "Users" *  # who has SERVICE_CHANGE_CONFIG?

Token privileges:
  whoami /priv                     # look for SeImpersonate, SeDebug, SeBackup

Registry autoruns:
  reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
  Get-Acl HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

Scheduled tasks:
  schtasks /query /fo LIST /v      # RunAs + command
  icacls C:\Windows\System32\Tasks\<taskname>

DLL paths (check writable dirs on %PATH%):
  echo %PATH%
  icacls <each-dir>

Linux enumeration checklist

SUID:
  find / -perm -4000 -type f 2>/dev/null

Capabilities:
  getcap -r / 2>/dev/null

Sudo:
  sudo -l

Cron:
  cat /etc/crontab
  ls -la /etc/cron.*
  cat /var/spool/cron/crontabs/root 2>/dev/null

PATH:
  echo $PATH
  find <PATH-dirs> -writable 2>/dev/null

Writable root scripts:
  find / -path /proc -prune -o -writable -type f -perm /6000 2>/dev/null

Classifying a finding

For every path found, document:

  1. Misconfiguration — what exactly is wrong (SUID on python3, SERVICE_CHANGE_CONFIG for Authenticated Users)
  2. ATT&CK technique — the canonical technique ID and name
  3. Precondition — what additional condition makes it exploitable (writable intermediate dir for unquoted paths; sudo rule with the right binary)
  4. Path to root/SYSTEM — the conceptual steps (no live exploitation)
  5. Detection — the log source, event ID, and detection condition
  6. Hardening — the fix (remove SUID bit, tighten DACL, fix sudo rule, quote the service path)

Chapter 8 — Detection Engineering for Privesc

The privesc finding is complete only when it includes the detection that catches it. This is the bar the track sets and the bar Mandiant's reports set — without it, the client cannot close the gap.

The detection design pattern

Every privesc technique is a behavior with a kernel-visible footprint. The footprint is detectable; the detection is most robust when it keys on the mechanism (the invariant the technique requires), not on the tool's name.

TechniqueKernel-visible footprintRobust detection key
Unquoted service pathservices.exe spawns a process from a non-standard pathParent = services.exe, image not in %SYSTEMROOT%
Weak service DACLRegistry write to \Services\<name>\ImagePath by non-adminEID 13 + non-admin writer SID
SeImpersonate potatoService account creates interactive shellService process spawning cmd.exe/powershell.exe as SYSTEM
SUID abuse (Linux)execve with euid=0, ruid!=0auditd: execve -F euid=0 -F ruid!=0
sudo misconfigsudo → child shell of unusual typeauth.log: sudo invocation of GTFOBins binary
Cron hijackWrite to /etc/cron* by non-rootauditd: openat on cron file with write flag by uid!=0

Sigma structure for a Windows privesc rule

title: Service Process Spawning Interactive Shell
id: e4f9a0b1-7c3d-4e2a-8f1b-9a0c2d3e4f5a
status: experimental
description: >
  A process managed by the Service Control Manager spawned an interactive shell,
  which is unexpected for legitimate services and consistent with token-impersonation
  (potato family) or service binary replacement.
logsource:
  product: windows
  category: process_creation
detection:
  selection:
    ParentImage|endswith: '\services.exe'
    Image|endswith:
      - '\cmd.exe'
      - '\powershell.exe'
      - '\pwsh.exe'
  condition: selection
falsepositives:
  - Legitimate services that spawn cmd for scripted tasks (enumerate and add to exclusion)
level: high
tags:
  - attack.privilege_escalation
  - attack.t1134.001

auditd rule for SUID execution (Linux)

-a always,exit -F arch=b64 -S execve -F euid=0 -F ruid!=0 -k suid_exec

This fires on every execve where the resulting process has euid=0 but was launched by a non-root ruid. Post-process the audit log: expected SUID execves (ping, sudo, newgrp) are allowlisted by image path; everything else is reviewed.


Chapter 9 — Significance: Why This Phase Makes or Breaks the Engagement

The foothold is not the outcome

On a Mandiant engagement, the question at Phase 04's end is not "did you get SYSTEM?" but "what would a FIN-LATTICE actor do with SYSTEM on this foothold, and can Meridian detect the path they took?" The outcome of Phase 04 feeds directly into:

  • Phase 05 (Active Directory): domain user → domain admin requires SYSTEM on at least one host to extract a Kerberos ticket, dump credentials, or install a persistent beacon.
  • Phase 06 (Payload/execution): SYSTEM context changes which execution APIs are available and which EDR hooks apply (SYSTEM processes are not constrained by user-mode hooks the same way a low-privileged user process is).
  • Phase 08 (C2): beacon persistence at SYSTEM level survives reboots and user logoffs.

What the client keeps

The deliverable is not the SYSTEM shell — the deliverable is a per-foothold finding document that Meridian's security team can hand to a system administrator with the five fields: misconfiguration, ATT&CK technique, path-to-privilege, detection, and hardening. That document persists long after the engagement ends.


Chapter 10 — Misconceptions

"UAC stops privilege escalation." No. UAC is a UX friction layer. It requires admin users to acknowledge elevation, but it does not prevent an already-admin user from elevating to High IL, and it does not protect SYSTEM-level operations from a High-IL process. Microsoft formally documents UAC as a non-security boundary.

"A service account with SeImpersonatePrivilege is low-risk." It is high-risk. SeImpersonatePrivilege is the enabling condition of the entire potato family. A service account that holds it and that a low-privileged user can reach (by exploiting the service, injecting through a web app, etc.) is a SYSTEM escalation path.

"An unquoted service path is always exploitable." Only if a lower-precedence path has a writable directory. If C:\ is locked to admins (standard hardened build), the unquoted path C:\Program Files\My App\svc.exe is not exploitable even though it is misquoted.

"Root capabilities (cap_setuid) are safer than SUID root." Capabilities were designed to limit the blast radius of a root escape, but cap_setuid+ep on a binary is functionally identical to SUID root: the binary can call setuid(0) and become fully root. Capabilities reduce surface area only when used minimally; a broad capability like cap_dac_override or cap_sys_admin is nearly as dangerous as root.

"sudo is always safe because it requires authentication." It is safe when the rule is minimal and the binary is not in GTFOBins. A NOPASSWD: /usr/bin/vim rule is a passwordless root shell. Authentication does not matter if the command itself spawns a shell.

"SUID bash is always bash -p." The -p flag is required because most modern bash distributions drop privileges on execution if euid != ruid. Without -p, bash drops back to ruid. The exploit requires the flag — and that flag is well-documented and well-detected.

"Privesc findings are complete when you've named the technique." A finding without a detection paired to it fails the track's bar and fails the client. The detection is what turns the finding from a one-time offense into a permanent defensive control.


Lab Walkthrough

Lab 01 — Windows Privilege-Escalation Analyzer

What it does. Ingests a synthetic Python dict of host facts (services with their binary paths and ACLs, token privileges, registry autoruns, scheduled tasks, integrity level) and returns a sorted list of privilege-escalation findings, each with ATT&CK technique, path-to-SYSTEM description, and Sysmon/Event-Log detection.

Key data structures:

host_facts = {
    "services": [
        {
            "name": "VulnSvc",
            "image_path": "C:\\Program Files\\Vuln App\\svc.exe",
            "run_as": "LocalSystem",
            "binary_writable_by": ["BUILTIN\\Users"],
            "dacl": ["BUILTIN\\Users:SERVICE_CHANGE_CONFIG"],
        }
    ],
    "token_privileges": ["SeImpersonatePrivilege", "SeChangeNotifyPrivilege"],
    "autoruns": [
        {"key": "HKLM\\...\\Run", "value": "Updater", "path": "C:\\Temp\\updater.exe",
         "key_writable_by": ["BUILTIN\\Users"]}
    ],
    "scheduled_tasks": [
        {"name": "Cleanup", "run_as": "SYSTEM", "action": "C:\\Scripts\\cleanup.bat",
         "script_writable_by": ["BUILTIN\\Users"]}
    ],
}

Walking through the solution logic:

  1. unquoted_path_issue(service) — checks image_path contains a space and is not quoted, then checks binary_writable_by or dacl for a writable intermediate directory.
  2. writable_service(service) — checks binary_writable_by for non-admin SIDs, or dacl contains SERVICE_CHANGE_CONFIG for non-admin SIDs.
  3. dangerous_privileges(privileges) — cross-references against the catalog {SeImpersonatePrivilege: T1134.001, SeDebugPrivilege: T1134.002, …}.
  4. weak_autorun(autorun) — checks key_writable_by for non-admin SIDs.
  5. writable_task(task) — checks script_writable_by or task binary writable_by.
  6. analyze(host_facts) — calls all five checkers, deduplicates by technique, sorts by severity.

Running:

cd phase-04-os-internals-privesc/lab-01-windows-privesc-analyzer
LAB_MODULE=solution python3 -m pytest -q

Expected: 15 tests green. The reference finds unquoted path, writable service binary, writable service DACL, dangerous privilege (SeImpersonate), weak autorun ACL, and writable scheduled task — each with ATT&CK ID and detection.

Lab 02 — Linux Privilege-Escalation Auditor

What it does. Ingests a synthetic dict of enumerated Linux facts (SUID file list, capabilities, sudo rules, writable PATH entries, cron files/scripts) and returns audit findings with ATT&CK technique, escalation note, and auditd detection.

Key data structures:

facts = {
    "suid_files": [
        {"path": "/usr/bin/python3.9", "owner": "root"},
        {"path": "/usr/bin/ping", "owner": "root"},
    ],
    "capabilities": [
        {"path": "/usr/bin/python3.9", "caps": "cap_setuid+ep"},
    ],
    "sudo_rules": [
        {"runas": "root", "nopasswd": True, "cmd": "/usr/bin/vim"},
        {"runas": "root", "nopasswd": False, "cmd": "/usr/bin/find"},
    ],
    "writable_path_dirs": ["/home/user/.local/bin"],
    "cron_files": [
        {"path": "/etc/cron.daily/backup.sh", "writable_by_user": True}
    ],
}

Walking through the solution logic:

  1. suid_findings(facts) — cross-references SUID file names against the GTFOBins catalog. Known-safe binaries (ping, newgrp, mount, su) are excluded; dangerous ones (python3, bash, vim, find, perl, …) produce findings.
  2. capability_findings(facts) — looks for cap_setuid, cap_dac_override, cap_sys_ptrace, cap_sys_admin in the caps string.
  3. sudo_findings(facts) — checks NOPASSWD, wildcard in cmd, GTFOBins binary in cmd, checks for env_keep with LD_PRELOAD.
  4. path_findings(facts) — checks if user-writable directories appear in PATH before system directories.
  5. cron_findings(facts) — checks writable_by_user flag on cron files/scripts.
  6. audit(facts) — calls all five, deduplicates, returns sorted list.

Running:

cd phase-04-os-internals-privesc/lab-02-linux-privesc-auditor
LAB_MODULE=solution python3 -m pytest -q

Expected: 16 tests green. The reference finds dangerous SUID python3, cap_setuid capability, NOPASSWD vim (GTFOBins), writable cron script — each with detection.


Success Criteria

After completing this phase, without notes you can:

  • Draw the Windows security model: process → token → SID + groups + privileges → object → DACL → access check.
  • Explain why UAC is not a security boundary and what that implies for an engagement.
  • Explain SeImpersonatePrivilege: who gets it, the potato family concept, the detection.
  • State the exact precondition that makes an unquoted service path exploitable.
  • Draw the Linux model: execve → euid from SUID bit or capability → kernel check → root access.
  • List five GTFOBins binaries and the mechanism by which each provides a root shell (SUID path).
  • Explain cap_setuid+ep and why it equals SUID root.
  • List three sudo misconfigurations that give root and their detections.
  • Write a Sysmon rule (YAML Sigma format) for a Windows privesc technique.
  • Write an auditd -a always,exit rule for a Linux privesc technique.
  • Pass both labs (LAB_MODULE=solution pytest) and implement both stubs from scratch.

Common Mistakes and OPSEC

Reporting unquoted service paths without confirming the writable prefix. The automated check is not the finding; the finding is the exploitable path. Always verify.

Treating a privilege as an identity. In a report, name the privilege (SeImpersonatePrivilege) and the technique it enables (T1134.001) separately from the SID of the account. Conflating them confuses remediation — the fix is removing the privilege, not changing the identity.

Missing capabilities in a Linux audit. find / -perm -4000 is not enough. getcap -r / is a separate command that finds capabilities, which are not visible in file permissions. Miss it and you miss a path that find will not surface.

Treating NOPASSWD absence as safe. A sudo rule with a password requirement and a GTFOBins binary is still exploitable — it just requires the password. If the user can social-engineer or phish the password (or it is blank), the rule is dangerous. Flag any GTFOBins binary in sudo rules regardless of NOPASSWD.

Writing detections that key on tool names. A Sysmon rule detecting potato.exe by filename is bypassed by renaming. Write rules on the behavioral footprint: parent process, privilege use event ID, command line pattern, or API call — not on the binary name.

Not pairing a detection with every finding. The track's definition of done is the finding-plus-detection pair. A privesc finding submitted without the detection rule is incomplete and fails the portfolio bar.


Interview Q&A

Q1: Walk me through the Windows access-token model. What is the difference between a SID, a group SID, and a privilege, and how does an object's DACL get checked against a token?

Answer:

Every Windows process has a primary access token — a kernel object that carries three things: the user SID (who the process IS, e.g. S-1-5-21-…-1001), a list of group SIDs with attributes (the groups the user belongs to, each either enabled or deny-only), and a privilege table (named capability flags like SeImpersonatePrivilege, each enabled or disabled).

A SID is an identity; a privilege is a capability. They are orthogonal: you can hold SeDebugPrivilege as a regular domain user (if an admin assigned it), or be a member of Administrators without holding SeImpersonatePrivilege (if it was stripped). The distinction matters for escalation: most privesc paths abuse a privilege (a capability granted to a service account for a legitimate reason) rather than the user's SID (which requires direct identity substitution).

When a thread opens an object (file, service, registry key), the kernel calls SeAccessCheck. It:

  1. Takes the thread's effective token (the impersonation token if present, else the process primary token).
  2. Walks the object's DACL in order: DENY ACEs before ALLOW ACEs.
  3. For each ACE, checks whether the ACE's SID matches any SID in the token (user SID or any enabled group SID).
  4. Accumulates allowed access bits; if all requested bits are satisfied before a DENY blocks them, access is granted.

A weak DACL on a service is one where a non-admin SID (Authenticated Users, Everyone, BUILTIN\Users) holds SERVICE_CHANGE_CONFIG or WRITE_DAC. The SCM's DACL check will grant write access to that principal, so they can reconfigure the service's ImagePath — running as SYSTEM when the SCM starts it.


Q2: Why is UAC not a security boundary? What does that mean for how you treat a "medium-integrity admin" host?

Answer:

Microsoft formally documented this in their security servicing criteria: UAC is not a security boundary. A security boundary is a line Microsoft commits to defend with a security update if it is bypassed. UAC does not meet that bar — UAC bypasses (auto-elevation of specially-signed executables, fodhelper.exe COM hijack, eventvwr.exe registry hijack, etc.) are not patched as security vulnerabilities by default.

What UAC actually is: a UX friction layer that separates an administrator's non-elevated token (Medium IL) from their elevated token (High IL). When a standard admin logs in, Windows creates two tokens: a filtered one (Medium IL, with administrative group SIDs marked deny-only) for the default desktop, and a full one (High IL) that is only activated when the user approves an elevation prompt. UAC is the prompt.

For the engagement this means: a medium-IL shell running as an admin-group member is one approved prompt (or one auto-elevation bypass) away from High IL, which is one more step from SYSTEM-level access. Do not treat "we only got a medium-IL shell" as a security control. If the user is in the Administrators group, they are effectively at High IL as soon as they interact with the elevation. On an engagement, document the admin account running medium-IL as a direct path to SYSTEM unless explicit protections (Standard User enforcement, no admin group membership) are in place.


Q3: Explain the SeImpersonatePrivilege "potato" family — what is the privilege, why do service accounts have it, what is the escalation, and how would you detect it?

Answer:

SeImpersonatePrivilege is a Windows privilege that grants the right to call ImpersonateNamedPipeClient, DuplicateTokenEx, and related APIs to assume the identity of another process's token — specifically, to impersonate a token that was handed to you, rather than one you obtained yourself.

Service accounts — IIS application pool identities, SQL Server service accounts, WCF service hosts — hold this privilege by design. Their legitimate use is operating on behalf of a connecting user: when an HTTP request arrives, the IIS worker process impersonates the authenticated user's token to perform file or database operations as that user. SeImpersonatePrivilege makes that impersonation legal.

The escalation class (potato family — concept only, no code): if you control a low-privileged process that holds SeImpersonatePrivilege (e.g., you exploited an IIS web app and got code in the app pool), you can:

  1. Coerce a SYSTEM-token-bearing Windows component (the Print Spooler (SpoolSS), COM activation, RpcEptMapper) to authenticate to a locally-controlled named pipe or COM endpoint you set up.
  2. The OS hands you that connection's token on the pipe.
  3. You call ImpersonateNamedPipeClient — legal because you have the privilege — and your thread's impersonation token is now SYSTEM.
  4. You call CreateProcessWithTokenW to spawn a new process as SYSTEM.

The coercion mechanism varies per variant (PrintSpoofer exploits the Print Spooler's named-pipe protocol; JuicyPotato uses DCOM activation; RoguePotato uses an OXID resolver trick). The token-steal step is identical.

Detection:

  • Security EID 4673 (Sensitive Privilege Use): SeImpersonatePrivilege referenced by an account that is not a recognized service account performing a recognized impersonation.
  • Security EID 4624 Logon Type 9: an impersonation logon from a service account creating a new logon session where the subject SID matches a SYSTEM identity.
  • Sysmon EID 1: cmd.exe or powershell.exe spawned with a parent image path in %SystemRoot%\System32\inetsrv\ (IIS) or %SystemRoot%\System32\spoolsv.exe (spooler) — a service process spawning an interactive shell is anomalous.
  • Behavioral baseline: enumerate which processes legitimately use impersonation (IIS, COM+, SQL) and alert on any others calling the impersonation APIs.

Q4: What makes an unquoted service path exploitable, and what is the exact additional precondition?

Answer:

An unquoted service path is a Windows service whose ImagePath registry value contains spaces and no quotation marks — for example C:\Program Files\Acme Corp\Logger\logger.exe. When the SCM resolves this path, it tries each space-delimited prefix as a potential executable:

  1. C:\Program.exe
  2. C:\Program Files\Acme.exe
  3. C:\Program Files\Acme Corp\Logger.exe
  4. C:\Program Files\Acme Corp\Logger\logger.exe

The exact additional precondition: a low-privileged user must be able to write a file at one of the higher-precedence paths (i.e., must be able to create C:\Program.exe or C:\Program Files\Acme.exe). The SCM runs the first match it finds; if the attacker can write the interceptor to a directory checked before the real binary, the service runs attacker code on the next start — as SYSTEM if the service runs as LocalSystem.

On a hardened system, C:\ and C:\Program Files\ are admin-owned with no world-write access. The unquoted path exists as a misconfiguration, but it is not exploitable without the writable directory. This is the most common reporting error: automated tools (WinPEAS, PowerUp) flag unquoted paths but do not always verify the writable-prefix precondition. Always check both.


Q5: On Linux, what is the difference between SUID and a file capability like cap_setuid? Why does a defender need to enumerate both?

Answer:

SUID (set-user-ID) is a mode bit on the file. When a process calls execve() on a file with the SUID bit set, the kernel reads the file's owner UID from the inode and sets the new process's effective UID to that owner UID. If root owns the file and it has SUID set, any user who executes it gets euid=0. It is a coarse mechanism: one bit, one owner, applies to everyone who can execute the file.

File capabilities (cap_setuid+ep, cap_dac_override+ep, …) are stored in the binary's security.capability extended attribute. They are a fine-grained alternative to SUID root — they grant specific named capabilities without granting euid=0 directly. However, cap_setuid specifically grants the right to call setuid(0) inside the process — which achieves euid=0 programmatically. A Python3 binary with cap_setuid+ep can run os.setuid(0); os.system('/bin/sh') and get a root shell. cap_setuid+ep is functionally equivalent to SUID root for any language with access to the setuid syscall.

A defender must enumerate both because:

  1. find / -perm -4000 lists SUID bits. It does not read extended attributes — it sees nothing about capabilities.
  2. getcap -r / reads security.capability xattrs. It does not look at file mode bits.

Neither tool covers the other. An audit that runs only one of them will miss one entire class of privilege-escalation path. The correct enumeration is both commands, every time.


Q6: Give me three ways a too-generous sudo rule becomes a root shell, and the auditd rule that catches each.

Answer:

1. GTFOBins binary with NOPASSWD (NOPASSWD: /usr/bin/vim)

Escalation concept: sudo vim opens vim as root. :!/bin/sh drops a root shell (via vim's shell-escape feature). The shell is a child of sudo-launched vim, running as root.

Detection: auditd captures execve of /usr/bin/vim by the user followed by a child execve of /bin/sh with euid=0, ruid=<user>:

-a always,exit -F arch=b64 -S execve -F euid=0 -F ruid!=0 -k suid_gtfobins

Post-process: image path matches a GTFOBins catalog. /var/log/auth.log also records the sudo invocation.

2. Wildcard in command path (NOPASSWD: /opt/scripts/*.sh)

Escalation concept: the user creates /opt/scripts/evil.sh containing #!/bin/bash\nbash -i and runs sudo /opt/scripts/evil.sh. The sudo policy matches *.sh — it permits this command. The shell runs as root.

Detection: sudo invocation in /var/log/auth.log where the COMMAND field matches the wildcard pattern and the actual script does not match an approved allowlist. Alert on any sudo invocation of a script path that did not exist at policy-review time (inode or modification-time anomaly).

3. env_keep += LD_PRELOAD (NOPASSWD: /usr/bin/less with Defaults env_keep += LD_PRELOAD)

Escalation concept: the user writes a shared library /tmp/evil.so whose constructor calls setuid(0); setgid(0); system("/bin/sh -p"). They run sudo LD_PRELOAD=/tmp/evil.so /usr/bin/less. The sudo invocation preserves LD_PRELOAD; the dynamic linker loads /tmp/evil.so before less initializes; the constructor runs as root.

Detection:

  • /var/log/auth.log: sudo … LD_PRELOAD=/tmp/evil.so /usr/bin/less — the LD_PRELOAD value is logged.
  • auditd: write syscall on /tmp/evil.so (openat with O_WRONLY|O_CREAT) followed by a sudo exec shortly after — temporal correlation.
  • auditd: execve of the .so file's path is not directly logged, but the child shell spawned with euid=0 is. Use the euid=0, ruid!=0 rule above.

Q7: You have a foothold as a low-priv user on a Windows host and a Linux host. Describe your enumeration order and what you are looking for on each, and the telemetry each step would emit.

Answer:

Windows enumeration order and telemetry:

  1. Identity and tokenwhoami /all / whoami /priv. Looking for any dangerous privilege (SeImpersonate, SeDebug, SeBackup, SeRestore, SeAssignPrimaryToken). Telemetry: none directly — whoami is a conhost/cmd child with no special API calls. But EDR sees the process launch (Sysmon EID 1).

  2. Servicessc query, sc qc <name> for each running service; accesschk.exe -uwcqv "Users" *. Looking for unquoted paths with writable intermediate directories; writable service binaries; service DACLs granting non-admin SERVICE_CHANGE_CONFIG. Telemetry: sc calls OpenServiceQueryServiceConfig (no security log); accesschk walks the SCM DACL (no specific log but EDR/Sysmon may flag the tool by name).

  3. Registry autorunsreg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run. Looking for keys writable by non-admin SIDs. Telemetry: registry read events (Sysmon EID 12/13 if config captures reads, but most Sysmon configs only log writes; the read itself is usually invisible).

  4. Scheduled tasksschtasks /query /fo LIST /v. Looking for tasks running as SYSTEM with scripts writable by non-admin users. Telemetry: schtasks query generates no security log; the task enumeration is via named-pipe calls to the Task Scheduler service (usually not logged at this fidelity without ETW).

  5. DLL pathsecho %PATH%, then icacls on each directory. Looking for user-writable directories on system-service PATH entries. Telemetry: icacls spawns child processes (EID 1); the directory listing and ACL walk generate object-access events only if SACL is configured on those dirs.

Linux enumeration order and telemetry:

  1. SUID binariesfind / -perm -4000 2>/dev/null. Telemetry: find execve (auditd), with ruid of the low-priv user. The syscalls are openat + statx on every directory — detectable as a file-system sweep if auditd is configured to log openat broadly.

  2. Capabilitiesgetcap -r / 2>/dev/null. Reads security.capability xattr via getxattr syscall. Telemetry: execve of getcap, then getxattr on every file walked (high-volume, noisy, typically not individually logged).

  3. Sudo rulessudo -l. Telemetry: execve of sudo → PAM authentication → /var/log/auth.log records the -l invocation. This is the most visible enumeration step — it is definitively logged.

  4. Croncat /etc/crontab, ls /etc/cron.*, cat /var/spool/cron/crontabs/root. Telemetry: openat on cron files; if auditd is watching /etc/cron* with an -a always,exit -F path=/etc/crontab -F perm=r rule, the read is logged.

  5. Writable PATHecho $PATH, write test on each dir. Telemetry: write attempts on directories are logged by auditd openat with O_WRONLY if watched.


Q8: A SOC asks you to turn your privesc findings into durable detections. Pick two (one per OS) and write the detection logic and what it would false-positive on.

Answer:

Windows — SeImpersonatePrivilege token impersonation leading to an interactive shell (T1134.001)

Logic:

title: Service Process Spawning Interactive Shell
logsource: {product: windows, category: process_creation}
detection:
  selection:
    ParentImage|endswith: '\services.exe'
    Image|endswith:
      - '\cmd.exe'
      - '\powershell.exe'
      - '\pwsh.exe'
  condition: selection

Also correlate with Security EID 4673 (privilege use) for SeImpersonatePrivilege where the subject's logon type is impersonation and the object does not match a known legitimate call site.

False positives: some legitimate services spawn cmd.exe as part of their normal operation (batch-mode setup, some monitoring tools, certain backup agents). The exclusion list should be built empirically from the specific environment. The rule fires on services.exe parentage directly — if a legitimate service routes through cmd.exe for a known reason, exclude by ParentCommandLine pattern or SourceImage of the grandparent service process.

Linux — Dangerous SUID execution (T1548.001)

Rule:

-a always,exit -F arch=b64 -S execve -F euid=0 -F ruid!=0 -k suid_exec

Plus a second rule watching setuid(0) calls:

-a always,exit -F arch=b64 -S setuid -F a0=0 -k setuid_root

Post-process both rules: compare the executed binary path against an allowlist of expected SUID binaries for this host (/usr/bin/sudo, /usr/bin/newgrp, /usr/bin/mount, /usr/bin/umount, /usr/bin/su, /bin/ping). Any execve with euid=0, ruid!=0 that fires a binary not in the allowlist is a high-severity alert.

False positives: legitimate SUID binaries not in the initial allowlist (site-specific tools like ssh-agent, crontab, at). Tune the allowlist from a clean baseline. The setuid(0) rule may also fire on PAM modules during authentication — exclude by the process context (parent = sshd, login, su).


References

Primary sources:

  • Windows Internals, 7th Edition — Mark Russinovich, Andrea Allievi, Alex Ionescu, David Solomon. Parts 1 & 2: processes, threads, access tokens, SIDs, privileges, integrity levels, object manager, security descriptors, services.
  • Microsoft Docs: Access Tokens (Win32), Privilege Constants, Mandatory Integrity Control, How UAC Works ("UAC is not a security boundary"), Service Security and Access Rights, Security Descriptors.
  • MITRE ATT&CK: T1068, T1134.001/002, T1548.001/002/003, T1574.001/009/010, T1543.003, T1547.001, T1053.003/005.
  • GTFOBinsgtfobins.github.io: SUID / sudo / capabilities shell-escape catalog.
  • LOLBASlolbas-project.github.io: Windows living-off-the-land binaries.
  • PrintSpoofer (itm4n write-up) and RoguePotato (splinter_code write-up): SeImpersonatePrivilege concept + detection — read the technique description and detection section, not the exploit code.
  • Linux capabilities(7) man page; sudoers(5) man page.
  • auditd documentation and auditctl(8) man page.
  • Sysmon (Sysinternals): event reference; SwiftOnSecurity sysmon-config and Olaf Hartong sysmon-modular for production configs.
  • Sigma specification and rule repository (github.com/SigmaHQ/sigma): Windows process-creation and registry-modification rule examples.

Tooling (enumerate only, on owned ranges):

  • WinPEAS — Windows privilege-escalation enumeration; understand its output, not just run it.
  • Seatbelt — C# host-survey tool used by defenders and red teamers; covers token privileges, services, tasks.
  • LinPEAS — Linux privilege-escalation enumeration; cross-reference every flag against the concepts above.
  • pspy — process monitor without root; reveals cron jobs and PATH-sensitive scripts on Linux.
  • accesschk.exe (Sysinternals) — audits Windows DACLs on services, files, registry.