acc-check-path-traversal

📁 dykyi-roman/awesome-claude-code 📅 2 days ago
1
总安装量
1
周安装量
#44570
全站排名
安装命令
npx skills add https://github.com/dykyi-roman/awesome-claude-code --skill acc-check-path-traversal

Agent 安装分布

opencode 1
claude-code 1

Skill 文档

Path Traversal Security Check

Analyze PHP code for path/directory traversal vulnerabilities (OWASP A01:2021).

Detection Patterns

1. File Operations with User Input

// CRITICAL: Direct path from user
$content = file_get_contents($_GET['file']);
$content = file_get_contents('/uploads/' . $_GET['filename']);

// CRITICAL: File reading
$data = file($_POST['path']);
$lines = readfile($request->input('document'));

// CRITICAL: File writing
file_put_contents('/reports/' . $_GET['name'], $content);

// CRITICAL: File deletion
unlink('/uploads/' . $_POST['file']);
unlink($basePath . $userInput);

2. Include/Require with User Input

// CRITICAL: Local File Inclusion (LFI)
include $_GET['page'];
include 'templates/' . $_GET['template'] . '.php';
require $request->input('module');

// CRITICAL: Remote File Inclusion (RFI) if allow_url_include=On
include 'http://' . $_GET['host'] . '/script.php';

// CRITICAL: Dynamic class loading
$class = $_GET['type'];
require "classes/{$class}.php";

3. Directory Traversal Patterns

// CRITICAL: Basic traversal
$file = '/var/www/uploads/' . $_GET['name'];
// Input: ../../../etc/passwd
// Result: /var/www/uploads/../../../etc/passwd = /etc/passwd

// CRITICAL: Encoded traversal
// Input: ..%2F..%2F..%2Fetc/passwd (URL encoded)
// Input: ..%252f..%252f..%252fetc/passwd (double encoded)

// CRITICAL: Null byte injection (PHP < 5.3.4)
$file = '/templates/' . $_GET['name'] . '.php';
// Input: ../../../etc/passwd%00
// Result: /templates/../../../etc/passwd (null terminates string)

4. File Upload Path Manipulation

// CRITICAL: User-controlled upload path
$uploadDir = '/uploads/' . $_POST['folder'] . '/';
move_uploaded_file($tmp, $uploadDir . $filename);

// CRITICAL: Original filename used directly
$filename = $_FILES['upload']['name'];
move_uploaded_file($tmp, "/uploads/{$filename}");
// Filename: ../../../var/www/html/shell.php

5. Archive Extraction (Zip Slip)

// CRITICAL: Zip extraction without path validation
$zip = new ZipArchive();
$zip->open($_FILES['archive']['tmp_name']);
$zip->extractTo('/uploads/');
// Archive may contain: ../../var/www/html/malicious.php

// CRITICAL: Tar extraction
$phar = new PharData($uploadedFile);
$phar->extractTo('/uploads/');

6. Symlink Attacks

// CRITICAL: Following symlinks
$realPath = '/uploads/' . $_GET['file'];
$content = file_get_contents($realPath);
// Attacker creates symlink: uploads/secret -> /etc/passwd

// CRITICAL: Checking existence without symlink validation
if (file_exists($userPath)) {
    include $userPath;
}

7. Image/File Processing

// CRITICAL: Image path from user
$image = imagecreatefromjpeg('/images/' . $_GET['photo']);

// CRITICAL: PDF generation with user paths
$pdf->image('/assets/' . $_POST['logo']);

// CRITICAL: File download
$filepath = '/downloads/' . $_GET['file'];
header('Content-Disposition: attachment; filename="' . basename($filepath) . '"');
readfile($filepath);

8. Log File Access

// CRITICAL: Log file path manipulation
$logFile = '/var/log/' . $_GET['app'] . '.log';
$content = file_get_contents($logFile);

// CRITICAL: Log viewing
$logPath = "/logs/{$_GET['date']}/app.log";
return file($logPath);

9. Configuration File Access

// CRITICAL: Config path from user
$config = parse_ini_file('/config/' . $_GET['env'] . '.ini');

// CRITICAL: YAML loading
$yaml = yaml_parse_file('/config/' . $_POST['file']);

10. Backup/Export Path

// CRITICAL: Backup destination from user
$backupPath = $_POST['backup_path'] . '/backup.sql';
file_put_contents($backupPath, $sqlDump);

// CRITICAL: Export path
$exportDir = '/exports/' . $_GET['folder'];
mkdir($exportDir, 0777, true);

Grep Patterns

# File operations with variable
Grep: "(file_get_contents|file_put_contents|fopen|readfile|file)\s*\([^)]*\\\$" --glob "**/*.php"

# Include/require with variable
Grep: "(include|require|include_once|require_once)\s+[^;]*\\\$" --glob "**/*.php"

# Path concatenation
Grep: "(/uploads/|/downloads/|/files/|/images/).*\\\$" --glob "**/*.php"

# Unlink/delete with variable
Grep: "(unlink|rmdir)\s*\([^)]*\\\$" --glob "**/*.php"

# Archive extraction
Grep: "(extractTo|extract)\s*\(" --glob "**/*.php"

# move_uploaded_file
Grep: "move_uploaded_file\s*\(" --glob "**/*.php"

Secure Patterns

Validate with realpath()

// SECURE: Validate path is within allowed directory
final class SafeFileReader
{
    public function __construct(
        private readonly string $baseDir,
    ) {}

    public function read(string $filename): string
    {
        $basePath = realpath($this->baseDir);
        $fullPath = realpath($this->baseDir . '/' . $filename);

        if ($fullPath === false) {
            throw new FileNotFoundException('File not found');
        }

        if (!str_starts_with($fullPath, $basePath . '/')) {
            throw new SecurityException('Path traversal detected');
        }

        return file_get_contents($fullPath);
    }
}

Basename for Filenames

// SECURE: Extract only filename, no path
$filename = basename($_GET['file']);
$filepath = '/uploads/' . $filename;

// SECURE: Remove path separators
$safeName = str_replace(['/', '\\', '..'], '', $_GET['file']);

Whitelist Approach

// SECURE: Whitelist allowed files
final class TemplateLoader
{
    private const ALLOWED_TEMPLATES = [
        'header',
        'footer',
        'sidebar',
        'content',
    ];

    public function load(string $name): string
    {
        if (!in_array($name, self::ALLOWED_TEMPLATES, true)) {
            throw new InvalidArgumentException('Invalid template');
        }

        return file_get_contents("/templates/{$name}.php");
    }
}

ID-Based File Access

// SECURE: Use database ID instead of filename
final class DocumentController
{
    public function download(int $documentId): Response
    {
        $document = $this->repository->find($documentId);

        if ($document === null) {
            throw new NotFoundException();
        }

        // User can't manipulate stored path
        return $this->file($document->getStoredPath());
    }
}

Secure Archive Extraction

// SECURE: Validate paths before extraction
final class SafeZipExtractor
{
    public function extract(string $zipPath, string $destDir): void
    {
        $zip = new ZipArchive();
        $zip->open($zipPath);

        $destDir = realpath($destDir);

        for ($i = 0; $i < $zip->numFiles; $i++) {
            $filename = $zip->getNameIndex($i);
            $targetPath = $destDir . '/' . $filename;

            // Resolve path
            $realTarget = realpath(dirname($targetPath));
            if ($realTarget === false) {
                $realTarget = $destDir;
            }

            // Validate within destination
            if (!str_starts_with($realTarget, $destDir)) {
                throw new SecurityException("Zip slip detected: {$filename}");
            }
        }

        $zip->extractTo($destDir);
        $zip->close();
    }
}

Disable Symlinks

// SECURE: Check if path is symlink
final class SafeFileHandler
{
    public function read(string $path): string
    {
        if (is_link($path)) {
            throw new SecurityException('Symlinks not allowed');
        }

        $realPath = realpath($path);
        if ($realPath === false || is_link($realPath)) {
            throw new SecurityException('Invalid path');
        }

        return file_get_contents($realPath);
    }
}

Input Sanitization

// SECURE: Remove dangerous characters
final class PathSanitizer
{
    public function sanitize(string $input): string
    {
        // Remove null bytes
        $input = str_replace("\0", '', $input);

        // Remove path traversal sequences
        $input = str_replace(['../', '..\\', '..'], '', $input);

        // Remove URL encoding
        $input = urldecode($input);
        $input = str_replace(['../', '..\\', '..'], '', $input);

        // Only allow alphanumeric, dash, underscore, dot
        if (!preg_match('/^[\w\-\.]+$/', $input)) {
            throw new InvalidArgumentException('Invalid characters in path');
        }

        return $input;
    }
}

Severity Classification

Pattern Severity CWE
include/require with user input 🔴 Critical CWE-98
File read with user path 🔴 Critical CWE-22
File write with user path 🔴 Critical CWE-22
Zip extraction without validation 🔴 Critical CWE-22
Upload path manipulation 🟠 Major CWE-22
Missing realpath validation 🟠 Major CWE-22
Symlink following 🟡 Minor CWE-59

Output Format

### Path Traversal: [Description]

**Severity:** 🔴 Critical
**Location:** `file.php:line`
**CWE:** CWE-22 (Path Traversal)

**Issue:**
User input is used in file path without validation, allowing access to arbitrary files.

**Attack Vector:**
1. Input: `../../../etc/passwd`
2. Path becomes: `/uploads/../../../etc/passwd`
3. Resolves to: `/etc/passwd`
4. Attacker reads system files

**Code:**
```php
// Vulnerable
$content = file_get_contents('/uploads/' . $_GET['file']);

Fix:

// Secure: Validate path within allowed directory
$basePath = realpath('/uploads');
$fullPath = realpath('/uploads/' . $_GET['file']);

if ($fullPath === false || !str_starts_with($fullPath, $basePath . '/')) {
    throw new SecurityException('Invalid path');
}

$content = file_get_contents($fullPath);

References: