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.
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?
| Technique | What the attacker abuses | Telemetry fingerprint | ATT&CK |
|---|---|---|---|
| sudo misconfiguration / CVE | Permissive sudoers or a sudo bug | sudo by an unusual user; sudo -R to a writable path | T1548.003 |
| SUID / GTFOBins | A setuid binary with a shell escape | execve of find/vim/awk running as root | T1548.001 |
| Kernel / service exploit | A vulnerable kernel or daemon | Crash logs, unexpected root process from a service | T1068 |
| Writable cron / service file | An editable scheduled task | File writes to cron/systemd units by a normal user | T1053 |
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.
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 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
lostcounter 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:
| Capability | What it grants |
|---|---|
CAP_SETUID | Become any user, including root |
CAP_SYS_ADMIN | Very broad; frequently described as root by another name |
CAP_DAC_READ_SEARCH | Bypass file read permission checks — read any file on the system |
CAP_SYS_PTRACE | Attach to and manipulate other processes, including privileged ones |
CAP_SYS_MODULE | Load kernel modules — full kernel-level control |
CAP_BPF / CAP_PERFMON | Load 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_keysmodification. 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. ptraceusage. 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_ADMINgranted 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?
- 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.
- 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.
- 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.
- Check persistence comprehensively —
authorized_keyson 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. - 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.
- 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.
- 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:
- Add the auditd rules above and confirm events flow with
ausearch. - Run a benign
sudo -Rand a GTFOBins shell-escape in a lab context; confirm both Sigma rules fire. - Add a
NOPASSWDtest entry to/etc/sudoers.d/and confirm the sudoers-write rule fires. - 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+epis root-equivalent and appears in no SUID search; rungetcap -r /alongside it. - Ignoring the auditd
lostcounter. 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_keysmonitoring. 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
- Deploy auditd rules for identity changes, sudoers writes, and SUID
execve. - 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.
- Alert on a new user escalating, a service account invoking
su, orsudo -Rto a writable path. - Alert on GTFOBins binaries executing as root in a shell-escape context.
- Inventory SUID/SGID binaries and file capabilities; remove or restrict GTFOBins entries.
- 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. - Patch sudo (1.9.17p1+/backport) and the kernel on a KEV cadence.
- Confirm auditd events flow with
ausearch; fire each rule in a lab. - Inventory file capabilities with
getcap -r /alongside the SUID list, and diff both on a schedule — a binary withcap_setuid+epis root-equivalent and appears in neither a SUID search nor most hardening checklists. - Monitor the auditd
lostcounter as a metric, raise the backlog limit, and set the failure mode deliberately rather than by default. - Add rules for
authorized_keyswrites, cron and systemd unit changes, PAM edits, kernel module loading, andptrace. - Make the audit configuration immutable with
-e 2so the ruleset cannot be quietly removed. - 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 TrainingStart 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.