FlawOpen

Vulnerabilities/Path Traversal

Path Traversal

High CWE-22
Language: English Português (Brasil) Español Français Deutsch Русский 简体中文 日本語 हिन्दी 한국어 Bahasa Indonesia

Path traversal happens when an application builds a file path from request data and the operating system follows ../ segments, absolute names or symbolic links out of the folder the developer intended. Every language's path-join function keeps those segments, and string prefix checks are easy to get wrong. The fix is the same everywhere: resolve the final path, check containment by path component against the resolved base, or better, open files through an API that the kernel confines, and extract archives with a library that rejects unsafe member names. Pick your language below for the exact APIs, vulnerable and fixed code, and the linters that catch it.

Plain English Explainer (ELI5)

A records clerk may only fetch papers from your folder in the Customers cabinet. You hand in a slip that says: 'Start in my folder, step back out of it, step out of the Customers cabinet, go into Payroll and bring the salary sheet.' The clerk follows each step literally and never checks where the walk ends, so you get Payroll's file. A file name like ../../etc/passwd is that slip: each ../ means 'step out one folder'. The fix is for the clerk to work out the final location first and refuse any slip that ends outside your folder.

Core Concepts & Subsystem Terms

Path traversal (CWE-22)
An application uses untrusted input to build a file-system path and does not keep the result inside the intended directory, so a request can read or write files elsewhere with the application's permissions.
Joining is not confining
os.path.join, path.join, filepath.Join, Path.Combine, File.join and Path::join only glue strings together. They keep .. segments, and several of them discard the base entirely when the user's part is absolute.
Canonical path and containment check
Resolve the combined path to its canonical form (no ., no .., links followed), then require that it starts with the resolved base plus a path separator, or compare path components. Comparing strings without the separator accepts sibling folders such as uploads-old.
Symbolic links and races
A lexical check cannot see a symbolic link inside the folder that points elsewhere, and a check done with realpath before open can be raced by swapping a link in between. Kernel-enforced APIs such as openat2 with RESOLVE_BENEATH or Go's os.Root close both gaps.
Zip Slip
Archive members named ../../app/config are written outside the extraction folder by code that joins them onto it. Snyk's 2018 research found this in many libraries; use extractors that reject such names (filter="data", unpack_in, ExtractToDirectory, libarchive's secure flags).

Step-by-Step Attack Flow

Step 1

Request data reaches a file API

A file name from a query string, form field, API response or archive member is joined onto an upload, template or report directory.

Step 2

The name leaves the intended folder

It contains ../ segments, is absolute, names a sibling folder with the same prefix, or reaches a symbolic link. The benign test value ../../etc/hostname illustrates the read case.

Step 3

The operating system opens the resolved path

The kernel resolves every .. and link and opens whatever file the final path names, with the application's permissions.

Step 4

The attacker reads or overwrites files

Reads expose configuration, keys and source code; writes can replace templates, scripts or libraries that the application later executes.

Path traversal guides by language

Python JavaScript Go Java PHP C# Ruby C/C++ Rust Kotlin Swift

Engineering & System Hardening Checklist

Related reading

Is path.join() enough? Is os.path.join() safe? What is Zip Slip? Python tarfile extractall traversal