Research and Development

Some bugs are so simple and so elegant that you wonder how it is possible that no one has found them until now. Those are my favorites. They are simple, they do not involve memory corruption and most of the time they do not even need an advanced exploit code to abuse it. Stéphane Chazelas’ Bash bug is one of these bugs.

Note: Many other bugs related to Bash parser were discovered in the past few days such as CVE-2014-6277, CVE-2014-7169, CVE-2014-7186, CVE-2014-7187 and some others that have not yet been made public. This article is only about CVE-2014-6271.

Bash and functions

With Bash it is possible to define functions. This is done as follows.

$ myfunction () { id; }
$ myfunction
uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),108(sambashare),113(libvirtd),114(lpadmin)

Bash also comes with a nifty feature. It allows functions to be exported and passed as an environment variable. This is done as follows:

() { id; }

It is possible to pass this function to a new bash process.

$ myfunc='() { id; }' bash
$ myfunc
uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),108(sambashare),113(libvirtd),114(lpadmin)

Indeed, when the myfunc function is called, the id command is executed.

Installing a testing environment

First install Ubuntu 14.04.1 LTS available here:

Make sure you have a vulnerable bash version :-)

# wget
# dpkg -i bash_4.3-6ubuntu1_amd64.deb


Let’s have a look at bash 4.2 source code to see how it works.

First, in the general.h file, the STREQN() function is defined. It is a simple wrapper to the strncmp() function.

/* String comparisons that possibly save a function call each. */
#define STREQN(a, b, n) ((n == 0) ? (1) \
				  : ((a)[0] == (b)[0] && strncmp(a, b, n) == 0))

The way environment variables are handled is done in the initialize_shell_variables() function located in the variable.c file. It loops through each variables and if it finds an exported function, it defines it by running it. To catch if it is an exported function, it compares the string’s content with “() {“. If found, the complete string is evaluated and executed using the parse_and_execute() function.

initialize_shell_variables (env, privmode)
     char **env;
     int privmode;
  char *name, *string, *temp_string;
  int c, char_index, string_index, string_length;
  SHELL_VAR *temp_var;


  for (string_index = 0; string = env[string_index++]; )


      /* 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))
	  string_length = strlen (string);
	  temp_string = (char *)xmalloc (3 + string_length + char_index);

	  strcpy (temp_string, name);
	  temp_string[char_index] = ' ';
	  strcpy (temp_string + char_index + 1, string);

	  parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);

	  /* Ancient backwards compatibility.  Old versions of bash exported
	     functions like name()=() {...} */
	  if (name[char_index - 1] == ')' && name[char_index - 2] == '(')
	    name[char_index - 2] = '\0';

As seen before, the myfunc bash function is not executed, it is only parsed and defined. But reading the source code, the whole string is parsed and executed, not only the function definition. If some code is inserted after the function definition, it should also be executed. This is easy to verify:

$ myfunc='() { id;}; echo "This should not be executed but it is."' bash -c "echo legit shell"
This should not be executed but it is.
legit shell

There may be multiple exploit vectors, mainly every time an environment variable can be passed to a new instance of Bash.
For instance:

  • A CGI using HTTP_USER_AGENT variable
  • OpenSSH on a restricted shell and the SSH_ORIGINAL_COMMAND variable
  • dhclient with a custom domain-name
  • Many more to find

Request to be added to the Portcullis Labs newsletter

We will email you whenever a new tool, or post is added to the site.

Your Name (required)

Your Email (required)