Bash Environment Safety
Bash environment safety is the practice of controlling the variables and search path a script inherits. A hostile value in PATH or in an exported variable can redirect a trusted command.
A planted directory shadows a real command
A fake ls is planted in a directory placed first in PATH. Running ls then runs the fake, not the system command.
mkdir fakebin
printf '#!/bin/sh\necho "fake ls ran"\n' > fakebin/ls
chmod +x fakebin/ls
PATH="fakebin:$PATH"
lsenv -i drops inherited variables
A poisoned variable is exported to child shells. A plain child sees it, a child given one clean value sees that value, and an env -i child sees nothing at all.
export GREETING="poisoned"
bash -c 'echo "inside: $GREETING"'
env -i GREETING="clean" bash -c 'echo "inside: $GREETING"'
env -i bash -c 'echo "inside: ${GREETING:-empty}"'env -i drops an exported fake function
A fake ls function is exported, so a plain child shell runs the fake. A clean child shell runs the real ls, which lists only the script file itself.
ls() { echo "hijacked ls"; }
export -f ls
bash -c 'ls'
env -i bash -c 'ls'
echo "clean shell"Example
mkdir fakebin
printf '#!/bin/sh\necho "fake ls ran"\n' > fakebin/ls
chmod +x fakebin/ls
export PATH="fakebin:$PATH"
export GREETING="poisoned"
echo "unsafe:"
ls
echo "safe:"
env -i PATH="/usr/bin:/bin" bash -c 'ls; echo "greeting: ${GREETING:-empty}"'Key points
- PATH search: Bash finds a command in the first matching directory listed in PATH.
- PATH attack: a directory placed first in PATH can shadow a system command with a fake one.
- Fixed PATH: set PATH to known system directories before running outside commands.
- env -i: env -i starts a command with an empty environment, dropping inherited poison.
- Exported functions: Bash can export a function, and a child shell may run it instead of the real command.
- Clean child: env -i also drops exported functions, so the child finds the real command.
- Trust boundary: never run a script with an environment you did not build or check.
In this example, a fake ls is planted first in PATH and a poisoned variable is exported. The unsafe call runs the fake. The safe call rebuilds the environment from scratch with a fixed PATH, so the real ls lists the directory and the poisoned variable is gone.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
Why should a script that runs outside commands reset PATH first?