Is Django vulnerable to shellshock?

A security advisory about the matter was posted on the Django blog:

This issue does not affect Django directly, but will affect most users of Django.

Any web server which is serving traffic over a CGI or CGI-like interface (including WSGI) should upgrade its version of Bash immediately.


Update bash, and this specific problem goes away.

But if you want to know if you were vulnerable before this, there will be application specific paths to add to generic vulnerabilities (until bash is patched - then there may be new non-bash vulnerabilities discovered later with the same pattern).

The specific problem is in allowing attacker controlled environment variables. If the attacker is able to remotely get an environment variable set to a function definition followed by an immediate command; the attacker could wait for some totally unrelated command to get exec'ed by bash. It does not matter which bash program it is, or if the arguments were validated (or hardcoded, etc).

Specifically, bash has a main entry point like any other program, and it has this signature:

int main(int argc, char** argv, char** arge); //bash's main entry point

This main function gets invoked as an internal detail of invoking an exec. If arge was not cleaned before-hand, the main of bash will dutifully do what the bash shell is supposed to do. One of those things is parsing the environment.

We are all in the habit of scrubbing argc and argv for cleanliness. But most people totally ignore what is in arge. So if a process was told by a remote socket to set an environment variable (ie: HTTP_COOOKIE) then some entry of arge has that value. Say that arge[42] has a value:

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

Then bash's main sees that the string value has the open/close parenthesis, and guesses that this must be a function definition. So it dutifully defines HTTP_COOKIE as a function, and also executes the immediate command after it.

So, if the environment has this variable, nothing bad has happened yet. But you could easily be calling innocuous Python code that isn't obviously executing bash. This is all you see:

Licensing().check()

Perhaps there is a binary that your company uses that checks a license, and you call this function (you don't know what it does). But say that ultimately what it does is this (resolved to the C implementation):

//There are no arguments to this program so this is "safe"
execl("/bin/bash", "bash", "-c", "licensingWrapper", (char*)0);

Then bash starts up...

//This is bash's main function
int main(int argc, char** argv, char** arge) {
    ....
    parseEnvironment(arge);  //!!!!
    ....
}

Then in parseEnvironment, at arge[42] it sees some variable named 'HTTP_COOKIE' and parses it. bash hasn't got the faintest clue what this variable is for, but it sees that it's a function definition with some immediately executed command; and just does it. This environment parse is just a standard prerequisite for getting the shell ready to execute whatever argv says to execute.

If you think about this a bit deeper, it is just another case of not cleansing input. Nobody cleanses environment input. To make things worse, the environment variables get interpreted differently based on what they are being passed in to (bash, sh, java, ...). So if you had a generic function to cleanse the environment, it would be specific to the kinds of exec you expect to get run later.

int main(int argc, char** argv, char** arge) {
    BOOL isClean = TRUE;
    isClean &= cleanseEnvironment(arge, BASIC_SANITY);
    isClean &= cleanseEnvironment(arge, BASH_RULES);
    if(isClean) {
        parseEnvironment(arge);
    }
    else {
        return -EBADENVIRONMENT;
    }
    ...
}

So I would be highly suspicious of environment variables being set to attacker tainted values. Then simply assume that some shell that you hadn't thought of will get executed with that environment later. You have an obvious problem if you can show that bash is eventually executed. But it could easily be an indirect path, like tcl being executed, then tcl causes bash to get invoked.