13

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?
Ben
  • 3,697
  • 1
  • 18
  • 24
  • Pretty good question! May I suggest you to shorten the title to: "Is the bash shell exportation of functions a risk?" – dan Sep 29 '14 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. – Steve Dodier-Lazaro Sep 29 '14 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 Sep 29 '14 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. – Steve Dodier-Lazaro Sep 29 '14 at 16:41

4 Answers4

6

These tests are run on an unpatched system:

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

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

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

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'
NO

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'
NO

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 Sep 29 '14 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. – Steve Dodier-Lazaro Sep 29 '14 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. – Sam Liddicott Sep 29 '14 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. – Sam Liddicott Sep 29 '14 at 20:58
2

"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.

Steve Dodier-Lazaro
  • 6,798
  • 29
  • 45
  • 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 Sep 29 '14 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 Sep 29 '14 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. – Steve Dodier-Lazaro Sep 29 '14 at 16:05
  • The call to `system` is not itself the vector. It's the lack of input validation that causes escalations. System just happens to be what the escalation affords, and system is recommended against because it offers more possibilities to attackers than a single-purpose chunk of code. – Steve Dodier-Lazaro Sep 29 '14 at 16:06
  • Thanks for your comments though! I'll rework the answer around Bash itself when I have a minute. – Steve Dodier-Lazaro Sep 29 '14 at 16:07
  • "The call to system is not itself the vector. It's the lack of input validation that causes escalations". No. Exploitation requires both. A Posix shell doesn't have to have this problem - see Dash. This feature of Bash makes a vulnerability out of something otherwise safe. – Ben Sep 30 '14 at 09:11
  • 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 Sep 30 '14 at 19:10
  • @Ben you're right! The fact that Bash (or any other program) processes the input it is given makes it insecure ;) ! It's the privileged binary that decides to call Bash or Dash or whatever else that should be able to define what is safe to do and what isn't, because only that binary can decide what is trusted and what is untrusted input. What you're saying is like saying that "unlink" is unsafe because it can be used to cause a confused deputy to delete files. The tool is *not* the issue. The bug in the parser is almost anecdotal compared to the fact that environment is passed unverified. – Steve Dodier-Lazaro Sep 30 '14 at 20:14
  • 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. – Steve Dodier-Lazaro Sep 30 '14 at 20:18
2

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);
#else
  initialize_shell_variables (shell_environment, privileged_mode||running_setuid);
#endif

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))
ninjalj
  • 121
  • 3
-1

Yes!

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

/bin/bash
SHELL=/bin/bash
export SHELL
ls() { echo executing /tmp/booby_trapped_ls ; }
declare -fx ls
perl -e 'system ("ls *") ;'
dan
  • 3,033
  • 14
  • 34
  • Note: this isn't using the Shell Shock bug. – dan Sep 29 '14 at 15:18
  • Please demonstrate that you can cause a properly written setuid binary to misbehave simply by calling it from Bash. – Steve Dodier-Lazaro Sep 29 '14 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 Sep 29 '14 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. – Steve Dodier-Lazaro Sep 29 '14 at 16:33
  • Exactly. Since there is no protection mechanism which prevent a `setuid` binary to do exactly the same thing, what does avoid too many `setuid` binaries to do this are just **good practice**. And everyone knows that they fail with a probability. Bugs arround the `setuid` mechanism since its inception are just a rough estimate of this probability. – dan Sep 29 '14 at 16:43
  • 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 – Sam Liddicott Sep 29 '14 at 21:02