~/home/study/intro-sudo-configuration

Intro to Sudo Configuration & Enumeration: Basics Every Pentester Should Know

Understanding Sudo Configuration and Enumeration (Intro)

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, and Cmnd_Alias let 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: #includedir and #include pull 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.d

Important 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/#include directives.
  • 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/psql

Key points to notice:

  • If the output is truncated, add -l -U otheruser (requires root) or use sudo -l -S to 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.d

To 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/bash

Even 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 the admin group.

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: ALL

This 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/zsh with NOPASSWD.
  • Runas with no restriction, e.g., (ALL) or (%wheel) without explicit user list.
  • Writable include files – any file under /etc/sudoers.d that is not mode 0440.

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/vim

Attack steps:

  1. Invoke sudo with the vulnerable binary: sudo vim -c ':!/bin/bash'
  2. The ! /bin/bash command 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.conf

Command:

$ sudo -u postgres cat /var/lib/postgresql/data/pg_hba.conf

This 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 verify sudoers plugin).
  • grep -R and awk – 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 0440 on every file under /etc/sudoers.d.
  • Restrict NOPASSWD to the minimum set of commands; avoid wildcards.
  • Prefer explicit command paths over aliases that expand to many binaries.
  • Regularly run sudo -l for 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 blanket ALL entries.

Common Mistakes

  • Leaving default #includedir /etc/sudoers.d without auditing the directory. Attackers can drop a malicious file if the directory is writable.
  • Using ALL in 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 -c after 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 sudo in container orchestration (e.g., kubectl exec wrappers) – new vectors appear.
  • Automation tools (Ansible, Chef) often drop sudo fragments without strict permission checks.

Practice Exercises

  1. Enumerate the policy on a test VM. Run sudo -l as a normal user, then as sudo -U anotheruser -l. Record any NOPASSWD entries.
  2. Identify insecure includes. Execute find /etc/sudoers.d -type f ! -perm 0440. Fix any offending files.
  3. Create a benign sudo rule. Add a Cmnd_Alias for /usr/bin/uptime and allow a test user to run it without a password. Verify with sudo -l.
  4. Exploit a simulated vulnerable rule. On a controlled box, add bob ALL=(root) NOPASSWD: /bin/bash in /etc/sudoers.d/bob. As bob, run sudo /bin/bash -c 'id' and observe root privileges.
  5. Write a detection script. Using Bash or Python, parse sudo -l output and alert if any line matches ALL=(ALL) NOPASSWD: ALL or 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-parser for 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.