# Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (CWE-22) The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory. - Prevalence: High Frequently exploited - Impact: Critical 1 critical-severity rules - Prevention: Documented 6 fix examples **OWASP:** Broken Access Control (A01:2021-Broken Access Control) - #1 ## Description Many file operations are intended to take place within a restricted directory. By using special elements such as '..' and '/' separators, attackers can escape outside of the restricted location to access files or directories that are elsewhere on the system. ## Prevention Prevention strategies for Path Traversal based on 6 Shoulder detection rules. ### Go Resolve the full path and verify it stays within the intended base directory Validate that extracted archive paths resolve within the target directory ### Node.js Use path.basename() to strip directory components or validate resolved paths stay within allowed directories Validate that extracted archive entry paths resolve within the target directory before writing ### Python Use os.path.basename() or pathlib to restrict file access to intended directories Validate extracted file paths stay within the target directory before writing ## Warning Signs - [HIGH] unsafe extraction of zip/tar archives without path validation, which can lead to arbitrary file writ - [HIGH] untrusted user input being used in file system operations without proper validation - [HIGH] unsafe extraction of ZIP/TAR archives without path validation - [CRITICAL] untrusted user input used in file system operations without proper validation ## Consequences - Read Files or Directories - Modify Files or Directories - Execute Unauthorized Code ## Mitigations - Use a vetted library or framework that does not allow this weakness to occur - Use an allowlist of acceptable inputs that strictly conform to specifications - For filenames, use stringent allowlists that limit the character set ## Detection - Total rules: 6 - Critical: 1 - Languages: go, javascript, typescript, python ## Rules by Language ### Go (2 rules) - **Path Traversal via File Operations** [HIGH]: User input flows to file operations like os.Open without path validation. - Remediation: Validate resolved path stays within the base directory. ```go cleanPath := filepath.Clean(filename) fullPath := filepath.Join(baseDir, cleanPath) absPath, _ := filepath.Abs(fullPath) absBase, _ := filepath.Abs(baseDir) if !strings.HasPrefix(absPath, absBase+string(os.PathSeparator)) { return errors.New("invalid path") } ``` Learn more: https://shoulder.dev/learn/go/cwe-22/path-traversal - **Zip Slip / Path Traversal in Archive** [HIGH]: Archive extraction uses filename without validating it stays within target directory. - Remediation: Validate extracted paths are within the target directory. ```go destPath := filepath.Join(destDir, filepath.Clean(f.Name)) if !strings.HasPrefix(destPath, filepath.Clean(destDir)+string(os.PathSeparator)) { return errors.New("illegal file path") } outFile, _ := os.Create(destPath) ``` Learn more: https://shoulder.dev/learn/go/cwe-22/zip-slip ### Javascript (2 rules) - **Path Traversal in File Operations** [CRITICAL]: Detects untrusted user input used in file system operations without proper validation. This can allow attackers to read or write arbitrary files on the server. - Remediation: Use path.basename() to extract filenames or validate resolved paths stay within allowed directories. ```javascript const safeName = path.basename(userInput); const filePath = path.join(__dirname, 'uploads', safeName); fs.readFile(filePath, callback); ``` Learn more: https://shoulder.dev/learn/javascript/cwe-22/path-traversal - **Zip Slip Path Traversal** [HIGH]: Detects unsafe extraction of zip/tar archives without path validation, which can lead to arbitrary file writes via path traversal (Zip Slip). Zip Slip is a form of path traversal attack where a malicious archive contains entries with paths like "../../../etc/passwd" that escape the intended extraction directory and overwrite arbitrary files on the system. Vulnerable patterns: 1. Extracting zip entries without validating the extracted path 2. Not checking if extracted path is inside target directory 3. Trusting entry.fileName from the archive 4. Not normalizing/resolving paths before extraction Impact: - Arbitrary file overwrite (RCE if overwriting .bashrc, cron jobs, etc.) - Configuration tampering - Code injection (overwriting source files) - Data exfiltration (overwriting log files) - Remediation: Validate extracted paths are inside the target directory: ```javascript const path = require('path'); function isPathSafe(baseDir, targetPath) { const resolvedBase = path.resolve(baseDir); const resolvedTarget = path.resolve(baseDir, targetPath); return resolvedTarget.startsWith(resolvedBase + path.sep); } for (const entry of zip.getEntries()) { if (!isPathSafe(targetDir, entry.entryName)) { throw new Error('Path traversal attempt'); } zip.extractEntryTo(entry, targetDir, false, true); } ``` Learn more: https://shoulder.dev/learn/javascript/cwe-22/zip-slip ### Typescript (2 rules) - **Path Traversal in File Operations** [CRITICAL]: Detects untrusted user input used in file system operations without proper validation. This can allow attackers to read or write arbitrary files on the server. - Remediation: Use path.basename() to extract filenames or validate resolved paths stay within allowed directories. ```javascript const safeName = path.basename(userInput); const filePath = path.join(__dirname, 'uploads', safeName); fs.readFile(filePath, callback); ``` Learn more: https://shoulder.dev/learn/javascript/cwe-22/path-traversal - **Zip Slip Path Traversal** [HIGH]: Detects unsafe extraction of zip/tar archives without path validation, which can lead to arbitrary file writes via path traversal (Zip Slip). Zip Slip is a form of path traversal attack where a malicious archive contains entries with paths like "../../../etc/passwd" that escape the intended extraction directory and overwrite arbitrary files on the system. Vulnerable patterns: 1. Extracting zip entries without validating the extracted path 2. Not checking if extracted path is inside target directory 3. Trusting entry.fileName from the archive 4. Not normalizing/resolving paths before extraction Impact: - Arbitrary file overwrite (RCE if overwriting .bashrc, cron jobs, etc.) - Configuration tampering - Code injection (overwriting source files) - Data exfiltration (overwriting log files) - Remediation: Validate extracted paths are inside the target directory: ```javascript const path = require('path'); function isPathSafe(baseDir, targetPath) { const resolvedBase = path.resolve(baseDir); const resolvedTarget = path.resolve(baseDir, targetPath); return resolvedTarget.startsWith(resolvedBase + path.sep); } for (const entry of zip.getEntries()) { if (!isPathSafe(targetDir, entry.entryName)) { throw new Error('Path traversal attempt'); } zip.extractEntryTo(entry, targetDir, false, true); } ``` Learn more: https://shoulder.dev/learn/javascript/cwe-22/zip-slip ### Python (2 rules) - **Path Traversal / Directory Traversal** [HIGH]: Detects untrusted user input being used in file system operations without proper validation. - Remediation: Use os.path.basename() to extract the filename only. ```python import os safe_filename = os.path.basename(user_filename) ``` Learn more: https://shoulder.dev/learn/python/cwe-22/path-traversal - **Zip Slip / Archive Path Traversal** [HIGH]: Detects unsafe extraction of ZIP/TAR archives without path validation. Malicious archives can contain filenames with "../" to write files outside the intended directory (path traversal). Always validate extracted paths. - Remediation: Validate that extracted paths stay within the target directory before writing. ```python import zipfile import os def safe_extract(zip_path, extract_to): with zipfile.ZipFile(zip_path, 'r') as zf: for member in zf.namelist(): target = os.path.normpath(os.path.join(extract_to, member)) if not target.startswith(os.path.abspath(extract_to)): raise ValueError(f"Path traversal: {member}") zf.extract(member, extract_to) ``` Learn more: https://shoulder.dev/learn/python/cwe-22/zip-slip