Remote File Inclusion & Local File Inclusion (RFI & LFI)

Hello friends! We are back again with another cybersecurity theory again. This time we will be talking about remote file inclusion. This is a common vulnerability because we love dynamic content. Basically, this attack exploits the include() function in PHP. I found it really difficult for me when I was learning this method in the first place. For me, after I understand it, there are a lot of important things that can be confusing if they are not clearly stated. Let’s get started.

For you who have been working around web development, most likely you are aware of the “include” function to make the web development process more efficient in terms of generating dynamic content. Referring to PHP official site.

The include statement includes and evaluates the specified file.

The include function usage is not wrong, not at all, but sometimes the implementation is not careful, hence opens up to a vulnerability.

Remote file inclusion or RFI is almost always paired with local file inclusion or LFI. Talking about LFI and RFI, the inclusion part is referring to the exploitation of the include function that forces the system to evaluate the inappropriate files. The difference between those two is LFI loads local files like on the worst case, the “etc/.passwd” file. On the other hand, RFI loads files from an external source outside the server.

...
$page = $_GET["page"]; // or using post $_POST["page"] the point is there is data passed to a server.
include($page.".php"); // This is vulnerable to RFI and LFI$base_url = "https://example.com/";
include($base_url.$page.".php"); //This is vulnerable to LFI only
...

Local File Inclusion (LFI)

There are several ways when comes down to LFI exploitation. You can identify a potential LFI vulnerable website when you see at least a get parameter that may be in accordance with the content of the website. It does not always vulnerable, but sometimes, it does, worth to be considered.

Straight Forward LFI

For example, I have a really useful website.

A Very Useful Website

Now, when I click the link, pay attention.

A Very Useful Website (2)

As you can see, after I clicked the link, there is an URL variable appended which calls page and the value is normal.txt. Most of the case, server takes the page variable value using $_GET[“page”]. After that, the server will “include” this value which is referring to another file in the server. In this case, I have a file called normal.txt sitting in the root of the page and the content is as follow.

Normal.txt Content

If we stop here, nothing is malicious. But, think about it, how if we actually modify the value of page parameter into something more malicious. I have another file in my server called special.txt. The website itself does not spoil any files that are sitting in the server, BUT sometimes files are named with common names, are not they? How many of you use admin.php, login.php, process.php, page.php, home.php, index.php, etc. Even without any necessary tool like dirbuster, we can basically guess them. So in this scenario, let’s imagine that somehow I guess there is a file called special.txt on the server.

LFI Exploitation

As you can see, even the web application does not suggest you to open that file (by not giving you any link to it), you can force the application to include the file you want that sits in the server. Even with a disasterous missconfiguration, you can include file from another parent by using path traversal.

Example of Path Traversal

The point is, you can actually explore the whole system and look for its content one by one (obviously you need to know you target’s file name) when you meet this kind of vulnerability. The point is that we force the web application to include files we want that sits inside the server.

Null Byte Injection

Actually forget about this. Nowdays, PHP latest version is PHP 7.4. The Null Byte Injection exploitation requires PHP 5.3. Later than that, this vulnerability is patched and can not be used again.

But just for a knowledge, null byte injection is we supply the null character to PHP string in order to control the string terminator.

As we know, PHP is built on C, which treats a string as an array of character. In order to know when the string is over, there is another byte appended to the series, which is the null byte. For example, a word, “apple” will be interpreted as a-p-p-l-e-(null byte), hence when the computer read it, it knows that it has to stop at e, e is the latest character in apple.

Now, in PHP below 5.3, there is a vulnerability where we can put the null byte in the middle of a string and it causes the string to stop at that point. The URL Encoding of null byte is %00. The attack comes like “http://some-example.com/lang=abc.txt%00”. It is usefull when there is a LFI prevention by adding trailing file format.

#pretty secure $page = $_GET["page"];
include($page.".php"); #it basically prevents any inclusion aside than file with PHP format

If we are targetting the /etc/.passwd file, we can not include the trailing .php defined by the server, otherwise, it will be like include(“../../../../../../etc/.passwd.php”), which is kinda dumb and funny. That is where null byte injection comes. By injecting a null byte, we can terminate the string reading process right where we want. include(“../../../../../etc/.passwd%00.php”) will read the string (which is the path of file we want to include) as “../../../../../etc/.passwd”, because the null byte terminates the reading process and we can bypass the prevention.

For your info, if you are a web developer, you do not have to worry because the common distribution of PHP version in hosting service is up to PHP 7 which has patched this security hole, so, do not worry.

PHP Filters Base64 Conversion

I know this attack when I practice with a vulnerable box from vulnhub. There is another security hole in LFI we want to read the source code / content of the page. As you also know, PHP code is not sent to the client side, which means, we can not actually read the PHP code. But, there is another LFI exploitation that allows us to do that such thing, that is, using PHP filters base64 conversion.

Let’s look back to our beloved, very useful website.

Very Useful Website

If we look at the source code, it looks like this.

Source Code

We can not see any PHP code and that is normal because PHP codes are executed on the server and we only see the result of those calculation. Now our goal is to get the source code of vuln.php. We can use PHP filters as follows,

http://localhost/lab/rfi/vuln.php?page=php://filter/convert.base64-encode/resource=vuln.php

We force the web application to do the base64 conversion of the resource vuln.php and send to us as a response (we include them in the page), and this is the result.

The Display of Inclusion Base64 Filter

The original base64 string is

PGh0bWw+DQoJPGJvZHk+DQoJCTxhIGhyZWYgPSAiP3BhZ2U9bm9ybWFsLnR4dCI+Q2xpY2sgbWUgdG8gc2hvdyBjb250ZW50ITwvYT4NCgkJPD9waHANCg0KCQlpZihpc3NldCgkX0dFVFsicGFnZSJdKSl7DQoJCQkkcGFnZSA9ICRfR0VUWyJwYWdlIl07DQoJCQlpbmNsdWRlKCRwYWdlKTsNCgkJfQ0KDQoJCT8+DQoJPC9ib2R5Pg0KPC9odG1sPg0K

We can use online base64 converstion tool, this is one of my favourite. Below is the decoding result.

Base64 Decoding of the Source Code

I know, I know you are really excited and you want to use this everywhere. Keep in mind, this only works when there is a LFI vulnerability where the developer DOES NOT set any filter to the data supplied to the system. If the developer is a bit more diligent and put some filter, just the simple one, this will not work.

Attack Combination

Now think about it. Rather than just guessing the file, how if we can upload a file to the server and exploit this vulnerability to actually include our uploaded file and execute them? (since include function means execute the code as well if it is a php code). That is (one of many things) what hackers actually do. They write a kind of reverse shell code to PHP, upload them, and exploit this vulnerability hence the server is forced to execute the malicious code.

Remote File Inclusion (RFI)

Now we are talking about remote file inclusion or RFI. This is a bit different and a bit more complicated to execute because we need a server that is publicly accessible (if we are not doing penetration testing for internal network).

Preparation

You can find a lot of cheap hosting or even free. I use Free Web Hosting. Set them up until you can get access to its file manager which looks like this.

File Manager Display

We will work mostly on this section.

Strategy

RFI Schema

The above picture is the RFI attacking schema.

  1. The attacker prepares the malicious code and uploads it to their server.
  2. The attacker makes a request via browser and points the attacker’s file inside the attacker’s server. (RFI began)
  3. The web application interface then forwards the request to the web application server.
  4. In the server, the request is processed and it needs to include a URL (written by the web application’s developer). In this case, the resource/file that needs to be included defined in the URL parameter (malicious).
  5. Because of that, the server makes a request to the attacker’s server and requests the file required. (RFI executed)
  6. The attacker’s server receives the request and starts to processes the request. It executes any code in its server like loops, variables, etc.
  7. After the server has done with the processing, it gives back to the web application’s server as a response from its request before.
  8. The web application’s server receives the response and includes the content into its response (that will be sent back to the user/in this case, the attacker). The web application then executes all the code like loops, variables, etc before it is sent back to the user. (RFI become malicious if the content from the attacker’s server is malicious)
  9. When the response is ready, the response is sent back to the user’s browsers.
  10. The browser renders the response and displays them to the user/attacker.

The most common thing that people including me are doing wrong is, we forget that when we craft the malicious code, the codes must be executed at the victim server, not on our server. Let me give you an example.

This is the Code on The Attacker’s Server (rfi-exploit.php)

now, we exploit the RFI vulnerability on our beloved super useful website.

RFI Attack

Do you see it? instead of putting a file name on the page variable, I put an absolute path. This absolute path is then included in our response from this server. That is why we can see the “hello from the other site” text. KEEP IN MIND that the web application server receives the text “hello from the other site” instead of executing the “echo” expression. The attacker’s server does the “echo” expression and sends back the result of the expression to the web application server. Below is the proof.

Proof of Concept Where the RFI Code is Executed
The Exploit Code

We can see that $_SERVER[“SERVER_NAME”] is executed on the attacker’s server that is why it shows “kopikulogojek.000webhostapp.com” instead of “localhost”. This is the common mistake, the boomerang. With this mistake, yea you basically infect your own server, congratulations.

Now our goal is to transfer the content to the target server, without executing it first beforehand in our server (the attacker’s server). Because of that, our exploit file can not be in .php format because otherwise, it will be executed first by our server because almost every hosting’s default configuration is PHP can be executed. We change it to .txt format hence the content will be transferred rather than executed.

Our RFI Exploit

And when we refresh the web application,

RFI Exploitation

We force the system to execute the malicious command from the outside of the file.

Sad Story of RFI

When other people force you to say “I am ugly”.

Prevention

From these 2 inclusion vulnerabilities, we know that never trust the user’s input. Always make filtering and do not let the user decide what files should be presented. Instead, you can do like below.

$lang = $_GET["lang"];if($lang === "en"){
include("en_lang.php");
else if($lang == "id"){
include("id_lang.php");
else{
include("en_lang.php");
}

Always use the absolute path. Instead of…

include("en_lang.php"); 

you must do like…

include("path/to/the/included/file/en_lang.php");

Closing

I think that is all. I really hope this article helps you to understand RFI and LFI. I found a lot of difficulties and trial-and-errors before I finally understand and be able to write this article. To GOD be all the glory, Soli Deo Gloria!

A humble learner of everything around IT especially in IT implementation, governance, risk management, and cybersecurity.