Can Someone Explain How This "Shellshock" Code Works in Shell

Can someone explain the Shell Shock Bash code?

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

what env does?

From the docs, env runs programs in modified environment

env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]

it clear that x is a name/variable and () { :;}; echo vulnerable' is the value fo the variable

now what is () { :;};?

when a function is exported, bash stores its defenition as value to the environment variable

$ x() {echo hello world;}
$ export x
$ env | grep x
x=() {echo hello world};

now when x='() {:;}' means similar as writing

$ x() {:;}
$ export x
$ env | grep x

That is we indirectly made export x onto the new environmnet created by the env
Here : is a null statement in bash

Hope it helps

Is the behavior behind the Shellshock vulnerability in Bash documented or at all intentional?

This seems like an implementation bug.

Apparently, the way exported functions work in bash is that they use specially-formatted environment variables. If you export a function:

f() { ... }

it defines an environment variable like:

f='() { ... }'

What's probably happening is that when the new shell sees an environment variable whose value begins with (), it prepends the variable name and executes the resulting string. The bug is that this includes executing anything after the function definition as well.

The fix described is apparently to parse the result to see if it's a valid function definition. If not, it prints the warning about the invalid function definition attempt.

This article confirms my explanation of the cause of the bug. It also goes into a little more detail about how the fix resolves it: not only do they parse the values more carefully, but variables that are used to pass exported functions follow a special naming convention. This naming convention is different from that used for the environment variables created for CGI scripts, so an HTTP client should never be able to get its foot into this door.

How does CVE-2014-7169 work? Breakdown of the test code

The bug

CVE-2014-7169 is a bug in bash's parser. Bash's parser uses a variable eol_ungetc_lookahead to ungetc characters across lines. That variable wasn't being properly reset from the reset_parser function, which is called e.g. on some syntax errors. Using that bug, it's possible to inject a character into the start of the next bash input line.

So the test code forces a syntax error, using either (a)= or function a a, adds the redirection character to prepend to the next line >, and adds a line continuation \, which leads to either version of the test code:

() { (a)=>\
() { function a a>\

When bash is executed, it processes variables from the environment, finds that variable X is a exported function, and evaluates it to import the function. But the evaluation fails with a parse error, leaving the > character in the eol_ungetc_lookahead variable. Then, when parsing the command argument echo date, it prepends the > character, leading to >echo date, which runs date redirected to a file named echo.

Its relation to the previous bug

The above bug is obviously very different to the original shellshock bug. There are actually several problems:

  • Bash evaluates completely a variable that looks like an exported function (starts with the four characters () {). CVE-2014-6271.
  • Under some conditions, it is possible to inject a character into an ungetc variable, that will be prepended to the next input line. CVE-2014-7169.
  • Bash allows every environment variable to be treated like an exported function, so long as it starts with the four characters () {. CVE-2014-6271, CVE-2014-7169, all the other CVEs where a bug is triggered in bash's parser.
  • There is a limited stack for here-doc redirection, and there is no check for overflow. CVE-2014-7186, which leads to memory corruption, and can probably be leveraged for arbitrary code execution.
  • There is a limited stack for nested control structures (select/for/while), with checks for overflow. That stack is still corrupted. CVE-2014-7187.

The fixes

  • The first patch restricts bash to evaluating a single function definition in each variable that looks like a exported function.
  • The second patch properly resets eol_ungetc_lookahead on reset_parser.
  • The third patch changes how functions are exported: now they are exported in variables named BASH_FUNC_functionname%%.

Attack surface

The big problem here has been that every environment variable could be used as a vector for attack. Typically, attackers cannot control arbitrary environment variables, otherwise there are already other known attacks (think of LD_PRELOAD, PATH, IFS, ...).

sudo is not affected because it strips exported bash functions from the environment, as mentioned by Gilles on security.SE.

ssh is affected. Typical sshd installations only allow a limited set of environment variables to be exported as configured in AcceptEnv in sshd_config, e.g: LANG and LC_*. Even with this aggressive whitelisting approach, in shellshock any variable could be an attack vector.

Not only was every environment variable a potential attack vector, they exposed a >6000 lines parser.

Lessons relearned

system, popen, and others are potentially dangerous. Not only should you take care with their arguments: even when the arguments are fixed at compile-time, the environment is a potential attack vector. Use fork()/execve(), preferably with a clean environment (but at least restrict the environment to white-listed variables, preferably with their values sanity-checked). Remember that a good quality system does what it is supposed to do, while a secure system does what it is supposed to do and nothing more. Invoking a full-blown shell makes doing nothing more a little bit harder.

Complexity is the enemy of security. These days you can easily find people recommending simpler shells. Most shells are free from shellshock by not supporting exported functions at all. Conversely, bash has received lots of security features over the years (you need to invoke it with -p to avoid it dropping privileges on startup, it sanitizes IFS, ...), so don't assume I'm advocating switching shells, this is more of a general advice.

Some excerpts from David Wheeler's ancient "Secure Programming for Linux and UNIX HOWTO" chapter on environment variables are still worth rereading.

§5.2.3 ¶1:

For secure setuid/setgid programs, the short list of environment
variables needed as input (if any) should be carefully extracted. Then
the entire environment should be erased, followed by resetting a small
set of necessary environment variables to safe values. There really
isn't a better way if you make any calls to subordinate programs;
there's no practical method of listing ``all the dangerous values''.

§5.2.3 ¶6:

If you really need user-supplied values, check the values first (to
ensure that the values match a pattern for legal values and that they
are within some reasonable maximum length).

Root Cause of Shellshock Bash bug

C's system() function, used to execute an arbitrary external command, passes its argument to /bin/sh for parsing. (This is specified by POSIX.)

/bin/sh, on Linux systems, is commonly a symbolic link to /bin/bash.

If Apache invokes any external program via system(), and an attacker has been able to insert malicious code into any environment variable, it will be passed on to /bin/bash.

I'm having difficulty understanding the Shellshock vulnerability verification

Bash recognizes an environment variable as a function if it starts with precisely the four characters () {, including the space. So env x='(){ :;}; echo vulnerable' doesn't count.

This doesn't quite conform to the syntax you use to define a function in bash; internally, bash will store the string representation of a function in a normalized form. If the function is exported (with export -f function_name), then the normalized form is added to the environment, and child bash processes will recognize it as a function definition.

The "shellshock" bug comes from the way bash handles recognized functions; the buggy versions of bash (which go back a long way) simply evaluate the string from the environment as a function definition (by prepending the name of the variable as the function name), which is subject to an injection attack as demonstrated in the vulnerability test.

Manually creating strings which look like bash function definitions in order to define functions in child bash processes is a known technique. Exporting functions and reimporting them is very common, and often is not even noticed by the user. (For example, this technique is used to pass bash functions into subshells started by xargs bash -c and find ... -exec bash -c.)

Can not exploit shellshock vulnerability for all files

In order to exploit the shellshock bug, the following steps need to occur:

  1. you must get the target server to inject a specific string into an environment variable, and

  2. after setting the environment variable, the target must (directly or indirectly) launch (a vulnerable version of) the bash shell.

One way this can happen is when a web server executes an external program through the Common Gateway Interface (CGI), and the external program either is a bash shell script, or otherwise invokes bash. This happens because CGI protocol specifies that headers from the original HTTP request are passed to the external program in environment variables with the prefix HTTP_ (e.g. HTTP_USER_AGENT for the User-Agent header). Since the attacker can control those headers, they can also directly control the values of those environment variables.

However, the web server needs to set those environment variables only when invoking an external program via CGI. When delivering a static file, the server only needs to read that file from the disk and send it back to the client; it doesn't need to set any environment variables or invoke any external tools like bash.



Related Topics



Leave a reply



Submit