Path traversal
Path traversal
What is path traversal?
Path traversal is also known as directory traversal. These vulnerabilities enable an attacker to read arbitrary files on the server that is running an application. In some cases, an attacker might be able to write to arbitrary files on the server, allowing them to modify application data or behavior, and ultimately take full control of the server.
Typical vulnerable patterns
Path traversal often hides in features that seem harmless. Common vulnerable patterns include:
- File download or export endpoints:
/download?file=report.csv→file=../../../etc/passwd - Log viewers or debuggers:
/view-log?path=latest.log - Template preview features:
/preview?template=invoice.html - Internal file fetchers or backup tools:
/api/fetch?name=backup.tar.gz
Reading arbitrary files via path traversal
Imagine a shopping application that displays images of items for sale. This might load an image using the following HTML:
<img src="/loadImage?filename=218.png">
The loadImage URL takes a filename parameter and returns the contents of the specified file. The image files are stored on disk in the location /var/www/images/. To return an image, the application appends the requested filename to this base directory and uses a filesystem API to read the contents of the file. In other words, the application reads from the following file path:
/var/www/images/218.png
This application implements no defenses against path traversal attacks. As a result, an attacker can request the following URL to retrieve the /etc/passwd file from the server's filesystem:
https://insecure-website.com/loadImage?filename=../../../etc/passwd
This causes the application to read from the following file path:
/var/www/images/../../../etc/passwd
So here we will read the file located in /etc/passwd.
In windows both ../ and ..\ are valid directory traversal sequences.
Common obstacles to exploiting path traversal vulnerabilities
If an application strips or blocks directory traversal sequences from the user-supplied filename, it might be possible to bypass the defense using a variety of techniques.
You might be able to use an absolute path from the filesystem root, such as filename=/etc/passwd, to directly reference a file without using any traversal sequences.
You might be able to use nested traversal sequences, such as ....// or ....\/. These revert to simple traversal sequences when the inner sequence is stripped.
In some cases, Web servers may strip any directory traversal sequences before passing your input to the application. You can sometimes bypass this kind of sanitization by URL encoding, or even double URL encoding, the ../ characters. This results in %2e%2e%2f and %252e%252e%252f respectively. Various non-standard encodings, such as ..%c0%af or ..%ef%bc%8f, may also work.
An application may require the user-supplied filename to end with an expected file extension, such as .png. In this case, it might be possible to use a null byte to effectively terminate the file path before the required extension. For example: filename=../../../etc/passwd%00.png
Tip to accelerate testing
To accelerate testing use tools like ffuf with a custom wordlist:
ffuf -u https://target.com/download?file=FUZZ -w path-wordlist.txt -fc 403
ffuf, which is written in Go, is a fast web fuzzer. The -fc (filter status code) option is used to ignore responses with specific HTTP status codes. In the example above, -fc 403 filters out forbidden responses, freeing researchers up to focus on more relevant results.
Alternatively, we recommend leveraging predefined payload lists like those provided by Wfuzz.
Internal path traversal via internal API forwarding
Once user-controlled input is passed directly into a file access function without restricting it to a base directory, the attack surface expands far beyond directory traversal.
This kind of vulnerability doesn’t rely on ../ sequences. Instead, it stems from backend logic that blindly trusts a full user-supplied path, often within JSON APIs or internal tooling. You’re no longer escaping a path – you’re choosing which files to access, often without constraint.
Cloud paths
In real-world cloud environments, this can lead to severe consequences. Applications running on AWS Lambda, Google Cloud Functions or Azure often store source code and credentials in predictable locations such as /var/task/, /home/site/wwwroot/ or /var/secrets/. Arbitrary read access to these paths can reveal:
- Source code of the deployed service
- Environment variables used for authentication
- Temporary cloud credentials injected by the platform
- API tokens and secrets stored in
.env,config.ymlor JSON config files
/proc tricks
Linux exposes a wealth of process information via the /proc/ filesystem. Reading /proc/self/environ discloses secrets injected into the runtime environment, while /proc/self/cmdline can leak command-line flags passed to the application.
On web servers, arbitrary file read allows you to dump source code files, which may expose hardcoded secrets and unsafe code patterns. These insights often lead to higher-impact vulnerabilities.
User-upload exploits
You can also explore user-uploaded data or internal log files. Predictable file naming conventions – such as /uploads/42/resume.pdf – enable horizontal privilege escalation by exploring paths and accessing private documents. If session files are stored on disk, reading them may allow for full session hijacking.
For example, if you’re able to upload a file and control its content type or extension, you could escalate a vulnerability to remote code execution by tricking the server into interpreting your upload as executable code!
Valuable files to target
Once you confirm file read capabilities, shift your focus to targets with the greatest potential impacts:
Unix/Linux
/etc/passwd– Proof of read/etc/shadow– Credential hashes/proc/self/environ– Environment variables (look for AWS keys, tokens, debug flags).env,config.json,settings.py,config.yaml– Secrets (DB creds, API keys, cloud access)/var/run/secrets/kubernetes.io/serviceaccount/token– Kubernetes service account token.bash_history,.git/config– operational commands and repo info
Windows
C:\Windows\win.ini– Classic PoCC:\Users\Administrator\NTUser.dat– user data and registry hivesC:\inetpub\wwwroot\web.config– IIS credentials and app secrets%APPDATA%\Microsoft\Credentials\– Windows stored credentials
Web-specific and deployment-related
.git/config,.svn/entries– source control metadatacomposer.lock,package-lock.json,yarn.lock– dependency info (may expose vulnerable packages)docker-compose.yml,terraform.tfvars,cloudbuild.yaml– infrastructure-as-code secrets- Log files (
access.log,error.log, etc.) – session tokens, JWTs, IPs, headers credentials.json,secrets.json,.npmrc,.pypirc– API tokens, cloud credsaws/credentials,~/.aws/config– AWS IAM keys.kube/config– Kubernetes cluster access.npmrc,.gem/credentials,.docker/config.json– Package registry or container access tokens
These files often contain JWT secrets, SMTP credentials, database logins, API keys, hardcoded cloud tokens and internal service credentials used across CI/CD pipelines or production systems.
How to prevent a path traversal attack
The most effective way to prevent path traversal vulnerabilities is to avoid passing user-supplied input to filesystem APIs altogether. Many application functions that do this can be rewritten to deliver the same behavior in a safer way. If you can't avoid passing user-supplied input to filesystem APIs, we recommend using two layers of defense to prevent attacks:
- Validate the user input before processing it. Ideally, compare the user input with a whitelist of permitted values. If that isn't possible, verify that the input contains only permitted content, such as alphanumeric characters only.
- After validating the supplied input, append the input to the base directory and use a platform filesystem API to canonicalize the path. Verify that the canonicalized path starts with the expected base directory.
Below is an example of some simple Java code to validate the canonical path of a file based on user input:
File file = new File(BASE_DIRECTORY, userInput);
if (file.getCanonicalPath().startsWith(BASE_DIRECTORY)) {
// process file
}
And if you’re building microservices or APIs, be especially careful with internal routing logic. Don’t let path segments or filenames be built from user data without full validation and constraint.