root/security-operations/sudoers-nopasswd-the-silent-path-to-root

Sudoers NOPASSWD: The Silent Path to Root

A technical breakdown of sudoers privilege escalation via wildcard NOPASSWD rules, with audit scripts and hardened Cmnd_Alias configurations.
Sudoers NOPASSWD: The Silent Path to Root
09/07/2026|1 min read|

A single misplaced wildcard in /etc/sudoers is often the difference between a hardened production host and an open root shell. The pattern is depressingly common across fleets managed by teams under delivery pressure: an admin grants NOPASSWD: /usr/bin/systemctl restart * to unblock a deployment pipeline, or delegates ALL=(ALL) NOPASSWD: /opt/scripts/* to a service account for “convenience”. Six months later, a penetration test or a compromised CI runner turns that convenience into a full sudoers privilege escalation chain. This article dissects exactly how that chain forms, how to audit for it at scale, and how to remediate it without breaking the automation that depended on the broken rule in the first place.

This is not a beginner’s guide to sudo. It assumes you already understand basic Unix permissions and are looking for the specific architectural failure modes that turn a benign-looking sudoers entry into an exploitable sudoers privilege escalation vector across hundreds or thousands of hosts.

#The Problem: Wildcard and NOPASSWD Rules as an Escalation Surface

The sudo binary evaluates rules against a policy that was almost certainly never designed with the current command set in mind. Three specific patterns account for the overwhelming majority of real-world sudoers privilege escalation findings:

  • Trailing wildcards on command paths/usr/bin/systemctl * matches systemctl restart nginx but also systemctl link /tmp/evil.service.
  • Directory wildcards/opt/scripts/* allows execution of any file an attacker can drop into that directory, including one they created themselves if the directory is group-writable.
  • NOPASSWD without command restriction — bypasses the interactive authentication step entirely, removing the last human checkpoint before privilege elevation.

None of these are theoretical. They map directly onto documented CVE classes and are catalogued extensively in the official sudoers manual, which explicitly warns against unrestricted argument wildcards — a warning most admins never read past the installation step.

#Architectural Breakdown: How Sudo Actually Parses Rules

#Last-Match-Wins Precedence

Sudo does not evaluate rules top-to-bottom and stop at the first match. It evaluates the entire file (and any included files via #includedir) and applies the last matching rule for a given user/host/command tuple. This means a restrictive rule at the top of the file can be silently overridden by a permissive rule appended later — a common outcome when configuration management tools append entries without deduplication logic. A single Ansible role adding a broad NOPASSWD: ALL line at the bottom of a drop-in file will override every carefully scoped rule above it, and nothing in the default output warns you.

#Wildcard Expansion Is Not Shell Globbing

This is the architectural trap most admins fall into. Sudo’s wildcard matching in the Cmnd_Spec field uses fnmatch(3) semantics, not shell globbing, and critically it matches against the literal argument string passed to the command, not the resolved filesystem path. A rule like:

1someuser ALL=(root) NOPASSWD: /usr/bin/find /var/log -name *

looks scoped to log inspection. In practice, find supports -exec, so any user with this grant can pivot directly to root via:

sudoers privilege escalation

1sudo /usr/bin/find /var/log -name '*' -exec /bin/sh ; -quit

This is not a bug in sudo — it is a design consequence of granting execution rights to a binary whose own capabilities exceed the scope the admin assumed. The sudoers privilege escalation risk here is entirely in the mismatch between what the admin thinks they authorised (log filtering) and what the binary can actually do (arbitrary command execution as root).

#Environment Variable Leakage via env_keep

By default, modern sudo builds enable env_reset, but fleets inheriting legacy configuration frequently carry forward Defaults env_keep += "LD_PRELOAD LD_LIBRARY_PATH" from older hardening guides that predate current best practice. Any command executed under a rule that preserves LD_PRELOAD allows an attacker to inject a shared object that executes arbitrary code inside the privileged process before main() even runs — a textbook sudoers privilege escalation primitive that bypasses command restriction entirely, because the exploit lives in the dynamic linker, not the sudo-restricted binary.

#Implementation Logic: Auditing Fleet-Wide Exposure

Manual review of visudo output does not scale past a handful of hosts. The correct approach treats sudoers policy as code, subject to the same review pipeline as your architectural patterns for infrastructure configuration generally. The audit workflow is:

  1. Extract the effective, merged policy per host (not just the raw file) using sudo -ll under each relevant account, since #includedir fragments and LDAP-sourced rules are invisible to a static file grep.
  2. Flag every Cmnd_Spec containing an unescaped *, ?, or bracket expression.
  3. Cross-reference flagged binaries against a known-dangerous list (find, vim, less, awk, systemctl, python*, tar, rsync — all documented shell-escape vectors on GTFOBins).
  4. Confirm whether NOPASSWD is set on any flagged rule, which removes the authentication checkpoint and materially increases the blast radius of a stolen session token or compromised CI job.
  5. Remediate via Cmnd_Alias definitions with fully qualified arguments, never bare wildcards.

#Code and Configurations

#Vulnerable Configuration

1# /etc/sudoers.d/deploy — written under deadline pressure
2deployer ALL=(root) NOPASSWD: /usr/bin/systemctl *
3deployer ALL=(root) NOPASSWD: /opt/scripts/*

The second line alone constitutes a full sudoers privilege escalation path: any process running as deployer — including a compromised build agent — can drop an arbitrary executable into /opt/scripts/ and run it as root without a password prompt, assuming write access to the directory, which is frequently the case for deployment tooling.

#Exploitation Path

1echo -e '#!/bin/shnchmod u+s /bin/bash' > /opt/scripts/setup.sh
2chmod +x /opt/scripts/setup.sh
3sudo /opt/scripts/setup.sh
4/bin/bash -p   # root shell, no password required

#Hardened Configuration

1# /etc/sudoers.d/deploy — remediated
2Cmnd_Alias SVC_RESTART = /usr/bin/systemctl restart nginx, 
3                          /usr/bin/systemctl restart app-worker
4Cmnd_Alias DEPLOY_SCRIPTS = /opt/scripts/deploy_v2.sh, 
5                             /opt/scripts/rollback_v2.sh
6
7Defaults!SVC_RESTART, DEPLOY_SCRIPTS !env_reset
8Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
9
10deployer ALL=(root) NOPASSWD: SVC_RESTART, DEPLOY_SCRIPTS

Note the removal of wildcards entirely in favour of an explicit, enumerated Cmnd_Alias. This is more verbose and requires a policy change per new script, but that friction is the point — it forces a review step instead of implicit trust in directory contents.

#Fleet-Wide Audit Script

1#!/usr/bin/env bash
2# scan-sudoers-wildcards.sh — flag risky Cmnd_Spec entries across a fleet
3set -euo pipefail
4
5HOSTS_FILE="${1:-hosts.txt}"
6DANGEROUS_BINS="find|vim|less|awk|python|perl|tar|rsync|systemctl|nmap"
7
8while read -r host; do
9  echo "== ${host} =="
10  ssh -o BatchMode=yes "${host}" "sudo -l 2>/dev/null" | 
11    grep -E '(\*|\?)' | 
12    grep -E "NOPASSWD|${DANGEROUS_BINS}" && 
13    echo "  [FLAGGED] wildcard + NOPASSWD or dangerous binary" || true
14done < "${HOSTS_FILE}"

Run this through your configuration management pipeline as a pre-deployment gate rather than an occasional manual sweep. Any CI job that pushes a new sudoers fragment should fail the build if the diff introduces an unescaped wildcard paired with NOPASSWD.

#Failure Modes and Edge Cases

Delegating sudoedit access to a path pattern introduces a time-of-check-to-time-of-use window. If the granted path is a directory rather than a fixed filename, an attacker can pre-create a symlink pointing at /etc/shadow before invoking the sudo-permitted edit, and depending on the sudo version and filesystem, the edit operation may follow the link rather than the intended target. Always grant sudoedit against fully resolved, non-symlinkable paths, and verify the sudo build is patched against the relevant CVE class for your distribution.

sudoers privilege escalation

#Glob Matching Unintended Binaries

A rule scoped to /usr/sbin/user* intended to permit usermod for a helpdesk account will also match userdel and, on some distributions, userhelper. The sudoers privilege escalation risk here is not injected code — it is simply granting more capability than intended because the admin reasoned about the rule in terms of the one binary they had in mind, not the full set the pattern actually resolves against on that specific filesystem layout.

#Concurrent visudo Edits Under Configuration Management

When multiple automation runs (Puppet, Ansible, Chef) write to the same sudoers drop-in file without respecting the lock that visudo enforces, you get partial writes on failure — occasionally leaving a syntactically broken file that sudo refuses to honour, locking out every account that depended on it, including the account running your remediation pipeline. Always write sudoers fragments atomically (write to a temp file, validate with visudo -c -f, then move into place) rather than editing in situ.

1visudo -cf /etc/sudoers.d/deploy.new && 
2  mv /etc/sudoers.d/deploy.new /etc/sudoers.d/deploy

#Environment Injection via sudo -E

Even with env_reset enabled, a rule that grants NOPASSWD on a command combined with a user’s ability to invoke sudo -E (if setenv is not explicitly disabled) can re-inject the full calling environment, resurrecting LD_PRELOAD-class attacks that the admin believed were closed off by the default reset behaviour.

#Scaling and Security Trade-offs

Centralising sudoers policy becomes necessary once the fleet crosses roughly 50–100 hosts, at which point local /etc/sudoers.d/ management stops being auditable by hand. The trade-offs differ materially by approach:

  • Local sudoers.d fragments — fast to deploy, zero network dependency, but drift undetected between hosts is common; a single stale fragment can persist a sudoers privilege escalation vector for months after the “fixed” version has been rolled out everywhere else.
  • LDAP/FreeIPA-backed sudo rules — single source of truth, immediate revocation across the fleet, but introduces a hard dependency on directory availability for privilege checks; an LDAP outage during an incident can either lock admins out entirely or, if fail-open policies are configured, silently grant broader access than intended.
  • Policy-as-code with CI validation — every sudoers change goes through pull request review and automated wildcard/NOPASSWD linting before merge; slower to iterate but produces an audit trail that satisfies compliance frameworks such as SOC 2 and ISO 27001 change-control requirements.
  • sudo logging granularity — enabling Defaults logfile and forwarding to a SIEM gives command-level visibility, but at high fleet scale the volume of benign wildcard-matched invocations can bury the genuinely anomalous ones unless you correlate against a baseline of expected command patterns per role.
  • Command aliasing overhead — fully enumerated Cmnd_Alias definitions eliminate wildcard risk entirely but multiply the maintenance burden; every new script or binary requires a policy change, which is precisely the friction that pushes under-resourced teams back toward wildcards in the first place.

The correct posture depends on your actual threat model and change velocity, but the default should always be enumerated commands over wildcards, and password-gated over NOPASSWD, with exceptions requiring explicit sign-off rather than being the path of least resistance during a rushed deployment.

None of this requires exotic tooling — it requires treating sudoers as a security-critical artefact subject to the same review discipline as firewall rules or IAM policies, rather than a convenience file edited under deadline pressure and never revisited.

Reader Interaction

Comments

Add a thoughtful note on Sudoers NOPASSWD: The Silent Path to Root. Comments are checked for spam and held for moderation before appearing.

Loading comments...

Loading anti-spam check...

Alistair Vance

Written By

Alistair Vance

Alistair Vance brings over fifteen years of experience architecting resilient, multi-region Kubernetes clusters for tier-one financial institutions. A core contributor to several CNCF incubation projects, his expertise lies in constructing automated self-healing infrastructures and establishing stringent service level objectives. He focuses relentlessly on operational excellence and eliminating toil through advanced eBPF-based observability.