Defensive Research

Linux Privilege Escalation: Defensive Lessons

Linux privilege escalation detection for defenders — auditd rules for sudo, SUID and GTFOBins abuse, a CVE-2025-32463 sudo chroot case study, and hardening.

A dark Linux terminal showing a root prompt and an audit log line flagged in red

Linux privilege escalation detection lives in auditd. The escalation itself is quiet — a misconfigured SUID binary, a permissive sudoers entry, a kernel bug — but the moves leave high-confidence telemetry: identity-change syscalls, writes to /etc/sudoers, and execve of setuid binaries. Watch those and a user who was never root suddenly becoming root lights up. This guide ships the auditd rules and the Sigma logic on top of them.

Privilege escalation maps to MITRE ATT&CK T1068 — Exploitation for Privilege Escalation and the sudo/SUID abuse paths to T1548 — Abuse Elevation Control Mechanism. The 2025 sudo flaw CVE-2025-32463 — a critical local privilege escalation in CISA’s KEV catalog — is the worked example.

What is Linux privilege escalation?

Linux privilege escalation is any technique that takes an attacker from a low-privilege foothold to root (or another higher-privilege account). It is the step after initial access — the same shell a command injection hands an attacker is usually unprivileged, and privilege escalation is how they turn it into full control of the host.

It rarely involves a flashy exploit. Most real-world escalation abuses misconfiguration: an over-permissive sudoers rule, a SUID binary that can spawn a shell, a writable service file. That is why detection focuses on behavior and configuration changes, not malware signatures.

What are the main Linux privilege escalation techniques?

TechniqueWhat the attacker abusesTelemetry fingerprintATT&CK
sudo misconfiguration / CVEPermissive sudoers or a sudo bugsudo by an unusual user; sudo -R to a writable pathT1548.003
SUID / GTFOBinsA setuid binary with a shell escapeexecve of find/vim/awk running as rootT1548.001
Kernel / service exploitA vulnerable kernel or daemonCrash logs, unexpected root process from a serviceT1068
Writable cron / service fileAn editable scheduled taskFile writes to cron/systemd units by a normal userT1053

The unifying defensive lesson: escalation is visible at the syscall layer. auditd captures the identity change and the privileged execve even when the technique itself is novel.

How to detect Linux privilege escalation with auditd

auditd provides tamper-resistant, syscall-level logging. A small, focused ruleset gives strong coverage without flooding the SIEM.

Caption: an auditd ruleset for the three highest-value escalation signals.

## identity changes (setuid/setgid family)
-a always,exit -F arch=b64 -S setuid,setreuid,setresuid -F auid>=1000 -F auid!=4294967295 -k privesc_idchange
## sudoers tampering
-w /etc/sudoers -p wa -k privesc_sudoers
-w /etc/sudoers.d/ -p wa -k privesc_sudoers
## execution by SUID binaries (root EUID, real user)
-a always,exit -F arch=b64 -S execve -C uid!=euid -F euid=0 -k privesc_suid_exec

Confirm events flow with ausearch -k privesc_suid_exec. Scope to auid>=1000 to focus on human users, and exclude known maintenance jobs with -F exe!=... to cut noise. On the SIEM side, two Sigma rules turn that telemetry into alerts.

Sigma Sudo Invoked With chroot Referencing a User-Writable Path
title: Sudo Invoked With chroot Referencing a User-Writable Path
id: 7d2e9b41-darkpwn-illustrative
status: experimental
logsource:
  product: linux
  service: auditd
detection:
  selection:
    type: 'EXECVE'
    a0|endswith: 'sudo'
  flags:
    a1|contains: ['-R','--chroot']
  condition: selection and flags
falsepositives:
  - Legitimate administrative use of sudo --chroot (rare; allowlist operators)
level: high
Sigma Execution of a GTFOBins SUID Binary by a Standard User
title: Execution of a GTFOBins SUID Binary by a Standard User
id: 2f8a4c63-darkpwn-illustrative
status: experimental
logsource:
  product: linux
  service: auditd
detection:
  selection:
    key: 'privesc_suid_exec'
    exe|endswith: ['/find','/vim','/awk','/nmap','/python3','/perl','/less']
  condition: selection
falsepositives:
  - Backup/monitoring jobs running these as root (allowlist the service account)
level: high

What auditd will not tell you

auditd is the right backbone and it has limits that determine whether your rules produce reliable data. All four of these fail quietly.

Events are dropped when the backlog fills. The kernel audit subsystem buffers events, and if the daemon cannot keep up, records are lost — silently, from your perspective. Check auditctl -s and read the lost counter. A non-zero and growing value means your detection has gaps precisely during the busiest periods, which correlate with the periods that matter.

  • Raise the backlog limit (-b) above the default, which is conservative for a busy server.
  • Choose the failure mode deliberately (-f). Silent dropping preserves availability and loses evidence; halting on failure preserves evidence and can take the host down. Most estates want the middle option, and the decision should be recorded rather than defaulted into.
  • Monitor the lost counter as a metric. A detection pipeline that discards events without telling you is worse than one that is switched off, because it reports clean.

Syscall auditing costs CPU. Rules that inspect every execve, and especially comparison rules like -C uid!=euid, add overhead to a hot path. Measure on a representative host before a fleet rollout. The classic failure is a ruleset validated on an idle test VM and deployed to a busy database server.

Container attribution is poor. The audit subsystem was designed before containers and is not namespace-aware in the way people expect. Events from processes inside a container arrive with host context and limited indication of which container produced them. Support for audit container identifiers exists in some kernels and distributions and is not something to assume. If your workloads are containerised, host-level auditd tells you a privileged execve happened somewhere on the node, which is a much weaker statement than it appears.

eBPF-based tooling addresses the container gap. Falco, Tetragon, and Tracee observe kernel events with container awareness built in, and generally impose less overhead than syscall auditing. The trade-offs are real — kernel version requirements, another agent, and a different rule language — but for a containerised estate they are the correct layer, and auditd is a poor substitute rather than a cheaper one.

Capabilities are the SUID inventory you are missing

The hardening section recommends inventorying SUID binaries with find / -perm -4000. That command is correct and it misses an entire parallel mechanism.

Linux file capabilities grant specific privileges to a binary without setting the setuid bit. A binary carrying cap_setuid+ep can change its user ID to root and will not appear in any SUID search. The inventory looks clean, the escalation path is open, and nothing in the standard checklist finds it.

Run getcap -r / 2>/dev/null alongside the SUID inventory. The capabilities to treat as root-equivalent when found on an unexpected binary:

CapabilityWhat it grants
CAP_SETUIDBecome any user, including root
CAP_SYS_ADMINVery broad; frequently described as root by another name
CAP_DAC_READ_SEARCHBypass file read permission checks — read any file on the system
CAP_SYS_PTRACEAttach to and manipulate other processes, including privileged ones
CAP_SYS_MODULELoad kernel modules — full kernel-level control
CAP_BPF / CAP_PERFMONLoad eBPF programs and read kernel state

Two operational points. Capabilities survive package updates unpredictably — a rebuilt package may or may not restore them, so an inventory taken once drifts. And capability drift is a finding: a binary that gains a capability between two inventories has either been updated or been tampered with, and both need an explanation. Diff the output on a schedule, the same way you diff the SUID list.

What else belongs in the ruleset

The three rules above cover the highest-value signals. These are the additions worth the extra volume, and they lean toward persistence — which is what an attacker establishes immediately after escalating.

  • ~/.ssh/authorized_keys modification. Adding a key is the simplest, most durable Linux persistence there is, it survives password changes, and almost nobody watches for it. This is arguably the highest-value rule on the page after the identity-change one.
  • Writes to /etc/passwd, /etc/shadow, and /etc/group. Direct account manipulation.
  • Writes to cron directories and systemd unit paths, both system-wide and per-user. Scheduled execution is the second-most-common persistence mechanism.
  • PAM configuration changes. A modified PAM module can capture credentials or grant authentication outright, and the change is a single file edit.
  • Kernel module loading (init_module, finit_module). On most servers this happens at boot and never again, which makes it a near-zero-noise rule with severe implications when it fires.
  • ptrace usage. Process manipulation, credential theft from memory, and injection. Noisier on developer machines; excellent on servers.

Then one configuration setting that protects all of them: make the audit configuration immutable with -e 2 as the final line of your rules. After that, the ruleset cannot be modified until reboot. An attacker with root can still stop the daemon or reboot the host — and both of those are themselves loud events you can alert on, which is the point. Without immutability, the first thing a competent intruder does is quietly remove the rule that would have recorded them, and you never learn that anything happened.

Containers change the layer

A large share of Linux workloads now run in containers, and that shifts what privilege escalation means.

Escalating to root inside a container is often trivial and frequently unimportant. Many containers run as root already. The meaningful question is whether root in the container reaches root on the host, and that depends on configuration rather than on any exploit:

  • A privileged container is effectively root on the node.
  • A mounted container runtime socket lets a process start a new container with the host filesystem mounted — a complete escape in one command.
  • CAP_SYS_ADMIN granted to a container is close to equivalent.
  • Host namespace sharing (hostPID, hostNetwork) removes the isolation the container was providing.
  • Host path mounts of sensitive directories give direct access.

Detection therefore moves upward — to the orchestrator’s admission and audit layer, which is the subject of Kubernetes security events to prioritize. Watching for a root shell inside a container that was configured to run as root is watching the expected state; watching for a container being created with escape-enabling configuration catches the actual risk, before anything runs.

The honest framing: for containerised workloads, host-level auditd is the wrong primary layer. Keep it for the nodes themselves, which are long-lived and worth defending as hosts, and put the workload detection where the workload configuration lives.

How do you respond to a confirmed escalation?

  1. Assume the host is compromised at root level, which means everything running on it is suspect and any evidence gathered from the running system is of limited confidence.
  2. Capture before you contain, if the host matters. Process list, network connections, open files, loaded modules, and memory if you have the capability. Isolation is fine; rebooting destroys most of this.
  3. Identify the escalation path, because it is a configuration finding that almost certainly exists on other hosts built from the same image. This is the part that scales.
  4. Check persistence comprehensivelyauthorized_keys on every account, cron for every user, systemd units, PAM modules, loaded kernel modules, and shell profile files. Removing the entry point without the persistence returns you to the same state in a week.
  5. Rotate every credential the host held, including SSH keys it trusted, and treat any cloud instance role attached to it as compromised — which frequently makes this a cloud incident.
  6. Rebuild rather than clean. For most modern estates hosts are replaceable, and rebuilding from a known-good image is faster and more trustworthy than proving a root-compromised system is clean. Where the host genuinely cannot be rebuilt, that fact is itself a finding worth raising.
  7. Sweep the fleet for the same misconfiguration, using the inventory commands above. One permissive sudoers rule or one unexpected capability is rarely unique to one host — it usually came from a configuration management template, which means it is everywhere that template ran.

Step 7 is where the lasting value is. A single escalation is an incident; the misconfiguration behind it is usually a systemic finding, and fixing it once removes the path everywhere.

How to test your privilege-escalation detection

In a disposable VM you own:

  1. Add the auditd rules above and confirm events flow with ausearch.
  2. Run a benign sudo -R and a GTFOBins shell-escape in a lab context; confirm both Sigma rules fire.
  3. Add a NOPASSWD test entry to /etc/sudoers.d/ and confirm the sudoers-write rule fires.
  4. Replay normal admin activity and confirm the rules stay quiet; record allowlist exclusions.

How to harden against privilege escalation

  • Patch sudo and the kernel promptly — CVE-2025-32463 is actively exploited and KEV deadlines applied. Verify by behaviour rather than by version string, since distributions backport fixes without changing the reported version and a version check can both miss a vulnerable host and flag a patched one.
  • Make /etc/sudoers.d/ and cron/systemd unit files non-writable by normal users, and monitor them with auditd — configuration change is the earliest signal available here, and it arrives before the escalation rather than during it.
  • Run workloads in least-privileged containers/users so a foothold has nowhere to climb — and verify the container configuration itself, since a privileged container or a mounted runtime socket makes in-container root equivalent to root on the node regardless of how carefully the workload user was chosen.

Common privilege-escalation detection mistakes

  • Version-only patch checks. Distro backports break version detection — verify the behavior, not just the number.
  • No auditd, or unscoped auditd. Without it you have no syscall telemetry; with everything logged, the signal drowns.
  • Signature-only thinking. GTFOBins abuse is living-off-the-land; only behavioral rules catch it.
  • Ignoring SUID inventory drift. A new SUID binary after a deploy is a finding.
  • Inventorying SUID and not capabilities. A binary with cap_setuid+ep is root-equivalent and appears in no SUID search; run getcap -r / alongside it.
  • Ignoring the auditd lost counter. A full backlog drops events silently, during exactly the busy periods that matter, and the pipeline still reports healthy.
  • Validating the ruleset on an idle VM. Syscall auditing costs CPU on a hot path, and comparison rules are the expensive ones.
  • Not setting the configuration immutable. Without -e 2, the first thing an intruder with root does is quietly remove the rule that would have recorded them.
  • Omitting authorized_keys monitoring. Adding an SSH key is the simplest durable Linux persistence there is, and it survives every password reset.
  • Using host-level auditd as the primary control for containers. The audit subsystem is not namespace-aware in the way people expect, so attribution is poor; the orchestrator’s audit layer is the right place.
  • Cleaning a root-compromised host instead of rebuilding. Proving a root-level compromise is gone costs more than rebuilding and produces less confidence.
  • Fixing one host. A permissive sudoers rule or an unexpected capability almost always came from a configuration template, which means it is everywhere that template ran.

Linux privilege escalation detection checklist

  1. Deploy auditd rules for identity changes, sudoers writes, and SUID execve.
  2. Baseline which users run sudo and which binaries run as root, then alert on the deviation rather than on the event — a single sudo invocation is normal, and a first-ever one is not.
  3. Alert on a new user escalating, a service account invoking su, or sudo -R to a writable path.
  4. Alert on GTFOBins binaries executing as root in a shell-escape context.
  5. Inventory SUID/SGID binaries and file capabilities; remove or restrict GTFOBins entries.
  6. Harden sudoers — never NOPASSWD: ALL, and scope every rule to specific commands; make sudoers.d, cron, and systemd unit files non-writable by normal users.
  7. Patch sudo (1.9.17p1+/backport) and the kernel on a KEV cadence.
  8. Confirm auditd events flow with ausearch; fire each rule in a lab.
  9. Inventory file capabilities with getcap -r / alongside the SUID list, and diff both on a schedule — a binary with cap_setuid+ep is root-equivalent and appears in neither a SUID search nor most hardening checklists.
  10. Monitor the auditd lost counter as a metric, raise the backlog limit, and set the failure mode deliberately rather than by default.
  11. Add rules for authorized_keys writes, cron and systemd unit changes, PAM edits, kernel module loading, and ptrace.
  12. Make the audit configuration immutable with -e 2 so the ruleset cannot be quietly removed.
  13. For containerised workloads, detect at the orchestrator layer instead; host auditd cannot attribute events to a container reliably.

Item 9 is the gap most hardening guides leave open. The SUID inventory has been standard advice for decades and capabilities have not, which means the escalation path that does not appear in the standard check is the one still sitting there.

The takeaway

Linux privilege escalation detection is auditd plus a baseline: log identity changes, sudoers edits, and privileged execve, then alert on the deviation from normal. Patch the known escalation CVEs, shrink the SUID and sudo surface, and treat configuration drift as a signal. Pair this with command injection detection for the full initial-access-to-root chain, or explore the Defensive Research pillar.

Training & tools referenced

Disclosure: Some links below are affiliate links. If you buy through them, darkpwn may earn a commission at no extra cost to you. We only recommend training and tools we actually use in our own lab, and affiliate links never influence editorial coverage.

  • TryHackMeAuthorized labs to practice Linux privilege-escalation detectionSecurity Training
    Start training

Frequently asked questions

How do you detect Linux privilege escalation?

Use auditd to log identity changes, sudoers modifications, and execve calls by setuid binaries, then alert on anomalies — a user who never uses sudo suddenly running privileged commands, a service account invoking su, or execution of GTFOBins binaries (find, vim, awk) in a shell-escape context.

What is CVE-2025-32463?

CVE-2025-32463 is a critical sudo local privilege escalation flaw (added to CISA's Known Exploited Vulnerabilities catalog) in the sudo --chroot (-R) option. An unprivileged user can plant a fake /etc/nsswitch.conf in the chroot path and trick sudo into loading a malicious library as root. Patch to sudo 1.9.17p1 or a backported fix.

What is GTFOBins and why does it matter to defenders?

GTFOBins catalogs legitimate Unix binaries (find, vim, awk, python, nmap) that can be abused to escalate privileges when misconfigured with SUID or sudo rights. It is a living-off-the-land technique, so defenders watch for execve of these binaries in escalation contexts rather than relying on signatures.

Which auditd rules detect privilege escalation?

Monitor identity-change syscalls (setuid/setgid), writes to /etc/sudoers and /etc/sudoers.d, and execve of setuid binaries and known GTFOBins entries. Scope rules to real users (uid>=1000) and exclude known maintenance jobs to cut noise.