Local File Inclusion (LFI) is a type of vulnerability concerning web server. It allow an attacker to include a local file on the web server. It occurs due to the use of not properly sanitized user input.
I'll give some examples in PHP but it can also occurs in Perl, ASP, JSP, or whatever.
Here an attacker can simply chose to modify the GET request to give a file that is not in the web serer folder.
Here an attacker can use relative path to get out of the web server folder.
Here the script force to use .php file extension, but an attacker, by adding a null byte the the path, can drop the extension.
Why? %00 is the http encoded version of 0x00 in hex. It's the null caracter, a null byte. In C it's written \0 and it means the string termination character so that will stop processing the string immediately.
PHP (like other web server lang) require to process high-level code at system level and it's usually accomplished by using C/C++ functions. Bytes following the delimiter will be ignored.
Note: Null byte injection has been fixed in PHP 5.3.4 (unsupported).
The script replace all ../ with nothing but what if we encode slashs or dotes? or doubling them?
The first method won't work on recent PHP version but the second one will work nearly every times.
Sometimes it's possible to encode some characters of the URL to bypass filters as we have seen previously. This is because the browser decodes the input but PHP does not.
By using double encoding it’s possible to bypass security filters that only decode user input once. The second decoding process is executed by the backend platform or modules that properly handle encoded data, but don't have the corresponding security checks in place.
For example if urlencode() is used in PHP.
on PHP ./ can be appended as many tiems as you want to a path so /etc/passwd/., /etc/passwd/./, /etc/passwd/././. are all the same as /etc/passwd.
But there is another fact that is interesting to bypass extensions filter: on a lot of PHP installation, filenames longer than 4096 bytes are silently truncated! Characters after the firsts 4096 bytes are discarded and no error is triggered.
So what? Let's craft a very long path to discard .php extension!
Take your request and append a thousand times ./ to ../../../etc/passwd/./././././<...> so will get ../../../etc/passwd/./././././<...>/././.php but the filename will be longer than 4096 bytes so the overlong part will be dropped. So we'll just get our request and thousand times ./ that will be equivalent to ../../../etc/passwd.
It also exist the reverse version of path truncation. This works by trying to fill the file name from the beginning to exceed the MAX_PATH value.
But this method has a disadvantage: you have to inject the exact number of bytes to truncate only the .php extension. If too much ../ are injected the filename will also be truncated. Plus, that's why you'll need to add some characters if the path has not the good length because bytes are injected by 3 due to ../.
You found a LFI, you included the file but you can't see its content?
Some file like config.php or libraries of functions are only executed but not shown. So to help us show them, we can use some PHP wrappers.
A famous one is index.php?page=php://filter/read=convert.base64-encode/resource=config, this filter will encode the page in base64 and show the result like if we have done index.php?page=config and then base64 it.
A rare but very useful wrapper is expect://, it allows execution of system commands via php. But it is not enabled by default, an extension needs to be installed.
php://input allows to send payload via POST request.
data:// can be used to include executable PHP code.
Another payload can be <?php phpinfo(); die();?>, the die statement will prevent the execution of the rest of the script or the execution of the incorrectly decoded extension appended to the stream.
To directly execute a command the data request + payload may be: data:,<?system($_GET['x']);?>&x=ls or data:;base64,PD9zeXN0ZW0oJF9HRVRbJ3gnXSk7Pz4=&x=ls.
The best way to successfully execute the payload is to base64 it and then URL encode it.
Archive a php script into a zip or a phar (or others archive with others wrappers), send the archive and tell which file has to be read: zip://archive.zip#file.php.
You can append some php code at the end of an image and upload it or include it.
I invite you to discover more attack vectors here.
And don't forget that those methods can be combined.