Monday, January 4, 2010

Section 5.3.  Filename Manipulation










5.3. Filename Manipulation




Many situations warrant the use of dynamic
includes
, where part of the pathname or filename is stored in a variable. For example, you can cache some dynamic parts of your pages to alleviate the burden on your database server:



<?php

include "/cache/{$_GET['username']}.html";

?>



To make the vulnerability more obvious, this example uses $_GET. The same vulnerability exists when any tainted data is usedusing $_GET['username'] is an extreme example used for clarity.



While this approach has merit, it also provides an attacker with the perfect opportunity to choose which cached file is displayed. For example, a user can easily view another user's cached file by modifying the value of username in the URL. In fact, an attacker can display any .html file stored within /cache simply by using the name of the file (without the extension) as the value of username:



http://example.org/index.php?username=filename



Although an attacker is bound by the static portions of the path and filename, manipulating the filename isn't the only concern. A creative attacker can traverse the filesystem, looking for other .html files located elsewhere, hoping to find ones that contain sensitive data. Because .. indicates the parent directory, this string can be used for the traversal:



http://example.org/index.php?username=../admin/users



This results in the following:



<?php

include "/cache/../admin/users.html";

?>



In this case, .. refers to the parent directory of /cache, which is the root directory. This is effectively the same as the following:



<?php

include "/admin/users.html";

?>



Because every file on the filesystem is within the root directory, this approach allows an attacker to access any .html resource on your server.


On some platforms, an attacker can supply NULL in the URL to terminate the string. For example:


http://example.org/index.php?username=../etc/passwd%00

This effectively eliminates the .html restriction.



Of course, speculating about all the malicious things that an attacker can do when given this amount of control over the file to be included only helps you appreciate the risk. The important lesson to learn is to never use tainted data in a dynamic include. Exploits will vary, but the vulnerability is consistent. To correct this particular vulnerability, use only filtered data (see Chapter 1 for more information about input filtering):



<?php

$clean = array();

/* $_GET['filename'] is filtered and stored in $clean['filename']. */

include "/path/to/{$clean['filename']}";

?>



Another useful technique is to use basename( ) when you want to be sure that a filename is only a filename and has no path information:



<?php

$clean = array();

if (basename($_GET['filename'] == $_GET['filename'])
{
$clean['filename'] = $_GET['filename'];
}

include "/path/to/{$clean['filename']}";

?>



If you want to allow path information but want to have it reduced to its simplest form prior to inspection, you can use realpath( ):



<?php

$filename = realpath("/path/to/{$_GET['filename']}");

?>



The result ($filename) can be inspected to see whether it is within /path/to:



<?php

$pathinfo = pathinfo($filename);

if ($pathinfo['dirname'] == '/path/to')
{
/* $filename is within /path/to */.
}

?>



If it is not, then you should log the request as an attack for later inspection. This is especially important if you're using this approach as a Defense in Depth mechanism because you should try to determine why your other safeguards failed.












No comments: