Unix Shell Injection Attacks

Part of 22C:169, Computer Security Notes
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

Some Obscure Shell Features

The Unix shells have accumulated a number of obscure features that contribute to their utility but at the same time lead to potential trouble. In the following, these features are illustrated in the context of this little shell script:

#tcsh
# myscript arg1 arg2 arg3 ...
# echoes its arguments one per line
@ params = $#argv
@ count = 1
while ($count <= $params)
        echo $count $argv[$count]
        @count = $count + 1
end

Consider the following illustrations of successively more interesting applications of this script:

> myscript some $shell arguments
1 some
2 /bin/tcsh
3 arguments

The above script shows the basic behavior of this script. When run, it iterates over all of its parameters, echoing one per line. Normally, arguments are separated by spaces, so for most purposes, arguments may be thought of as words. The second argument above begins with a dollar sign, so it is taken as the name of a shell variable. By default, the variable $shell contains the name of the current shell interpreter, which, in this case, is /bin/tcsh.

> myscript a '$shell argument' list
1 a
2 $shell argument
3 list

Single quotes within the argument list cause the entire quoted text to be read as a single argument. Note that interpretation of dollar signs that would normally be read as the lead-in to variable names is suppressed.

> myscript a "$shell argument" list
1 a
2 /bin/tcsh argument
3 list

Double quotes within the argument list cause the entire quoted text to be read as a single argument, just like single quotes, except that the variable substitution mechanism of the shell remains in operation within the material between quotes.

> myscript a `echo $shell argument` list
1 a
2 /bin/tcsh
3 argument
4 list

Reverse quotes cause the quoted text to be interpreted as a shell command. Any output from that command is then parsed into words and the result is passed as one or more arguments. In the above example, one of the parameters to the echo command begins with a dollar sign, so that parameter is replaced by the value of the indicated variable first.

> myscript a "`echo $shell argument`" list
1 a
2 /bin/tcsh argument
3 list

Nesting quotes are understood. In the above, double quotes arund the reverse quotes cause the output of the echo command to be taken as a single long argument instead of being broken up into component words.

> eval "myscript a" `echo $shell argument` list
1 a
2 /bin/tcsh
3 argument
4 list

The eval command constructs a line of text from its arguments and then re-parses that line into words before it executes the line as a shell command. Thus, it defeats the blocking of parameters caused by quotation marks. In this case, the input to eval is a line beginning with the command myscript so our script is executed.

Finally, there are some shorthand notations in the shell that are widely used.

The notation $argv without a subscript is equivalent to the sequence $argv[1] $argv[2] $argv[3] ... This applies to all arrays, not just the pre-defined argv.

The notation $1 is equivalent to $argv[1].

The form if (cond) command (omitting the keyword then allows the execution of a single command to be conditional on the truth of the indicated condition. This is just like the C if statement.

Multiple shell commands may be put on one line separated by semicolons. This is no different from C or Java.

Code Injection Attacks on the Shell

Each of the above features is potentially useful, but in combination, some of them have quite dangerous consequences. Consider the following innocent looking script:

#tcsh
# myscript arg1 arg2 arg3 ...
# echoes its arguments one per line
@ params = $#argv
@ count = 1
while ($count <= $params)
        eval echo $count $argv[ $count ]
        @count = count + 1
end

Under most circumstances, the above script behaves exactly as the original did. The use of the eval command never causes any difficulty for most simple examples. That is, until someone includes a semicolon. Consider this example:

> myscript "nothing ; echo hello" '; echo count = $count' "; rm myscript"
1 nothing
hello
2 
count = 2
3 
rm: remove regular file file `myscript'?

This is not good! Our parameters have caused the shell to do far more than we intended. It didn't merely list its arguments, it executed commands that were embedded in its arguments. Many complex shell scripts include such dangers.

The vulnerability here was caused by the combination of three shell features: First, the ability to put multiple commands on a line, separated by semicolons. Second, the ability to use quotation marks to include more than one "word" in a parameter, and third, the use of the eval command. Each of these features, in isolation and in combination has legitimate uses.

The security threat that has emerged is a perfect example of an emergent property of the system. No detailed analysis of the individual components would reveal this threat. It is only in combination that these components permit this atttack.

The second parameter above is particularly interesting. An outsider (the user of myscript) was able to force the script to print out the values of one of its local variables. Local variables are conceptually in the private domain of the called procedure, and an outsider should not be able to access these.

Here is a new example script, used to illustrate another vulnerability.

#tcsh
# check arg
# outputs "it matches" if the argument is 1
if ($argv == 1) echo it matches

Now, consider the following applications of the check script:

> check 5
> check 1
it matches
> check "1 ) echo"
== 1 ) echo it matches

Here, what we did was introduce text into the argument list of the if shell command. Where the if command in the shell script looked well formed, we have introduced arguments that created text with a completely different structure. The final call to check this:

if (1 ) echo == 1) echo it matches

Here, the if command saw the simplified condition (1 ) which it considere entirely inoffensive. The tcsh shell considers nonzero values to be true, so this if statement is always true. Following this, it sees an echo command with unbalanced parenthesis in its argument list, but echo doesn't care about parentheses. If we could make it execute the echo command, we could have made it execute any other command. Consider this.

> check "1 ) myscript"
1 ==
2 1
3 )
4 echo
5 it
6 matches

Above, we've forced the check shell script to call myscript, the script we wrote previously that picks its argument list apart. We could as easily have injected a command to delete all of the files in the current directory.

Sadly, many shell scripts contain lingering vulnerabilities like these. The various Unix shells (and Microsoft command language interpreters) are not very well designed, from the point of view of readable and maintainable code. As a result, shell scripts are notoriously difficult to maintain.

But Wait, There's More

The problem is even worse than this! There are many scripting languages out there. Awk and particularly Perl are widely used for Internet server-side applications. SQL is widely used in database applications. Whenever users provide test that is directly substituted into a script in any of these languages, there is the potential risk of a code injection attack.

In many cases, the Perl script used on a client-side web application simply takes parameters from a web form and concatenates them into shell commands that Perl then executes. The output of those shell commands is the web page sent back to the user. Therefore, if a user includes the right material in the web form, this can be injected through Perl into the parameter lists of the shell script that Perl launches.

References

There is a considerable literature on SQL injection attacks. The basic principles are the same as for all text injection, so they are broadly relevant. Even if you don't know SQL, you should be immediately able to see the relationship between what has been discussed above and the examples provided. See any or all of the following:

The Wikipedia writeup on SQL injection

SecurityDocs.com has a decent writeup on SQL Injection Attack and Defense

SecuriTeam.com also has a decent SQL Injection Walkthrough

There are many notices posted on the web reporting various code injection attack vulnerabilities. Here are some example reports of such vulnerabilities -- it is a safe assumption that publically reported vulnerabilities have already been fixed, but the reports themselves teach something useful about how subtle these bugs can be.

A Vulnerability in the tWiki server

A vulnerability in the Asbru Software Web Content Editor

A vulnerability in the Q-Shop Pro on-line shopping system