Non-Recursive Path Traversal Filters
One of the most basic filters against LFI is a search and replace filter, where it simply deletes substrings of (../
) to avoid path traversals. For example:
So if we inject ../../../../etc/passwd
, finally path would be ./languages/etc/passwd
.
But this filter is not recursively removing ../
substring, which makes it insecure.
For example, if we use ....//
as our payload, then the filter would remove ../
and the output string would be ../
, which means we may still perform path traversal.
We can try out different kinds of payloads such as:
..././
....\/
....///
Encoding
Some web filters may prevent input filters that include certain LFI-related characters, like a dot .
or a slash /
used for path traversals.
We can bypass these filters through URL encoding.
If the target web application did not allow .
and /
in our input, we can URL encode ../
into %2e%2e%2f
, which may bypass the filter.
Furthermore, we may also use Burp Decoder to encode the encoded string once again to have a double encoded
string, which may also bypass other types of filters.
Approved Paths
Some web applications may also use Regular Expressions to ensure that the file being included is under a specific path. For example, the web application we have been dealing with may only accept paths that are under the ./languages
directory, as follows:
To find the approved path, we can examine the requests sent by the existing forms, and see what path they use for the normal web functionality.
./languages/../../../../etc/passwd
Some web applications may apply this filter along with one of the earlier filters, so we may combine both techniques by starting our payload with the approved path, and then URL encode our payload or use recursive payload.
Appended Extensions
As discussed in the previous section, some web applications append an extension to our input string (e.g. .php
), to ensure that the file we include is in the expected extension.
With modern web apps, with PHP versions after 5.3/5.4, we might won’t be able to bypass this and only can read files with the appended extensions.
Let’s cover some ways to bypass appended extensions.
Path Truncation
In earlier version of PHP, string longer than 4096 characters will be truncated, and any character beyond will be ignored.
Earlier version of PHP remove trailing slashes and single dots in path names. So if we call /etc/passwd/ .
, then PHP would call /etc/passwd
.
PHP, and Linux systems in general, also disregard multiple slashes in the path (e.g. ////etc/passwd
is the same as /etc/passwd
).
Similarly, a current directory shortcut (.
) in the middle of the path would also be disregarded (e.g. /etc/./passwd
).
Whenever we reach the 4096 character limitation, the appended extension (.php
) would be truncated, and we would have a path without an appended extension.
An example of such payload would be the following:
We can create such string using:
Null Bytes
PHP versions before 5.5 were vulnerable to null byte injection
, which means that adding a null byte (%00
) at the end of the string would terminate the string and not consider anything after it.
To exploit this vulnerability, we can end our payload with a null byte (e.g. /etc/passwd%00
), such that the final path passed to include()
would be (/etc/passwd%00.php
).