Path Traversal - Verzeichnisüberschreitung (Directory Traversal)
Path Traversal (CWE-22, OWASP A01:2021) ermöglicht Angreifern durch ../ Sequenzen auf Dateien außerhalb des erlaubten Verzeichnisses zuzugreifen. Ziel: Lesen sensitiver Dateien (/etc/passwd, web.config, .env, SSH-Keys), in schlimmsten Fällen Schreiben/Ausführen von Dateien. Varianten: URL-kodierte Traversal (%2e%2e%2f), doppelt kodiert (%252e%252e%252f), Null-Byte-Injection (.php%00), Windows-Pfade mit Backslash-Notation. Schutz: absolute Pfad-Validierung mit realpath(), Allowlist-basierte Dateiauswahl.
Path Traversal gehört zu den ältesten und beständigsten Web-Schwachstellen. Trotz der langen Geschichte taucht sie regelmäßig in Pentests auf - und 2021 machte die CVE-2021-41773 (Apache HTTP Server) globale Schlagzeilen: ein einzelnes %2e%2e/cgi-bin/ reichte für Remote Code Execution. Das Muster ist immer dasselbe: nicht-validierte Benutzereingaben fließen direkt in Dateisystem-Zugriffe.
Grundprinzip
Normaler Datei-Zugriff:
https://example.com/download?file=report.pdf
→ Server: /var/www/uploads/report.pdf ← erlaubt
Path Traversal Angriff:
https://example.com/download?file=../../../etc/passwd
→ Server: /var/www/uploads/../../../etc/passwd
= /etc/passwd ← außerhalb des erlaubten Verzeichnisses!
Einfaches verwundbare Implementierung (PHP):
$file = $_GET['file'];
readfile('/var/www/uploads/' . $file); // ← KEIN Schutz!
Typische Angriffsziele:
Linux:
→ /etc/passwd (Benutzerliste)
→ /etc/shadow (Passwort-Hashes, wenn als root)
→ /etc/hosts (Netzwerk-Konfiguration)
→ ~/.ssh/id_rsa (SSH-Private-Key!)
→ /proc/self/environ (Umgebungsvariablen mit API-Keys!)
→ /proc/self/cmdline (Prozess-Argumente)
Windows:
→ C:\Windows\win.ini (Systeminfo)
→ C:\Windows\System32\drivers\etc\hosts
→ C:\inetpub\wwwroot\web.config (DB-Passwörter!)
→ C:\Users\[user]\AppData\Local\Microsoft\Outlook\*.ost
Web-Anwendungen:
→ .env (Datenbankpasswörter, API-Keys, Secrets!)
→ config/database.yml (Rails)
→ WEB-INF/web.xml (Java)
→ ../../../wp-config.php (WordPress)
Bypass-Techniken
Einfache Filter umgehen:
Filter 1: Ersetzt "../" mit "" (nicht rekursiv):
Bypass: "....//....//....//etc/passwd"
→ Nach Ersatz: "../../../etc/passwd"!
Filter 2: URL-Dekodierung nur einmal:
Bypass: "%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd"
→ Dekodiert: "../../../etc/passwd"
Filter 3: Slash wird gefiltert:
Bypass: "%252e%252e%252fetc%252fpasswd" (doppelt kodiert)
→ Erste Dekodierung: "%2e%2e%2fetc%2fpasswd"
→ Zweite Dekodierung: "../../../etc/passwd"
Filter 4: Erwartet .jpg am Ende:
Bypass (Null-Byte, Legacy PHP < 5.3.4):
"../../../../etc/passwd%00.jpg"
→ PHP stoppt bei Null-Byte: liest /etc/passwd
→ .jpg-Check: Dateiname endet mit .jpg ← PASS!
Filter 5: Prüft ob Pfad mit "/uploads/" beginnt:
Bypass: "/uploads/../../../../etc/passwd"
→ Beginnt mit /uploads/ → PASS!
→ Enthält aber path traversal!
Windows-spezifische Bypasses:
Backslash: "..\..\..\..\Windows\win.ini"
URL-kodiert: "..\"..\"..\"..\Windows\win.ini"
UNC-Pfade: "\\server\share\file"
Drive-Letter: "C:\Windows\win.ini" (wenn absolute Pfade akzeptiert)
ZIP-/Archive-basierter Path Traversal (Zip Slip):
ZIP-Archiv mit Entry "../../../evil.php"
→ Wenn Anwendung ZIP entpackt: evil.php landet AUSSERHALB des Zielordners!
→ Bekannt als "Zip Slip" (2018, Snyk)
→ Betrifft: Java, Python, Go, Ruby zip-Libraries (ungepatchte Versionen)
Erkennung im Pentest
Systematisches Testing:
1. Parameter-Identifikation:
→ Alle Parameter die auf Dateinamen/Pfade hindeuten:
?file=, ?page=, ?path=, ?filename=, ?template=, ?doc=,
?view=, ?include=, ?load=, ?source=, ?content=, ?show=
2. Payload-Progression (von einfach nach komplex):
Stufe 1: Einfach
?file=../../../etc/passwd
Stufe 2: URL-kodiert
?file=%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd
Stufe 3: Doppelt kodiert
?file=%252e%252e%252f%252e%252e%252f%252e%252e%252fetc%252fpasswd
Stufe 4: Null-Byte (Legacy)
?file=../../../etc/passwd%00.pdf
Stufe 5: OS-spezifisch
?file=..\..\..\Windows\win.ini (Windows)
3. Burp Suite Intruder:
→ Payload-Position: ?file=§value§
→ Payload-Liste: SecLists/Fuzzing/LFI/LFI-Jhaddix.txt
→ Grep Match: "root:x:", "[drivers]", "DB_PASSWORD"
4. Indikatoren für Erfolg:
→ Response enthält "/bin/bash" oder "root:x:0:0:" → /etc/passwd
→ Response enthält "[fonts]" → win.ini
→ Fehler-Message enthält Pfad-Information: "Failed to open /etc/..."
5. Automatisiertes Testing:
# Nuclei mit LFI-Templates:
nuclei -u https://target.com -tags lfi
# dotdotpwn:
dotdotpwn -m http -h target.com -u "https://target.com/page?file=TRAVERSAL"
Schutzmaßnahmen
Sichere Implementierungen nach Sprache:
PHP - realpath() für absolute Pfad-Validierung:
$base = realpath('/var/www/uploads');
$requested = realpath($base . '/' . $_GET['file']);
if ($requested === false ||
strpos($requested, $base) !== 0) {
die('Zugriff verweigert');
}
// Erst hier: Datei lesen
Warum realpath() korrekt ist:
→ Löst alle ../ auf (auch URL-kodierte nach Dekodierung)
→ Gibt FALSE zurück wenn Datei nicht existiert → Angriff erkennbar
→ Vergleich mit $base: außerhalb? → DENY!
Python - pathlib für sichere Pfad-Validierung:
from pathlib import Path
BASE_DIR = Path('/var/www/uploads').resolve()
requested = (BASE_DIR / user_input).resolve()
try:
requested.relative_to(BASE_DIR) # Wirft ValueError wenn außerhalb!
except ValueError:
raise PermissionError('Zugriff verweigert')
# Jetzt sicher:
content = requested.read_bytes()
Java - Path.normalize() + startsWith():
Path base = Paths.get("/uploads").toAbsolutePath().normalize();
Path requested = base.resolve(userInput).normalize();
if (!requested.startsWith(base)) {
throw new SecurityException("Path traversal detected");
}
Node.js - path.resolve() + startsWith():
const base = path.resolve('/uploads');
const requested = path.resolve(base, userInput);
if (!requested.startsWith(base + path.sep)) {
return res.status(403).send('Forbidden');
}
Allowlist-Ansatz (am sichersten):
// Statt freie Pfade: nur erlaubte Dateinamen
const ALLOWED_FILES = {
'manual': 'manual.pdf',
'report': 'q4-report.pdf',
};
const filename = ALLOWED_FILES[req.query.doc];
if (!filename) return res.status(400).send('Invalid document');
res.sendFile(path.join('/uploads', filename));
Betriebssystem-Ebene:
□ Web-Server-Prozess nur mit minimalen Rechten (kein root!)
□ chroot-Jail oder Container-Isolation
□ AppArmor/SELinux-Profile: Lesezugriff nur auf definierte Pfade
□ WAF-Regeln: "../" und Kodierungsvarianten blocken
□ Logging: File-Zugriffe für anomale Pfade alertieren