1
\$\begingroup\$

I haven't understood how I can perform (and so prevent ) a Directory traversal attack thorugh php.

Probably isn't necessary, but I ask: my script contains a multiple file upload and download, this is the script to upload:

if (isset($setting[5]) && $setting[5] == 1) {
    $msid = $DBH->lastInsertId();
    if (isset($_FILES['filename'])) {
        $count = count($_FILES['filename']['name']);
        if ($count > 0) {
            echo '<script>parent.noty({text: "File Upload Started",type:"information",timeout:2000});</script>';
            if (!is_dir('../upload')) {
                mkdir('../upload');
            }
            $uploadarr = array();
            $movedfiles = array();
            $query = "INSERT INTO " . $SupportUploadTable . " (`name`,`enc`,`uploader`,`num_id`,`ticket_id`,`message_id`,`upload_date`) VALUES ";

            for ($i = 0; $i < $count; $i++) {
                if ($_FILES['filename']['error'][$i] == 0) {
                    if ($_FILES['filename']['size'][$i] <= $maxsize && $_FILES['filename']['size'][$i] != 0 && trim(
                            $_FILES['filename']['name'][$i]
                        ) != ''
                    ) {
                        if (count(array_keys($movedfiles, $_FILES['filename']['name'][$i])) == 0) {
                            $encname = uniqid(hash('sha256', $msid . $_FILES['filename']['name'][$i]), true);
                            $target_path = "../upload/" . $encname;
                            if (move_uploaded_file($_FILES['filename']['tmp_name'][$i], $target_path)) {
                                if (CryptFile("../upload/" . $encname)) {
                                    $movedfiles[] = $_FILES['filename']['name'][$i];
                                    $uploadarr[] = array($encid, $encname, $_FILES['filename']['name'][$i]);
                                    $query .= '("' . $_FILES['filename']['name'][$i] . '","' . $encname . '","' . $_SESSION['id'] . '",' . $tkid . ',"' . $refid . '","' . $msid . '","' . $date . '"),';

                                    echo '<script>parent.noty({text: "' . $_FILES['filename']['name'][$i] . ' has been uploaded",type:"success",timeout:2000});</script>';
                                }
                            }
                        }
                    } else {
                        echo '<script>parent.noty({text: "The file ' . $_FILES['filename']['name'][$i] . ' is too big or null. Max file size: ' . ini_get(
                                'upload_max_filesize'
                            ) . '",type:"error",timeout:9000});</script>';
                    }
                } else {
                    if ($_FILES['filename']['error'][$i] != 4) {
                        echo '<script>parent.noty({text: "File Name:' . $_FILES['filename']['name'][$i] . ' Error Code:' . $_FILES['filename']['error'][$i] . '",type:"error",timeout:9000});</script>';
                    }
                }
            }
            if (isset($uploadarr[0])) {
                $query = substr_replace($query, '', -1);
                try {
                    $STH = $DBH->prepare($query);
                    $STH->execute();

                    $query = "UPDATE " . $SupportMessagesTable . " SET attachment='1' WHERE id=?";
                    $STH = $DBH->prepare($query);
                    $STH->bindParam(1, $msid, PDO::PARAM_INT);
                    $STH->execute();

                } catch (PDOException $e) {
                    file_put_contents('PDOErrors', $e->getMessage() . "\n", FILE_APPEND);
                    echo '<script>parent.$(".main").nimbleLoader("hide");parent.noty({text: "An error has occurred, please contact the administrator.",type:"error",timeout:9000});</script>';
                }
            }
            echo '<script>parent.noty({text: "File Upload Finished",type:"information",timeout:2000});</script>';
        }
    }
}

The download form:

<form class="download_form" enctype="multipart/form-data" target="hidden_upload" action="../php/function.php" method="POST">
    <input type="hidden" value="fcf5a1b982f5ada8440aa07a6ceaac65b0bccfd1cb9f425da0b3d76d71bfb7b1521e440a7ad805.86327361" name="ticket_id">
    <input type="hidden" value="534eabdd15e48e23b570bb3a05a6f5e535952d9e526b39047d9a64fbe50a4789521e440a7fb5d5.01292029" name="file_download">
    <input class="btn btn-link download" type="submit" value="key.txt">
</form>

Next I perform a query to retrieve the information and download the file.
Basically I would like to know where the Directory traversal attack could be performed.

\$\endgroup\$
  • \$\begingroup\$ Do you have a page that lists all the files that can be downloaded? That's how a directory traversal attack is done, by following all the links on a page like that. \$\endgroup\$ – Barmar Aug 28 '13 at 18:48
  • \$\begingroup\$ No, I have got only a folder with access denied through htaccess and that form to download the files \$\endgroup\$ – Dheed Aug 28 '13 at 18:50
1
\$\begingroup\$

It doesn't appear to be vulnerable to Path Traversal as you appear to be creating your own $encname variable to store the file rather than directly using the filename from an untrusted source (i.e. the POST request).

Path Traversal usually happens if the user can manipulate a parameter to get to a file they normally can't access.

e.g. if your normal URL is www.example.com/get_file.php?file=readme.txt, the user might be able to change the URL to www.example.com/get_file.php?file=../private.txt in order to read a file in the directory above.

However, I would check your code for XSS and SQL Injection as it appears to be vulnerable.

For example, the output of $_FILES['filename']['name'][$i] in your code echo '<script>parent.noty({text: "' . $_FILES['filename']['name'][$i] . ' has been uploaded",type:"success",timeout:2000});</script>; } should be JS escaped, otherwise it could be possible for an attacker to insert JavaScript into your page by uploading with a spoofed filename containing <script> tags.

Your INSERT query should also use prepare($query); rather than manually building the query with string concatenation. This will stop an attacker changing the query in any way by again using a spoofed filename to break out of the SQL statement.

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ Thanks! About XSS, is it sufficent to invert single and double quote and use the htmlentities php function? About the second point: substitute the name with a ?, insert the name inside an array and bindParams the array, is it the correct way? \$\endgroup\$ – Razorphyn Aug 29 '13 at 10:47
  • \$\begingroup\$ XSS - no, I would do it properly or there might be some way round it that you haven't thought of (Except for alphanumeric characters, escape all characters less than 256 with the \xHH format). Yes, that sounds correct to prevent SQL injection. \$\endgroup\$ – SilverlightFox Aug 29 '13 at 11:03
  • \$\begingroup\$ I'm sorry, but I'm an Amateur, could you explain it in an easier way? \$\endgroup\$ – Razorphyn Aug 29 '13 at 11:20
  • 1
    \$\begingroup\$ stackoverflow.com/questions/168214/… - I'm not a PHP man myself so I can't vouch that that will encode everything as described. \$\endgroup\$ – SilverlightFox Aug 29 '13 at 11:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.