Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage News ShellShocked - Behind the Bug

ShellShocked - Behind the Bug

This item in japanese

The now infamous Bash bug CVE-2014-6271, which subsequently became known as "ShellShock", allowed remote execution of code through carefully crafted data sent across a network to a server that directly or indirectly ran a bash script. The initial bug was fixed, but subsequent zero-day concerns in the parsing routines resulted in a second vulnerability CVE-2014-7169 being disclosed and subsequently fixed over the weekend. But where did the vulnerability come from, and have we seen the last of these type of bugs? FreeBSD and NetBSD have disabled automatically importing functions by default to prevent future vulnerabilities.

The problem occurs because a feature (not a bug?) in the Bash shell allows functions to be defined in environment variables. Functions allow commonly called code to be defined and then re-used in later places, and are available in all shell script languages. Functions in Bash (and most other shells) can be defined as follows:

function hello {
 echo "Hello"
hello # call the function

However, Bash also has a means to define a function using environment variables, which is unique to Bash. An environment variable whose value starts with the characters (){ is treated as an implicitly imported function definition, which takes only takes effect on shell start-up:

$ export HELLO="() { echo 'Hello'; }"
-bash: HELLO: command not found
$ bash

Because it only takes effect at shell start-up, most of the examples demonstrating the vulnerability have been one-liners of the form:

env HELLO="() { echo 'Hello'; }" bash -c HELLO

which does the same thing as the above. (The env command says 'run the following program with this environment variable set' in a portable way; in fact, HELLO="() { echo 'Hello'; }" bash -c HELLO works just as well.) The one-line bash command (specified with -c) is then necessary to show the execution, which has to be in a new shell since it is only parsed at start-up.

The initial flaw in the parsing code was executing code after the function definition, so:

env CVE_2014_6271="() { echo 'Hello';}; echo 'Goodbye'" bash -c CVE_2014_6271

should only print out 'Hello' on fixed systems, but on vulnerable systems will print out 'Goodbye' as well. The problem was the auto-import function parser ran past the end of the function definition and kept executing the following code - and since this happens each time a new Bash shell starts, arbitrary code can be executed.

The problem is magnified for two reasons: firstly, Bash is a widely installed piece of software, running on everything from Rapsberry Pis through mobile phones to servers in data centres and mainframes. Since the auto-import function functionality has been present since at least Bash 3.0, the bug goes back through some 20 years of existence and is available on most systems. Secondly, when running Apache servers using scripts with mod_cgi (but not mod_php or mod_python), the data is passed through environment variables dating to some of the earliest techniques on the internet. It also doesn't help that some other clients — like the Linux DHCP client — largely use Bash scripts to perform changes, which allows for bad data in DHCP packets to be exploitable as well.

This, coupled with Bash being the default shell on most Linux (and OSX) systems means that passing an poisoned environment variable through to a server running a CGI script could trivially be compromised. For example, the HTTP header User-Agent is typically passed as an environment variable HTTP_USER_AGENT, which means that an exploit is as simple as writing:

curl -A "() {:;}; echo 'Game Over'}"

For servers that don't pass the user agent through, there are often other choices that will be — like Cookie, or the request method itself.

Note that the bug didn't affect just CGI scripts and Apache — if variables were poisoned in a similar way to other programs that pass values through (such as ssh with variables like TERM or DISPLAY) and those processes ran a Bash script (or implicitly executed them with system() calls) then the same vulnerability could be exploited. Unlike HTTP the requests cannot be made anonymously — a login is required to be able to trigger it — but with code hosting providers allowing free log-on (even to a restricted shell) GitHub provided updates for its enterprise product and Bitbucket updated its servers to remediate against any possible incursion.

So with the above bugs fixed in CVE-2014-6271, the immediate issue was resolved, and most vendors made fixed builds available ahead of the embargo date. There is no reason why internet-facing servers should not have already had this fixed, since it gives complete control (as the user the Apache server is run as) of the hosting machine.

However, with the focus being placed on this area, new bugs were identified. Using a combination of Bash's shell redirection and the auto-imported functions, CVE-2014-7169 was raised. This allowed the reading and writing of files on the remote machine, using the same techniques as before, but this time with the shell redirection characters of < or >.

env CVE_2014_7169='() { (a)=>\' bash -c "echo date"; cat echo

This time the parser falls over at the = sign (since the expression (a)= does not make sense in Bash) but crucially leaves the redirection character > in the pipeline. Together with the escape character \ this results in some (ignorable) whitespace between the redirection character resulting in translating the the subsequent command into a redirection:

>echo data

This is valid Bash and is semantically equivalent to the more common form:

date >echo

Note that the other redirection operators are in force here, including being able to consume files with <

So with this bug fixed as well, others have wondered whether fixing edge cases in Bash's auto-import function functionality has become a game of whack-a-mole; whenever one bug gets squashed, another one pops up elsewhere. Even before this bug was raised there were concerns about being able to use Bash's auto-import functionality to be able to override standard system executables that aren't fully qualified:

$ env ls="() { echo 'Game over'; }" bash -c ls
Game over
$ env ls="() { echo 'Game over'; }" bash -c /bin/ls
Applications Desktop Documents Downloads ...

(Prior to the recent patches, it was possible to override a fully qualified path with env /bin/ls="() { echo 'Game over'; }" bash -c /bin/ls but this has been fixed with the fully patched versions to date).

However, many executables don't have the path encoded in them, which means that a suitably named variable can be used to do things that they shouldn't be able to:

$ touch /tmp/a /tmp/b
$ env test='() { echo vulnerable >&2; }' /usr/bin/bzdiff /tmp/a /tmp/b
bzip2: Can't open input file a.bz2: No such file or directory.

Of course, being able to set arbitrary environment variables allows the attacker to control any number of things — changing the IFS environment variable (which has been used in the past as an exploit), or even the PATH will affect the behaviour of newly started shell scripts, and these techniques are likely to cause problems anyway. At least with both the prior SSH and CGI attacks, the variables that are passed through are either a limited set (TERM, DISPLAY etc) or have prefixes applied (HTTP_USER_AGENT,HTTP_REFERRER). so unless there are commands with those names the scope is much more limited.

Still, some expect that this isn't the last security vulnerability seen in Bash's use of auto-imported functions and are concerned that future bugs will be discovered. NetBSD has disabled auto-importing of functions by default in Bash, and so has FreeBSD, choosing to provide this as an opt-in function when invoked with a newly added --import-functions option. Although this technically is a backwardly breaking change, at this point paranoia may make sense, and it may not be long before such an option is made available upstream. A patch for OSX users is also available.

InfoQ will be monitoring developments and report changes when they happen.

UPDATE 29 September: the above mentioned flaw has been fixed in the latest series of patches. Releases have been made available from all vendors except Apple to resolve this issue.

UPDATE 30 September: Apple have released Bash Security Fix 1.0 to address the remote execution vulnerability but some vulnerabilities still exist.

Rate this Article