Explanation of the command to check shellshock
Here is the command I have used to check my bash shell for the Shellshock bug:
env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
Can anyone please explain the command in details?
This answer is a derivative of an original article on Fedora Magazine by Matthew Miller, licensed under the Creative Commons Attribution-Share Alike 4.0 license.
Let me explain:
env x='() { :;}; echo OOPS' bash -c :
This will print “OOPS” on a vulnerable system, but exit silently if bash has been patched.
env x='() { :;}; echo OOPS' bash -c "echo this is a test"
This will print “OOPS” on a vulnerable system, but print “this is a test”
if bash has been patched.
And you’ve probably heard that it has something to do with environment variables. But, why is code in environment variables getting executed? Well, it’s not supposed to be — but, because of a feature which I’m tempted to call a bit too clever for its own good, there’s some room for a flaw. Bash is what you see as a terminal prompt, but it also is a scripting language, and has the ability to define functions. You do that like this:
$ Ubuntu() { echo "Ubuntu is awesome."; }
and then you have a new command. Keep in mind that the echo
here isn’t actually run yet; it’s just saved as what will happen when we run our new command. This will be important in a minute!
$ Ubuntu
Ubuntu is awesome.
Useful! But, let’s say, for some reason, We need to execute a new instance of bash, as a subprocess, and want to run my awesome new command under that. The statement bash -c somecommand
does exactly this: runs the given command in a new shell:
$ bash -c Ubuntu
bash: Ubuntu: command not found
Ooh. Sad. The child didn’t inherit the function definition. But, it does inherent the environment — a collection of key-value pairs that have been exported from the shell. (This is a whole ‘nuther concept; if you are not familiar with this, trust me for now.) And, it turns out, bash can export functions as well. So:
$ export -f Ubuntu
$ bash -c Ubuntu
Ubuntu is awesome.
Which is all well and good — except that the mechanism by which this is accomplished is sorta dodgy. Basically, since there is no Linux/Unix magic for doing functions in environment variables, the export function actually just creates a regular environment variable containing the function definition. Then, when the second shell reads the “incoming” environment and encounters a variable with contents that look like a function, it evaluates it.
In theory, this is perfectly safe, because, remember, defining a function doesn’t actually execute it. Except — and this is why we’re here — there was a bug in the code where the evaluation didn’t stop when the end of the function definition was reached. It just kepts going.
That would never happen when the function stored in an environment variable is made legitimately, with export -f
. But, why be legit? An attacker can just make up any old environment variable, and if it looks like a function, new bash shells will think it is!
So, in our first example:
env x='() { :;}; echo OOPS' bash -c "echo this is a test"
The env
command runs a command with a given variable set. In this case, we’re setting x
to something that looks like a function. The function is just a single :
, which is actually a simple command which is defined as doing nothing. But then, after the semi-colon
which signals the end of the function definition, there’s an echo
command. That’s not supposed to be there, but there’s nothing stopping us from doing it.
Then, the command given to run with this new environment is a new bash shell, again with a “echo this is a test
” or “do nothing :
” command, after which it will exit, completely harmlessly.
But — oops! When that new shell starts up and reads the environment, it gets to the x
variable, and since it looks like a function, it evaluates it. The function definition is harmlessly loaded — and then our malicious payload is triggered too. So, if you run the above on a vulnerable system, you’ll get “OOPS”
printed back at you. Or, an attacker could do a lot worse than just print things.
In unpatched version of bash
it stores exported function definitions as environment variables.
Store a function x
as,
$ x() { bar; }
$ export -f x
And check its definition as,
$ env | grep -A1 x
x=() { bar
}
So one could exploit this by defining his own environment variables, and interprets them as function definitions. For example env x='() { :;}'
would be treated as
x() { :;
}
What does the command to check shellshock do,
env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
From man env
,
env
- run a program in a modified environment.:
do nothing but exits with exit status0
. see moreWhen a new instance of unpatched bash launched as
bash -c "echo this is a test"
, the crafted environmental variable is treated as a function and loaded. Accordingly one gets the output
vulnerable this is a test
Note: The echo outside the function definition has been unexpectedly executed during bash startup. The function definition is just a step to get the evaluation and exploit to happen, the function definition itself and the environment variable used are arbitrary. The shell looks at the environment variables, sees x, which looks like it meets the constraints it knows about what a function definition looks like, and it evaluates the line, unintentionally also executing the echo (which could be any command, malicious or not). Also See this