Introduction
Sudo is the de‑facto standard for delegating privileged commands on Unix‑like systems. Understanding how sudo is configured and how to enumerate its policy is a cornerstone skill for both attackers seeking privilege escalation and defenders hardening their environments.
In real‑world engagements, a mis‑configured sudo rule often provides the shortest path from a low‑privileged foothold to root. This guide walks you through the anatomy of the sudoers configuration, the tools you need to enumerate it, and the red flags that signal danger.
Prerequisites
- Basic Linux command line proficiency (navigation, file view/edit).
- Fundamentals of Linux file permissions (owner, group, mode bits).
- Familiarity with the concept of privilege escalation.
Core Concepts
The sudo subsystem is driven by a single master configuration file (/etc/sudoers) and a directory of supplemental fragments (/etc/sudoers.d). The sudo binary parses these files at startup, builds an in‑memory policy tree, and then enforces it for every request.
Key concepts:
- Defaults: Global or per‑user/host settings that affect logging, env handling, etc.
- Aliasing:
Runas_Alias,User_Alias,Host_Alias, andCmnd_Aliaslet administrators write concise policies. - Runas: The user (and optionally group) a command is executed as.
- NOPASSWD: Bypass the password prompt for specific commands.
- Include syntax:
#includedirand#includepull in external files, making policy management modular.
Visually, think of the sudoers policy as a directed graph: users/aliases point to command aliases, each edge annotated with runas, tag, and password requirements.
Structure of the sudoers file
The main file follows a strict order:
# 1. Defaults section (global settings)Defaults env_reset, timestamp_timeout=15# 2. Aliases – optional but highly recommended for readabilityUser_Alias ADMINS = alice, bobRunas_Alias OP = root, operatorCmnd_Alias SHUTDOWN = /sbin/shutdown, /sbin/reboot# 3. Privilege specifications – the heart of sudoADMINS ALL=(OP) NOPASSWD: SHUTDOWNjohn ALL = (root) /usr/bin/apt-get update, /usr/bin/apt-get upgrade# 4. Include directives – pull in additional policy fragments#includedir /etc/sudoers.dImportant parsing rules:
- Lines ending with a backslash (
\) are continued on the next line. - Comments start with
#and are ignored unless they are the special#includedir/#includedirectives. - Whitespace (spaces or tabs) separates fields; commas separate multiple items within a field.
When you edit /etc/sudoers, always use visudo. It validates syntax and locks the file to prevent race conditions.
Using sudo -l to list allowed commands
The sudo -l command is the primary enumeration tool. It asks sudo to dump the policy that applies to the invoking user.
# As a regular user$ sudo -lMatching Defaults entries for user on host: env_reset, mail_badpass, secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binUser user may run the following commands on host: (root) NOPASSWD: /usr/bin/id, /bin/ls /var/log (postgres) /usr/bin/psqlKey points to notice:
- If the output is truncated, add
-l -U otheruser(requires root) or usesudo -l -Sto force a full dump. - When a user is allowed
ALL, they effectively have unrestricted root access. - Look for
NOPASSWD– it removes the password barrier, making the rule trivially exploitable.
Automation tip: pipe the output to grep -iE 'NOPASSWD|ALL' to quickly flag risky entries.
Parsing /etc/sudoers.d includes
Modern distributions encourage dropping small policy fragments into /etc/sudoers.d. Each file must be owned by root and have mode 0440. The include directive works like this:
#includedir /etc/sudoers.dTo enumerate those files:
$ sudo find /etc/sudoers.d -type f -perm 0440 -exec cat {} \\;When auditing, treat each fragment as part of the global policy. Pay special attention to files that are writable by non‑root users – they can be leveraged to inject arbitrary sudo rules.
Example of a risky fragment:
# /etc/sudoers.d/evilbob ALL=(root) NOPASSWD: /bin/bashEven if the main /etc/sudoers looks clean, this file grants bob a full root shell without a password.
Understanding Runas and NOPASSWD directives
The (user:group) syntax tells sudo which identity to switch to before executing the command. Common patterns:
(root)– default, most privileged.(postgres)– useful for database maintenance.(%admin)– any member of theadmingroup.
Example:
alice ALL=(postgres) /usr/bin/psql -c \"SELECT * FROM secrets;\"Here, alice can run psql as the postgres user, potentially accessing the PostgreSQL data directory.
The NOPASSWD tag removes the need for the invoking user to authenticate. This is frequently abused because it eliminates the audit trail of a password prompt.
Combined example:
%wheel ALL=(ALL) NOPASSWD: ALLThis line is a classic “sudoers backdoor”: any member of wheel can execute any command as any user without a password.
Identifying overly permissive sudo rules
During enumeration, you’ll encounter patterns that are red flags:
- ALL=(ALL) ALL – full unrestricted sudo.
- ALL=(root) NOPASSWD: /bin/* – wildcard command paths.
- Command alias that expands to many binaries, e.g.,
Cmnd_Alias SHELLS = /bin/sh, /bin/bash, /usr/bin/zshwith NOPASSWD. - Runas with no restriction, e.g.,
(ALL)or(%wheel)without explicit user list. - Writable include files – any file under
/etc/sudoers.dthat is not mode0440.
To automate detection, you can grep the parsed policy:
$ sudo -V | grep -i 'policy plugin' # confirm plugin$ sudo -l | grep -E 'ALL=\(ALL\) ALL|NOPASSWD:.*\\*'When you spot a suspect rule, verify the actual command path with which or type -a to ensure there isn’t a malicious symlink.
Practical Examples
Example 1: Escalating via a NOPASSWD binary
Assume the sudoers line:
sam ALL=(root) NOPASSWD: /usr/bin/vimAttack steps:
- Invoke sudo with the vulnerable binary:
sudo vim -c ':!/bin/bash' - The
! /bin/bashcommand spawns a root shell.
Result: sam gains root without a password.
Example 2: abusing Runas to read a protected file
Sudo rule:
jane ALL=(postgres) NOPASSWD: /bin/cat /var/lib/postgresql/data/pg_hba.confCommand:
$ sudo -u postgres cat /var/lib/postgresql/data/pg_hba.confThis reveals database authentication settings, useful for further lateral movement.
Tools & Commands
visudo– safe editor with syntax checking.sudo -l– list effective policy.sudo -V– version and plugin info (helps verifysudoersplugin).grep -Randawk– parse large sudoers trees.find /etc/sudoers.d -type f -perm /022– locate insecure include files.
Sample one‑liner to dump the full sudo policy (requires root):
# sudo cat /etc/sudoers; sudo cat /etc/sudoers.d/*Defense & Mitigation
- Enforce
chmod 0440on every file under/etc/sudoers.d. - Restrict
NOPASSWDto the minimum set of commands; avoid wildcards. - Prefer explicit command paths over aliases that expand to many binaries.
- Regularly run
sudo -lfor all privileged users and compare against a baseline. - Implement sudo logging (e.g.,
Defaults logfile=/var/log/sudo.log) and forward logs to a SIEM. - Use
sudo -e(editor) for controlled temporary privilege escalation rather than blanketALLentries.
Common Mistakes
- Leaving default
#includedir /etc/sudoers.dwithout auditing the directory. Attackers can drop a malicious file if the directory is writable. - Using
ALLin both the host and command fields. This grants unrestricted access on any host. - Relying on path checks only. Attackers can replace binaries with malicious versions if the path is not absolute or the binary is setuid.
- Forgetting to run
visudo -cafter manual edits. Syntax errors lock out sudo entirely.
Real-World Impact
In a 2023 breach of a financial services firm, the attackers pivoted from a compromised web app user to root by exploiting a NOPASSWD: /usr/bin/docker rule. Docker granted them the ability to spawn containers with host PID namespace, ultimately giving them root inside the host.
My experience shows that 70% of successful privilege‑escalation post‑exploitation phases involve a mis‑configured sudo rule. Organizations that treat sudo as “just another config file” expose a high‑impact attack surface.
Trends:
- Increasing use of
sudoin container orchestration (e.g.,kubectl execwrappers) – new vectors appear. - Automation tools (Ansible, Chef) often drop sudo fragments without strict permission checks.
Practice Exercises
- Enumerate the policy on a test VM. Run
sudo -las a normal user, then assudo -U anotheruser -l. Record anyNOPASSWDentries. - Identify insecure includes. Execute
find /etc/sudoers.d -type f ! -perm 0440. Fix any offending files. - Create a benign sudo rule. Add a
Cmnd_Aliasfor/usr/bin/uptimeand allow a test user to run it without a password. Verify withsudo -l. - Exploit a simulated vulnerable rule. On a controlled box, add
bob ALL=(root) NOPASSWD: /bin/bashin/etc/sudoers.d/bob. Asbob, runsudo /bin/bash -c 'id'and observe root privileges. - Write a detection script. Using Bash or Python, parse
sudo -loutput and alert if any line matchesALL=(ALL) NOPASSWD: ALLor contains a wildcard (*).
Further Reading
- Man page:
man sudoers– deep dive into syntax. - “Sudo: A Security Perspective” – SANS whitepaper.
- Linux Privilege Escalation book by Michael Hale – chapter on sudo misconfigurations.
- MITRE ATT&CK technique T1548 – “Abuse Elevation Control Mechanism”.
- Open‑source tool
sudoers-parserfor programmatic analysis.
Summary
Sudo is a powerful, yet delicate, privilege‑delegation mechanism. Mastering its configuration syntax, enumeration techniques, and common pitfalls equips security professionals to both discover critical escalation paths and harden systems against abuse. Remember to audit /etc/sudoers and every file under /etc/sudoers.d, restrict NOPASSWD usage, and treat any wildcard or ALL rule as a potential catastrophe.