What happens when a single parameter lets an attacker step outside an app’s safe folder? I found a directory traversal bug that pivoted from a simple input to exposing server configuration and sensitive system data. This was not theoretical—proofs included classic targets like /etc/passwd and C:\Windows\win.ini.
Unsafe user input flowed straight into file resolution code. By controlling one file or template parameter, I navigated out of the intended directory and gained access the application never meant to expose.
This guide is practical. We will define directory traversal, analyze the vulnerable code, unpack payload encodings, and note OS and framework quirks. For foundational guidance, see the OWASP note on Path Traversal.
Key Takeaways
- Directory traversal risks arise when user input reaches file resolution without validation.
- Simple reads of system files prove arbitrary access and escalate exposure fast.
- Encodings and mixed separators often bypass weak filters.
- Reproducible examples help teams test safely in nonproduction environments.
- Concrete defenses include canonicalization, allowlists, and strict resolution controls.
What Is Path Traversal and Why It Still Matters Today
When user-supplied file names are stitched into file operations, attackers may climb out of the intended folder. This class of weakness lets code resolve locations outside the web root and expose sensitive system information. Modern stacks, legacy scripts, and cloud functions all increase the places where these mistakes appear.

Directory traversal versus root directory and ACLs
Directory traversal happens when unchecked input is combined with file operations so that an outsider moves above the web root. The root confines an application to its served directory. Access Control Lists (ACLs) still matter, but traversal often happens before the OS applies those rules.
Impact: from file reads to full system compromise
The impact ranges from proof-of-read of classic targets like /etc/passwd to disclosure of secrets that enable lateral movement. An attacker needs little more than an editable parameter, common locations, and trial-and-error to make progress.
- Vectors: ../ tokens, absolute names, mixed slashes, and encodings bypass weak checks.
- Platforms: UNIX and Windows handle separators differently, which complicates filters.
- Mitigation note: canonicalize and enforce base directory constraints, and use allowlists.
For practical testing tips and an in-depth primer on this class of vulnerabilities see the directory traversal guide.
How a path traversal flaw can read server configuration files
A few directory tokens appended to a template name often take you straight to secrets the application never intended to show. By escaping the intended base folder, that single trick exposes credentials, runtime values, and source that should stay private.
From ../ to secrets: mapping the path to sensitive configs
A typical exploit appends repeated ../ sequences to a user-controlled parameter so the resolved file lands outside the web root. Start with a harmless target to confirm access, then escalate to high-value artifacts such as .env, web.config, config.php, settings.py, or config.yaml.
High‑value targets: .env, web.config, settings, and cloud paths
- Check cloud/runtime locations like /var/task/ and /proc/self/environ for deployed source and environment secrets.
- Absolute path acceptance lets attackers request /etc/passwd or C:\inetpub\wwwroot\web.config when checks are absent.
- Verbose errors reveal application directories that speed mapping and refinement.

| Target | Why it matters | Typical impact |
|---|---|---|
| .env | Contains DB credentials and API tokens | Data breaches, DB access |
| web.config / config.php | Application settings and secrets | Service compromise, credential exposure |
| /var/task, /proc/self/environ | Deployed source and runtime env values | Token leaks, cloud credential theft |
For practical remediation steps and testing guidance, see the path traversal vulnerability guide.
The Vulnerable Code That Made It Possible
One line of code tied a cookie directly to include(), giving outsiders control over resolved targets. This concise example shows the exact anti-pattern that leads to directory traversal and broad disclosure in web apps.
Unsafe file inclusion pattern and cookie-driven template selection
The pattern is simple: take user input and concatenate it into an include or read call. In our PHP example the script sets $template from a cookie and runs include(“/home/users/phpguru/templates/” . $template). Supplying ../../../../../../../../../etc/passwd lets the call climb above the web root and return sensitive content.

It fails because the code never canonicalizes the final path or enforces a base directory. Web containers often decode percent‑encoded characters once, so %2e%2e%2f becomes ../ before the operation runs. Older PHP also accepted null bytes (%00), which let attackers bypass naive extension checks.
- Surface: GET, POST, cookies, and headers may all carry user input into file resolution.
- Portability: Node.js, Python, and Java show similar risks when joins lack post‑join validation.
- Fix note: Reject regex-only blocks; prefer indirection, canonicalize, and enforce an allowlist or base directory.
Audit any include/open/read call that builds a path from external values. Small design changes remove broad control and turn this vulnerability into a nonissue.
Request Payloads and Encoding Tricks Attackers Use
Attackers often hide traversal tokens inside layered encodings to slip past simple filters. This section lists practical payload types, explains why they work, and offers guidance to test safely.

Common encodings include %2e%2e%2f and double‑encoded forms like %252e%252e%255c. These pass through proxies or gateways then resolve into ../ or ..\ on the backend.
Dotless and mixed‑slash tricks
Dotless forms such as ./////.////etc////passwd and mixed slashes like ..\\..\\ defeat filters that search only for “../”.
Null bytes, overlong UTF‑8, and legacy quirks
Historic weaknesses let payloads end with %00.pdf or use overlong encodings like ..%c0%af. These appear in older stacks and still surface in chained systems.
Why multi‑layer decoders amplify bypasses
Proxies, web gateways, and frameworks each decode once. Double‑encoded URLs or JSON fields may be safe at the edge but dangerous after internal decoding.
“Always assume user input might be encoded, nested, or routed through multiple decoders.”
- IIS mis‑parsing shows escalation to commands: scripts/..%5c../Windows/System32/cmd.exe?/c+dir+c:\.
- Test cautiously: throttle fuzzing, capture payloads, and avoid production noise.
- Defend in layers: normalize, resolve absolute paths, then enforce base directory allowlists.
OS and Framework Nuances That Change the Attack Surface
OS differences and runtime quirks shift risk in real-world systems. Small inconsistencies let attackers bypass naive checks and expose sensitive directories.
Mixed separators matter. UNIX uses / exclusively, while Windows accepts both \ and /. That expands bypass options when filters only look for one form.
Windows also tolerates trailing dots and slashes. Extra characters may be ignored by the system, which defeats simple string comparisons and sanitizers.

Language behaviors worth noting
- PHP: Older builds accepted null‑byte truncation, breaking naive extension checks.
- Java: Use File.getCanonicalPath() and compare against an allowlisted base rather than trusting raw paths.
- Node.js: path.resolve normalizes but does not enforce scope; verify the resolved result stays under your base.
- Python: os.path.abspath and pathlib help, yet require explicit base checks after resolution.
- Go / serverless: Functions often expose deployment roots like /var/task; those directories may contain source and secrets.
Developer guidance: centralize file access helpers that normalize, canonicalize, then enforce base constraints. Test with mixed separators and encoded tokens in an isolated environment before rolling filters into production.
Modern Entry Points: APIs, Microservices, and Cloud Runtimes
APIs and microservice layers have become common vectors for traversal attacks. These modern entry points move user-controlled strings through several decoders and routers before they hit file resolution. That extra complexity multiplies risk and makes testing essential.
Internal API forwarding sometimes builds backend routes from user-controlled strings, widening attack scope. Gateways that concatenate segments may accept “../” style sequences inside JSON or GraphQL arguments and forward them unchecked.

JSON fields, GraphQL arguments, and internal forwarding
Identify inputs. JSON keys like “filename” or “templatePath” and GraphQL calls such as readFile(path:”…”) often carry unsafe values. Those fields travel through proxies and serializers that may decode or normalize tokens.
Watch internal forwarding. An API gateway that turns POST /api/v1/users/../../admin/roles into an internal route effectively exposes privileged endpoints to traversal attack probes.
Serverless, containers, and runtime secrets
Consider deployment roots. In serverless runtimes, directories such as /var/task and Azure’s /home/site/wwwroot often hold deployed source and source code. Access to those directories equals code disclosure.
Leverage /proc and mounts. Paths like /proc/self/environ and mounted secrets under /var/run/secrets reveal environment values and tokens when reached by an attack. Even status codes or timing differences may confirm access attempts.
- Safe PoCs: start with innocuous files, avoid brute force, and follow program rules.
- Dev takeaway: validate and constrain paths at each layer—frontend, gateway, and service. Prefer indirection (IDs) over raw path acceptance.
- Further reading: see this writeup on escaping containers and reading /etc/passwd for modern examples.
“Treat any feature that accepts user path input as high risk and test it in isolation.”
Testing Safely: Proofs of Concept, Wordlists, and Tooling
Start tests with minimal, low-impact proofs that demonstrate exposure without touching sensitive business data. Keep scope tight and explain each step to stakeholders before running automated sweeps.

Crafting PoC requests without overloading targets
Start with a single harmless read such as /etc/passwd or C:\Windows\win.ini to confirm a weakness. Log requests and responses so findings are reproducible.
Throttle activity, respect allowed windows, and avoid heavy concurrency. Show impact with minimal data and provide remediation guidance alongside evidence.
Fuzzing with ffuf/Wfuzz and filtering noisy responses
Use focused wordlists that include ../ variants, encodings, dotless forms, and mixed slashes. Example ffuf command:
ffuf -u https://target.com/download?file=FUZZ -w path-wordlist.txt -fc 403
Wfuzz ships payload sets useful for quick checks. Filter by status code, response size, or regex to isolate probable hits.
“Validate issues with low-risk checks, then expand tooling only with approval.”
| Action | Tool | Why |
|---|---|---|
| Minimal PoC | curl / browser | Confirm exposure without exfiltration |
| Fuzzing | ffuf / Wfuzz | Automate broad payload coverage with filters |
| Log & Compare | Burp / proxy | Store requests to help developers fix issues |
- Track layers: test query params, JSON bodies, cookies, and headers.
- Observe deltas: small size or timing differences often reveal normalization.
- Stay ethical: never attempt command execution or large-scale exfiltration without permission.
Defenses That Actually Work in Production
Remove direct file control and enforce strict, server-side mapping. Normalize and verify every resolved path before any I/O happens.
Design changes often stop exploitation before filters ever run. Replace client-supplied names with opaque IDs and map those to allowlisted resources on the backend. This removes raw control and limits access to known-good targets.
Design out raw control with indirection and allowlists
Prefer indexes over names. Store mapping in code or database and return only approved items. Reject absolute inputs and never expose full file paths to clients.
Canonicalize, resolve, then enforce base-directory constraints
Call realpath or getCanonicalPath, then verify the resolved value starts within your safe root. Log mismatches and deny access when checks fail.
OS-level hardening and web root hygiene
Use chroot, containers, or jails and run the web server user with least privilege. Keep secrets off the web root and move IIS roots off the system disk.
Patch, prune, and centralize helpers
- Remove legacy scripts that introduce vulnerabilities.
- Centralize file access in one audited utility that normalizes, validates, and logs.
- Monitor for encoded attack patterns and rate-limit probes.
“Design, canonicalize, harden—then test.”
For related hardening of web server headers and runtime settings, see secure web server headers.
Conclusion
Directory traversal remains one of the simplest yet most damaging weaknesses in modern apps. Design and discipline stop exploitation far more reliably than brittle filters.
A single unchecked input frequently exposes far more than developers expect. Treat any feature that accepts file-like names as high risk and prefer indirect mapping (IDs) over direct references.
Prioritize canonicalization, enforce base-directory checks, and harden OS boundaries to make attacks unexploitable. Test responsibly with curated payloads and tools such as ffuf or Wfuzz to confirm fixes without harming production.
Share playbooks listing high-value targets and practical test steps. For extra reading, see this path traversal writeup and the Apache zero-day advisory for recent cases and vendor guidance.