Sunday, September 28, 2014

ShellShock: Vulnerability, Patch and Exploit Analysis

On September 24th 2014, the World became aware of a very serious vulnerability (CVE-2014-6271 also known as the ShellShock vulnerability) in the widely used Bash shell. Bash is by far the most popular shell and is the default shell distributed with Linux and Mac OS X. In addition, it is used by many computers and servers that are exposed to the Internet. The patch was released on September 24th along with the announcement of the vulnerability, but attackers on the Internet relied on the fact that not everyone was going to promptly patch their computers and therefore started attacking vulnerable machines on the Internet.

The ShellShock vulnerability itself was a simple logic flaw in the Bash code rather than a memory corruption, making it very easy for a large audience to understand and exploit. To make matters worse, this vulnerability is “wormable” in that it does not require user interaction, allows untrusted/unauthenticated users to execute code on a remote machine and its exploit can be embedded into a malicious script to automate its proliferation around the Internet at an exponential rate. Some currently known attack vectors include (1) CGI-based web servers, SSH servers and DHCP clients, all of which are widely used services around the Internet.

The Vulnerability:
The vulnerability itself was the simple fact that Bash allowed the user to define functions and include multiple commands inside environment variables that are passed to Bash. This is dangerous in situations when the value of the environment variable passed to the new Bash instance is supplied by an unauthenticated and untrusted source.

The Fix:
The Fix for the ShellShock vulnerability was implemented in the patch “bash43-025” released on the GNU FTP website (2). Below is the relevant annotated code change inside Bash 4.3, which is the newest supported version of Bash.

//CVE-2014-6271: executed with “testbug = () { :;}; echo VULNERABLE”
void initialize_shell_variables (env, privmode)
  char **env;
  int privmode;
{
  ...
  /*
    Now, name = env variable name,
    string = env variable value
    char_index == strlen (name)

    temp_string = "testbug () { :;}; echo VULNERABLE"
    name = "testbug"
    SEVAL_FUNCDEF is OR'ed in below indicating that this was a
    function definition
    SEVAL_ONECMD is OR'ed in below to indicate that we only want
    a single command
  */
  if (legal_identifier (name))
    parse_and_execute (temp_string,
    name,
    SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);
  ...
}


int parse_and_execute (string, from_file, flags)
  char *string;
  const char *from_file;
int flags;
{
  ...
  while (*(bash_input.location.string))
  {
    ...
    /*
      1) SEVAL_FUNCDEF was OR'ed into line that called this function
      2) command->type == cm_connection != cm_function_def
      Therefore we execute inside the if statement, which produces an error.
      EX_BADUSAGE is eventually returned from this function.
    */
    if ((flags & SEVAL_FUNCDEF) && command->type != cm_function_def)
    {
      internal_warning ("%s: ignoring function definition attempt", from_file);
      should_jump_to_top_level = 0;
      last_result = last_command_exit_value = EX_BADUSAGE;
      break;
    }
    ...
    /*
      If we didn't go into the above if statement, and we only want one
      command, break out of the while loop now...
    */
    if (flags & SEVAL_ONECMD)
      break;
  }
  ...
}

The Exploit:
The exploit code (3) was also very simple. The below Bash script code triggers the vulnerability and prints “VULNERABLE” to the screen on vulnerable versions of Bash.

export testbug='() { :;}; echo VULNERABLE'
bash

The first line defines a new environment variable called testbug. This new environment variable contains just a function definition (no actual call to the function) followed by an additional command. The function definition is just “() { :;};” which is an unnamed Bash function which has the expression ':' inside of it. In Bash, when the ':' character is evaluated as an expression, it just evaluates to true (4). The function definition is followed by another command which in this case, just prints “VULNERABLE” to the screen. The second line executes a new instances of Bash which will consume this environment variable. This explicit execution of bash is analogous to a web server spawning a new instance of bash.

Other Events:
After the original patch was released for the original ShellShock vulnerability, the community started to find new variants of bugs in Bash and has since assigned multiple new CVEs to these bugs. As of the time of this writing, the discoveries are ongoing. Considering how long ago Bash was written, the old coding style, and the amount of string manipulation that is happening in C, it is very likely that new vulnerabilities (especially memory corruptions) will continue to be found.

Workarounds:
A possible way to avoid execution of vulnerable codepaths includes avoiding setting environment variables to untrusted user supplied input. This advice takes into consideration that new and currently unknown attack vectors and variants are likely to be discovered in the upcoming days and weeks.

References