Skip to content

Path traversal (directory traversal)

CWE-22OWASP A01:2021Updated August 29, 20265 min read

Path traversal, also called directory traversal, is a vulnerability that lets an attacker manipulate a file path so the application opens files outside the intended folder. Using the ../ sequence they climb out of the permitted directory and read sensitive files such as configs, keys or source code.

Path traversal, also known as directory traversal, is a classic yet still common web vulnerability: with a handful of characters an attacker breaks out of the folder your application meant to keep them in and reads files elsewhere on the server. This article explains what path traversal is, how such an attack unfolds, what an attacker can achieve with it, and how to protect your application against it.

What is path traversal?

Path traversal (also called directory traversal) is a vulnerability where an attacker manipulates a filename or path taken from user input so that the application opens files outside the intended folder. The key is the ../ sequence, which on virtually every operating system means “go up one directory”. By repeating that sequence often enough, the attacker climbs all the way to the root of the file system and then descends to exactly the file they are after.

Picture an archive clerk who retrieves documents based on directions a visitor writes down. The intention is that they only rummage through the public cabinet. But the form literally asks for a path, and the visitor writes: “leave the public cabinet, go up one floor, into the staff cabinet, grab the salary file.” The clerk dutifully follows the instruction, because after all it is neatly filled in. The ../ sequence is exactly that instruction “up one floor”, and the application carries it out without question.

How does a path traversal attack work?

Path traversal shows up wherever an application combines a user-supplied name with a path on the server and then opens the result: a download feature, a template loader, a language file or a “view log file” screen. As long as that input is not checked, the user decides which path is produced.

Vulnerable:

// Download feature that appends the given filename straight onto a base path
app.get('/download', (req, res) => {
  const file = req.query.file;
  const filePath = path.join('/var/www/files', file);
  res.sendFile(filePath);
});

In normal use, someone requests /download?file=manual.pdf and gets /var/www/files/manual.pdf back. But path.join happily resolves ../. If an attacker requests /download?file=../../../../etc/passwd, the base path evaporates during that calculation and the server hands over /etc/passwd, the list of system users. On Windows the same works with the backslash, through a request like ..\..\..\windows\win.ini or a config file full of passwords.

Safe:

import path from 'node:path';

const BASE_DIR = path.resolve('/var/www/files');

app.get('/download', (req, res) => {
  const requested = req.query.file ?? '';

  // Fully resolve the path and confirm it stays inside the base directory
  const resolved = path.resolve(BASE_DIR, requested);
  if (resolved !== BASE_DIR && !resolved.startsWith(BASE_DIR + path.sep)) {
    return res.status(403).send('Access denied');
  }

  res.sendFile(resolved);
});

The safe version trusts the end result, not the input. First the full path is resolved with path.resolve, so every ../ jump is actually calculated out. Then the code checks whether that resolved path still falls inside BASE_DIR. If it points outside the base directory, no matter how many ../ the attacker stacks, the request is refused. This after-the-fact check is more reliable than trying to filter ../ out of the input, because such filtering can be bypassed again and again with double encoding, backslashes or exotic variants.

Do not simply filter the string ../ out of the input. Attackers bypass such a filter with URL encoding (%2e%2e%2f), double encoding (%252e), backslashes or nested variants like ....//. Validate the final, resolved path rather than the raw input.

What is the impact of path traversal?

What a path traversal is worth depends on what is sitting on the server, which explains the span from medium to high. In the best case an attacker only reads files that give little away. But usually there is more within reach: source code, config files with database passwords, a .env full of API keys, SSH private keys or session files that let accounts be hijacked.

Technically the damage reaches further than reading alone. If the attacker can also write or overwrite files through the same flaw (think of an upload or extraction feature that does not check paths, the so-called zip slip) they can drop a web shell or replace existing code, upgrading path traversal into remote code execution. For the business that means a data breach at the very least, and a fully compromised server in the worst case. Because the flaw needs no special tooling and is often exploitable with a single HTTP request, it is probed on a large scale by automated attackers.

How do you detect path traversal?

During a manual test, a pentester enters variants of ../ into every field that looks like it handles a filename, path, language or template, and watches what comes back. If ../../../../etc/passwd returns the contents of that file, or an error message leaks an absolute path, that is a hit. Testers also try encoding tricks (URL encoding, double encoding, backslashes on Windows) to expose weak filters. Parameters that look suspiciously like file references, such as file=, path=, template= or lang=, get extra attention. Automated scanners find the obvious cases but often miss the spots where input reaches a path indirectly. AssistSec includes path traversal as standard in a penetration test and deliberately probes those less visible input points.

How do you prevent path traversal?

  • Resolve the full path and confirm it stays within the permitted base directory; reject everything else.
  • Where possible, use an allowlist or an indirect reference, for example an id you map to a fixed filename, instead of a freely typed path.
  • Strip directory components with path.basename when the user only needs to supply a bare filename.
  • Fully decode input before validating it, so %2e%2e%2f and double encoding cannot slip past a filter.
  • Do not rely on filtering out ../; always validate the final, resolved path.
  • Run the application with minimal privileges, so the process can read as few files as possible if it is abused.
  • Keep sensitive files such as .env, keys and backups outside any directory the web server can reach.

Sources

Frequently asked questions

What is the difference between path traversal and local file inclusion?

Path traversal is about reading, and sometimes writing, arbitrary files by manipulating the path. In local file inclusion, or LFI, an included file is not only read but also executed, so the impact is usually more severe. In practice LFI is often a path traversal that lands inside an include function.

Does path traversal work on Windows servers too?

Yes. Besides the Unix notation with ../, Windows also accepts the backslash as a separator, so the same trick works there with backslashes. Attackers usually try both variants, plus encoded forms, to slip past filters.

Is path.join enough to prevent path traversal?

No. path.join actually resolves ../, so a path like ../../etc/passwd is simply resolved to a file outside your directory. You must then explicitly check the resolved path against the permitted base directory.

Can path traversal lead to full server takeover?

Yes. If an attacker can also write or overwrite files through the same flaw, or get an included file executed, they can drop a web shell and reach remote code execution. Read-only access is already serious, but write access pushes the impact to server takeover.

Related articles

Press / to search · Esc