Knowledge Required: Low

If you’ve arrived on this site, you’ve probably fallen guilty to writing scripts without using absolute paths. Why not? After all, it’s far easier to just type uname in your shell scripts instead of /usr/bin/uname. Today’s post will highlight why this is poor secure coding practice, and how it could potentially lead to exploitation as a result.

Path poisoning

To understand how attackers can exploit this coding laziness, we first need a bit of background into how most shells use a path. Take the scenario where your script includes a common linux command like uname.

When the uname command is called, this is an executable located somewhere on your computer. So how does the shell find out where it is? Rather insecurely for the most part. The PATH variable is loaded into memory when a shell is spawned. This contains a list of directories the shell can use to find the location on disk of an executable called by a user. You can see the directories included in your path quite easily:

nathan@Angelina /tmp $ echo $PATH
/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/VMware Fusion Tech Preview.app/Contents/Public:/App

The path is a colon-delimited list of directories to look in

We can ‘poison’ this by adding our own directory which hosts a series of counterpart malicious binaries that are commonly referenced in scripts. Demonstrated below, I am in the directory /Users/nathan/Documents/dangerzone and so this will be added to our path:

nathan@Angelina ~/Documents/dangerzone $ PATH=$(pwd):$PATH

Add our current working directory to our path, adding it at the start of the path

If we echo our path again, we can now see our dangerzone directory is included, meaning that the system will enumerate that directory first when searching the other directories for executable files:

nathan@Angelina ~/Documents/dangerzone $ echo $PATH       
/Users/nathan/Documents/dangerzone:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/VMware Fusion Tech Preview.app/Contents/Public:/Applications/Wireshark.app/Contents/MacOS

Creating our malicious binary and example script

To demonstrate the simplicity of a path poisoning attack, I’ve generated (a potentially malicious) version of the command uname. I have stored this in our dangerzone folder and we can see when it’s run, it actually executes the whoami command:

nathan@Angelina ~/Documents/dangerzone $ ./uname -a
nathan

Now we have a malicious binary, let’s demonstrate an example of why you should use absolute paths. We will use the uname command in a new script. We call uname twice. One of the calls will use an absolute path, and one will be lazy and not use an absolute path:

#!/bin/bash 


# this will return the expected output
/usr/bin/uname -a 

echo " "

# this will not return the expected output
uname -a

Running the script

Using the above script, we can execute this to see the true value of path poisoning:

nathan@Angelina ~/Documents/dangerzone $ ./test_script.sh  
Darwin Angelina.local 21.4.0 Darwin Kernel Version 21.4.0: Fri Mar 18 00:46:32 PDT 2022; root:xnu-8020.101.4~15/RELEASE_ARM64_T6000 arm64
 
nathan
nathan@Angelina ~/Documents/dangerzone $ 

So what happened?

  • Our first call of the command uname returned the expected output of our kernel information because we told the scripting interpreter exactly where to find the binary file we called.
  • The second call of the uname command didn’t include an absolute path, so the interpreter enumerated the path to find uname and because our malicious version was returned in the path first, it ran that.

What most people would see as lazy coding has actually cost us, by giving attackers a method of running malicious binaries.

Why this matters?

Some may be viewing this as a low risk. In the example, even with path poisoning, we can see this hasn’t allowed us to elevate privileges through the output of the whoami command. However, this was a trivial example and path poisoning allows you to change the flow of execution without needing to actually alter the file. Furthermore, there’s no indication from within our original script to detect that path poisoning occurred.

If available to exploit, attackers can use this against scripts where they do not have write permissions but need to change execution flow, in order to combine with other exploits. Code reviews should regularly cover commonly used shell scripts to make sure they include absolute paths; especially those scheduled via cronjob as these may run with higher privileges. Path poisoning has been around for some time, so this is by no means a new ’exploit’ but should be considered in your secure coding practices.

EOF break