SmilerRyan
SmilerRyan

Reputation: 23

How do I Detect if a file is outside of a directory tree?

I've created a basic page where people "sign up" to upload/delete files in their own isolated folder. The uploading/deleting of files are secure, however I would also like to allow admins to "deny" files.

The code alone works, except if anyone changes the URL to somewhere else on the server, they can "deny" any file on the system putting it at risk. I am looking to create a system function of which detects if the file they are targeting exists anywhere in the directory tree.

Here's the code that I am using of which I'd like to create a function that returns either true/false.

<?php
if(isset($_GET['deny'])) {
    $tree_start = "Uploads";
    $targeted_file = $_GET['deny'];
    $safe_to_delete = in_directory_tree($tree_start, $targeted_file); <-- Looking for this
    if( $safe_to_delete == false ) {die("This file does not exist in the directory tree");}
    rename($_GET['deny'], "./Uploads/@Denied/". basename($_GET['deny']) );
}
?>


My Directory tree:

.htaccess <-- Prevent downloading of the database
admin.php <-- Problematic file browser script
index.php <-- User File management script
Users.db  <-- Names and hashed passwords

Uploads: 
  [FILE] htaccess <-- Prevent script execution (layer 2).
  [DIR] @Accepted: Notes.png, Video.mp4, etc...
  [DIR] @Denied: Project.png, new_timetable.txt, etc...
  [DIR] Admin: Proj1.txt, Proj1.png, etc...
  [DIR] User1: Task1.txt, Task2.txt, etc...
  [DIR] User2: Video1.txt, date.txt, etc...

Upvotes: 1

Views: 214

Answers (2)

SmilerRyan
SmilerRyan

Reputation: 23

I have solved this problem by preventing path traversal instead of checking if the file exists in this folder. This code works for me (returns true only when a file in /uploads exists (and blocks going back using C:/, ../, etc), and returns true only when the file does exist. Here is the finished code:

<?php

// Code used for the deny button:
// <button onclick="location.href='?deny=SmilerRyan/Project.png';">Deny</button>

if(isset($_GET['deny'])) {

$userpath = $_GET['deny'];
$basepath = 'Uploads/';
$realBase = realpath($basepath);
$userpath = $basepath . $userpath;
$realUserPath = realpath($userpath);
if ($realUserPath === false || strpos($realUserPath, $realBase) !== 0) {
    die("Invalid path - Possible Attack Blocked");
} else {
    rename($userpath, "./Uploads/@Denied/" . basename($userpath) );
}

}
?>

Upvotes: 0

Ray A
Ray A

Reputation: 1351

give this code a try:

    function in_directory_tree($dir,$file_to_search){

    $filesList = new RecursiveDirectoryIterator("uploads");
    $targetFile = "contact.php" ;
    foreach(new RecursiveIteratorIterator($filesList) as $file)
    {
        $contents = explode("\\",$file);
        if (in_array($targetFile, $contents))
            return true;
    }
    return false;
}

This code will load the directory and start searching recursively, if it reaches the end without finding the file it will return false, otherwise it will return true. I used RecursiveDirectoryIterator as it will help us get inside directories to list them

Upvotes: 1

Related Questions