Even without the immediate code-execution problem of Shellshock trailing commands, to what extent does the feature of bash importing functions from the environment give rise to a comprehensive privilege escalation vulnerability?

To be clear, there are many setuid programs, and some of them will directly or indirectly call scripts, or use the system(3) call, which on most Linuxes goes through bash. The question is to what extent, assuming systems have such setuid binaries installed, this feature of bash allows local privilege escalation.

  • For example if calling a script does it allow overriding of built-ins? If so can this give rise to an escalation of privilege in combination with a setuid executable and the system(3) call, by overriding e.g. ls, cd or something innocuous?
  • Would such an escalation vector require a script to be called (in which case one could potentially override ls or any command used by the script which didn't use a full path)?
  • Are imported functions potentially used at any point by the normal execution path of /bin/sh -c ARGUMENTS even when the first argument is not a script?
  • Could you simply give an environment variable the same name as the first argument to system?
  • If so does that mean that every call to system(3) by a setuid executable, on a system where /bin/sh is bash, is a privilege escalation vector?
  • Pretty good question! May I suggest you to shorten the title to: "Is the bash shell exportation of functions a risk?"
    – dan
    Commented Sep 29, 2014 at 12:02
  • 1
    If anything, remember that Shellshock is a confused deputy kind of problem: issues arise only when a higher privileged user processes your input carelessly without validating the environment variables you give, and in a specific way that involves Bash. What you describe would require that you control the code run by a higher privileges principal which is so insane and allows you to exploit directly without any Shellshock trick. The answers that say "yes Bash can lead to privilege escalations solely by redefining primitive foo or bar" are wrong. Commented Sep 29, 2014 at 16:00
  • 1
    @SteveDL, "What you describe would require that you control the code run by a higher privileges principal"... No it isn't. The issue is this does let you control the code run, if the environment is not sanitised, and most implementers are not aware of that fact therefore apparently adequate environment sanitisation may not in fact be adequate. Recall that Posix does not have this feature, and poor environment sanitisation is harder to exploit with bourne shell. You can't just override cd.
    – Ben
    Commented Sep 29, 2014 at 16:30
  • @Ben so you're saying that the problem is not just that a shell script behaves unexpectedly when run without sanitising untrusted input, but that it behaves unexpectedly in ways not anticipated by developers because they assumed the functions they called in their scripts to have a fixed meaning. You're right, I had overlooked this aspect. Though GNU already explicitly recommended using only absolute paths to commands because of PATH untrustworthiness so the same should've been assumed by consciencious devs when writing a shell script. Commented Sep 29, 2014 at 16:41

4 Answers 4


These tests are run on an unpatched system:

# ls='() { echo NO; }' bash -c 'ls'

the * wildcard is required or perl will replace system call with a fork/exec combination

# ls='() { echo NO; }' perl -e 'system("ls *");'

However ls is not a builtin:

# type ls
ls is hashed (/bin/ls)
# type echo
echo is a shell builtin

So lets try overriding built-in echo ("command" overides the built-in to avoid recursion):

# echo='() { command echo NO; }' bash -c 'echo YES'

So far we showed that:

  1. it can override a built-in
  2. and does not require a script to be called
  3. even when the first argument is not a script
  4. every call to "system" is a vector, giving privileges of the caller

I think naked "system" and "popen" are rotten, I think most uses of these don't bother to escape shell meta-characters in user data.

I have this invocation when I want to have the shell produce pipelines or use shell built-ins, but it still requires fork/exec:

exec("sh", "-c", "\"$0\" \"$1\" | tee -a \"$2\"", ...);

The technique gets bash to refer to the arguments of the -c commands, $0 being the first such argument, or "--" can be used as a filler:

exec("sh", "-c", "\"$1\" \"$2\" | tee -a \"$3\"", "--", ...);

This test is run on a patched system:

$ env "BASH_FUNC_ls()=() { echo NO ; }" bash -c 'ls'

showing clearly that this issue is nothing to do with shellshock.

While authors of scripts or script-using tools should know to validate user input, and should know to validate/clean the environment (as it contains user input) some parts of the environment can need to be preserved, and it can't always be easy to see which they might be;

XAUTHORITY, DBUS_SESSION_BUS_ADDRESS, XDG_SESSION_DESKTOP, SSH_AUTH_SOCK are a few and other important environment variables may not yet be invented

it is called "environment" for a reason. The PATH can maybe be sanitised, but the first command issued to system() can be surplanted

The sudoedit man page writes:

Running shell scripts via sudo can expose the same kernel bugs that make
setuid shell scripts unsafe on some operating systems (if your OS has a
/dev/fd/ directory, setuid shell scripts are generally safe).

However this will not be true for setuid bash shell scripts unless they refuse to import all bash functions

  • You don't need the help of the Shell Shock bug to find a basic risk with the bash exportation of functions.
    – dan
    Commented Sep 29, 2014 at 15:17
  • And then what? You can redefine your Bash as much as you want, when you call a setuid binary or run a new instance of Bash with a different UID this bears no relevance. In other words, there is no point in doing such circonvolutions when you can run the "new" commands directly with the same privileges. Commented Sep 29, 2014 at 15:57
  • 1
    @daniel, certainly one doesn't need shellshock to find a basic risk with bash exportation of functions, but a lot of people are now able to recognize the risk of bash exportation of functions because of shellshock. So they, at least, appear to have needed it. Commented Sep 29, 2014 at 20:56
  • 1
    @SteveDL - you may be right, I was just answering the question as asked. Whether or not this method can be used to smuggle an action into a script called by an suid binary, or a binary with magic network credentials, or one that can manage to call an unclean sudo, or in an selinux context, is another question. Commented Sep 29, 2014 at 20:58

"Can this give rise to an escalation of privilege in combination with a setuid executable...?"

You can't have setuid scripts on GNU so let's assume a setuid binary directly calling a Bash script. Note before starting that this is extremely poor design and is explicitly recommended against by GNU and again in the setuid manual. Also note that ld will strip the most nefarious environment variables and that it's not possible in GNU/Linux to inject code into a setuid process or to get a dump of it (unless you're root). So environment-based attacks do require a poorly-written setuid binary.

Shellshock plays absolutely no role in exploiting such setuid binaries. If the binary does follow sanity recommendations, it will not call any shell script, not call system and ensure that all exec* calls being performed are not influenced by untrusted environment variables, it will not be vulnerable to any privilege escalation by design. If the setuid binary does not validate untrusted input, it will be vulnerable anyway.

The reason why you shouldn't system or launch a shell is not because the environment chosen by the untrusted callee would be carried over to the new shell (that depends entirely on programming practices) but because if you had made any mistake you would allow a confused deputy attack with an unlimited scope rather than just allow specific operations: the system would just be compromised to a wider extent than in any other scenario.

Calling a setuid binary with a Shellshock-like environment from Bash will cause your instance of Bash to process the extra Bash primitives with your privileges, not the privileges of the setuid binary called later which should already assume a hostile environment anyway.

"Would such an escalation vector require a script to be called?"

Shellshock implies that Bash is invoked with an environment that is under the control of a principal who has lower privileges that the one invoking Bash in the first place.

If noone is invoking Bash, your Shellshock payload is useless since it won't be processed (just like an OpenSSL exploit doesn't work on Microsoft Word).

"Are imported functions potentially used at any point by the normal execution path of /bin/sh -c ARGUMENTS even when the first argument is not a script?"

If the argument is a shell script and /bin/sh points to Bash, then Bash can be exploited. However there is strictly no interest whatsoever in doing this since you're using your own privileges to run the command. You don't gain any privileges (unless assuming a SELinux or AppArmor setup where you have more privileges by using /bin/sh -c than directly calling something; such a setup would be broken and not intentional).

If the argument is a binary then the binary gets called. Does Microsoft Word use functions from a concurrently running Firefox? No. The same is true here: the loader will load the binary's code and run the binary, it will not run buggy code from another binary. The only scenario where you could change how a binary behaves is via code injection and you can't do code injections on setuid processes.

"Could you simply give an environment variable the same name as the first argument to system?"

Yes, but system does not read an environment variable, it executes a command. Environment variables are just a dictionary of name:string pairs. They do not replace every single text that will be used in the future regardless of semantics. A program must make explicit use of an environment variable by reading it and deciding to care about it.

"If so does that mean that every call to system(3) by a setuid executable, on a system where /bin/sh is bash, is a privilege escalation vector?"

Absolutely not. A call to system by a setuid binary is just a poor idea because if there was a vulnerability, the consequences would be ridiculously desastrous. A call to system on a Bash-using OS changes nothing to the fact that privilege escalations require two principals: Shellshock would cause a privilege escalation only when a higher privileged principal decides to call Bash with an unverified environment provided by a lower privileged principal.

In most cases such a system call would already be undesirable because there are several non-Shellshock ways in which such a set up could turn awry, and the precautions to take to avoid existing confused deputy attacks would prevent Shellshock as well.

  • 3
    This question is not about shellshock (though it is prompted by it) it is about whether the feature of bash is a security vulnerability even if correctly implemented. I grant that some environment variables like PATH and LD_LIBRARY_PATH are problematic with setuid anyway, but it seems like this feature broadens the problem to arbitrary environment variables are problematic.
    – Ben
    Commented Sep 29, 2014 at 16:02
  • 3
    As per your last comment, "Absolutely not", your answer seems to in reality be "Absolutely yes, but they shouldn't be doing it". And yet a trawl of Stack Overflow shows that people are doing this all the time.
    – Ben
    Commented Sep 29, 2014 at 16:04
  • 1
    There is nothing about the entirety of Bash that is relevant to the execution of setuid binaries. A high-level process should always validate all lower level input. Regardless of whether it calls Bash or something else, and regardless of whether that input is environment variables or other parameters to any computation it performs. Commented Sep 29, 2014 at 16:05
  • 1
    @Ben: actually, dash is more vulnerable. Bash takes precautions when running set[ug]id (e.g: sanitizing PATH IIRC), which dash does not.
    – ninjalj
    Commented Sep 30, 2014 at 19:10
  • 1
    A bit more seriously, of course you're right to point out that Shellshock is an attack vector: it systematises attacks that would otherwise be delicate to trigger (would need env vars to be read). My point is just that Bash is less broken than the vulnerable callers as the callers don't just have a bug: they have multiple design flaws. All these apps are exploitable in less easy ways but their devs are not aware of it and Bash gets all the blame. Commented Sep 30, 2014 at 20:18

If your set(u|g)id binary preserves its real [ug]id, the answer is no.

When e[ug]id != real [ug]id, and not invoked in privileged mode (-p), bash drops its privileges:

Bash shell.c:main()

  if (running_setuid && privileged_mode == 0)
    disable_priv_mode ();

When running in privileged mode, or when bash is patched to not drop privileges when called as /bin/sh (e.g: Debian has such a patch, to avoid breaking old set[ug]id programs, such as UUCP), bash doesn't import functions from the environment. Bash checks if it is running set[ug]id, or in privileged mode (-p), and doesn't import functions from the environment if so.

Bash shell.c:main()

  /* Initialize internal and environment variables.  Don't import shell
     functions from the environment if we are running in privileged or
     restricted mode or if the shell is running setuid. */
#if defined (RESTRICTED_SHELL)
  initialize_shell_variables (shell_environment, privileged_mode||restricted||running_setuid);
  initialize_shell_variables (shell_environment, privileged_mode||running_setuid);

Bash variables.c:initialize_shell_variables()

  /* If exported function, define it now.  Don't import functions from
 the environment in privileged mode. */
  if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4))


The proof that the function exportation of bash is a risk alone is pretty simple:

export SHELL
ls() { echo executing /tmp/booby_trapped_ls ; }
declare -fx ls
perl -e 'system ("ls *") ;'
  • Note: this isn't using the Shell Shock bug.
    – dan
    Commented Sep 29, 2014 at 15:18
  • Please demonstrate that you can cause a properly written setuid binary to misbehave simply by calling it from Bash. Commented Sep 29, 2014 at 15:58
  • Note: this is a correct answer to the OQ. Of course, from a theoretical point of vue, such a vulnerable setuid binary shouldn't exist. But from a practical point of vue, the reality is slightly different… as everydays bug remind us :).
    – dan
    Commented Sep 29, 2014 at 16:24
  • Your example is no more than an alias with syntactic sugar. If I typed what you wrote then I would get perl to execute booby_trapped_ls with... my own privileges. Commented Sep 29, 2014 at 16:33
  • 1
    Also, I note from the sudoedit man page, this advice: "If your OS has a /dev/fd/ directory, setuid shell scripts are generally safe." - unless it's bash, whose actions can easily be re-defined even if PATH is protected Commented Sep 29, 2014 at 21:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .