0

I'm writing some server side scripts for my web host that will do a whole bunch of auto updating for client side applications. As long as the user has properly identified themselves as the owner with the app name and password they should be able to upload to a repository on the server created automatically; however, I don't want to allow any scripts to be uploaded such that it could run on the server. I intend to allow people to create their own repositories without having to ask me so is there anyway to block this potential vulnerability?

This is the setup code for the repositories, named APPSEtUP.php :

<?php
$app = str_replace("_", " ", TDecode($_POST['app']));
$pass = str_replace("_", " ", TDecode($_POST['pass']));
$command = str_replace("_", " ", TDecode($_POST['command']));
$worked = false;
if ($command == "SETUP_API") { $worked = SETUP_API($app, $pass); }
if ($command == "MAKE_DIR") { $worked = Make_Directory($app, $pass, TDecode($_POST['DIR']), TDecode($_POST['up'])); }
if ($worked) { echo "SUCCESS!"; }
return;

function Make_Directory($api, $pw, $dir, $up) {
    $path = $_SERVER['REQUEST_URI'];
    if ($path == "/scripts/APPSETUP.php") { echo "API FAILURE: 008\r\n"; return false; }
    if (!startsWith($path, "/scripts/Apps/")) { echo "API FAILURE: 009\r\n"; return false; }
    if (!Get_API_PW("./security.LOCK", $pass)) { echo "API FAILURE: 010\r\n"; return false; }
    if ($path != "/scripts/Apps/".$api."/APPSETUP.php") { echo "API FAILURE: 011\r\n"; return false; }
    while (startsWith($dir, ".") || startsWith($dir, "/")) { $dir = substr($dir, -(strlen($dir)-1)); }
    while (endsWith($dir, "/")) { $dir = substr($dir, 0, strlen($dir)-1); }
    if (!(file_exists("./".$dir."/") || mkdir("./".$dir."/", "0777", true))) { echo "API FAILURE: 012\r\n"; return false; }
    if ($up == "true" && !(file_exists("./".$dir."/UploadFile.php") || copy("./UploadFile.php", "./".$dir."/UploadFile.php"))) {
        echo "API FAILURE: 013\r\n"; return false;
    } return true;
}

function startsWith($haystack, $needle) {
    $length = strlen($needle);
    return (substr($haystack, 0, $length) === $needle);
}

function endsWith($haystack, $needle) {
    $length = strlen($needle);
    return $length === 0 || (substr($haystack, -$length) === $needle);
}

function SETUP_API($api, $pw) {
    $temp1 = "./Templates/USERLOG.php";
    $temp2 = "./Templates/UploadFile.php";
    $temp3 = "./APPSETUP.php";
    $dest1 = "./Apps/";
    $dest2 = "./Apps/".$api."/";
    $dest3 = "./Apps/".$api."/USERLOG.php";
    $dest4 = "./Apps/".$api."/security.LOCK";
    $dest5 = "./Apps/".$api."/UploadFIle.php";
    $dest6 = "./Apps/".$api."/APPSETUP.php";
    if (!(file_exists($dest1) || mkdir($dest1, 0777, true))) { echo "API FAILURE: 001\r\n"; return false; }
    if (!(file_exists($dest2) || mkdir($dest2, 0777, true))) { echo "API FAILURE: 002\r\n"; return false; }
    if (!file_exists($dest4)) { if (!App_Reset($dest2, $dest4, $pw)) { echo "API FAILURE: 003\r\n"; return false; } } 
    if (!Get_API_PW($dest4, $pw)) { echo "API FAILURE: 004\r\n"; return false; }
    if (!copy($temp1, $dest3)) { echo "API FAILURE: 005\r\n"; return false; }
    if (!copy($temp2, $dest5)) { echo "API FAILURE: 006\r\n"; return false; }
    if (!copy($temp3, $dest6)) { echo "API FAILURE: 007\r\n"; return false; }
    return true;
}

function App_Reset($api, $sec, $pw) {
    try {
        Delete_Bad_App($api);
        $pWriter = fopen($sec, "w");
        fwrite($pWriter, TEncode($pw));
        fclose($pWriter);
        return true;
    } catch (exception $e) { return false; }
}

function Delete_Bad_App($api) {
    $di = new RecursiveDirectoryIterator($api, FilesystemIterator::SKIP_DOTS);
    $ri = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::CHILD_FIRST);
    foreach ( $ri as $file ) {
        $file->isDir() ?  rmdir($file) : unlink($file);
    } return;
}

function Get_API_PW($sec, $guess) {
    try {
        $pReader = fopen($sec, "r");
        $pw = TDecode(fread($pReader, filesize($sec)));
        fclose($pReader);
        return $pw == $guess;
    } catch (exception $e) { return false; }
}

function TriceInt($c) {
    $b = unpack("C*", $c)[1] % 255;
    $foo = (string)$b;
    while (strlen($foo) < 3) { $foo = "0".$foo; }
    return $foo;
}

function TEncode($str) {
    if (TEncoded($str)) { return $str; }
    return implode(array_map("TriceInt", str_split($str, 1)));
}

function TDecode($str) {
    if (!TEncoded($str)) { return $str; }
    return implode(array_map("chr", array_map('intval', str_split($str, 3))));
}

function TEncoded($str) {
    return (ctype_digit($str) && strlen($str) % 3 == 0);
}
?>

and here is the script that is for uploading files.

<?php
$uploads_dir = './'; 
if ($_FILES["file"]["error"] == UPLOAD_ERR_OK) {
    $tmp_name = $_FILES["file"]["tmp_name"];
    $name = $_FILES["file"]["name"];
    move_uploaded_file($tmp_name, "$uploads_dir/$name");
}
?>

Do note that the upload script is named UploadFile.php and is located in a templates folder as referenced in the setup script.

Naate
  • 115
  • 10
  • 3
    What if this code doesn't detect that it is a script? It's simpler to disable the execution of scripts of files in the uploaded files directory. – A.L May 28 '18 at 12:42
  • @A.L Would you describe how to do that? I wasn't aware that was possible. – Naate May 28 '18 at 12:44
  • 2
    For example, move uploaded files in a directory that is outside the web directory of your server. – A.L May 28 '18 at 12:52
  • Unfortunately that isn't an option, I'm using a shared hosting plan. – Naate May 28 '18 at 12:54
  • Can you use a `.htaccess` file? https://stackoverflow.com/questions/1271899/disable-php-in-directory-including-all-sub-directories-with-htaccess – A.L May 28 '18 at 13:53
  • Can you store the files in a database as a blob instead of writing to filesystem? – ivanivan May 28 '18 at 14:50
  • @A.L so i just spent all the last 3 days trying to figure out .htaccess and apparently godaddy blocks that and requires me to use web.config instead. figured it out tho :) – Naate May 31 '18 at 14:57

1 Answers1

1

So following the suggestions in the comments of the question I tried disabling script execution with .htaccess but because I'm using a Windows Server hosted by godaddy I was using the wrong kind of file to do so. I found this link which explained how to do the same thing in web.config which does apply to Windows servers.

http://issues.umbraco.org/issue/U4-8472

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <handlers>
            <clear />
            <add name="StaticFile" path="*" verb="*" modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule" resourceType="Either" requireAccess="Read" />
        </handlers>
    </system.webServer>
</configuration>
Naate
  • 115
  • 10