CVE-2017-15054 (PHP, published 2017-11-27T19:29Z)
Description: value: An arbitrary file upload vulnerability, present in TeamPass before 2.1.27.9, allows remote authenticated users to upload arbitrary files leading to Remote Command Execution. To exploit this vulnerability, an authenticated attacker has to tamper with parameters of a request to upload.files.php, in order to select the correct branch and be able to upload any arbitrary file. From there, it can simply access the file to execute code on the server.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-434 \u2014 Unrestricted Upload of File with Dangerous Type
The product allows the upload or transfer of dangerous file types that are automatically processed within its environment.
Repository: https://github.com/nilsteampassnet/TeamPass
Files changed:
admin.settings.load.php
admin.settings.php
api/functions.php
changelog.md
items.import.php
items.load.php
items.php
profile.php
sources/admin.queries.php
sources/import.queries.php
sources/items.queries.php
sources/main.functions.php
sources/upload/upload.files.php
Commit message: 2.1.27

Improved security regarding uploading files
Fixed issue while restoring DB from administration page


Vulnerable code:
        $("#restore_bck_encryption_key_dialog_error").html("").hide();
                        $("#restore_bck_encryption_key_dialog_error").html(data[0].message).show();
                    "PHPSESSID":"'.$_SESSION['user_id'].'",
                $.each(files, function(i, file) {
                    $("#restore_bck_fileObj").val(file.name);
                    $("#restore_bck_encryption_key_dialog").dialog("open");
                });
    <div class=ui-state-error ui-corner-all" style="display:none;padding:5px;display:none;margin:2px;" id="restore_bck_encryption_key_dialog_error"></div>
                            $category_query .= "select id from ".prefix_table("nested_tree")." where title LIKE '".$array_category[$slot]."' AND parent_id = 0";
                            $category_query .= "select id from ".prefix_table("nested_tree")." where title LIKE '".$array_category[$slot]."' AND parent_id = (";
                    $category_query = "select id from ".prefix_table("nested_tree")." where title LIKE '".$array_category[0]."' AND parent_id = 0";
                            $category_query .= "select id from ".prefix_table("nested_tree")." where title LIKE '".$array_category[$slot]."' AND parent_id = 0";
                            $category_query .= "select id from ".prefix_table("nested_tree")." where title LIKE '".$array_category[$slot]."' AND parent_id = (";
                    $category_query = "select id from ".prefix_table("nested_tree")." where title LIKE '".$array_category[0]."' AND parent_id = 0";
                $response = DB::delete(prefix_table("items"), "id_tree = (".$category_query.") and label LIKE '".$item."'");
                $response = DB::query("SELECT fonction_id FROM ".prefix_table("users")." WHERE login='".$username."'");
                    $response = DB::query("SELECT folder_id FROM ".prefix_table("roles_values")." WHERE role_id='".$role."'");
                $response = DB::query("SELECT fonction_id FROM ".prefix_table("users")." WHERE login='".$username."'");
                    $response = DB::query("SELECT folder_id, type FROM ".prefix_table("roles_values")." WHERE role_id='".$role."'");
                            $response2 = DB::queryFirstRow("SELECT title, nlevel FROM ".prefix_table("nested_tree")." WHERE id='".$folder_id."'");
                        DB::query("SELECT * FROM ".prefix_table("items")." WHERE label = %s AND inactif = %i", addslashes($item_label), "0");
                    "SELECT id, fonction_id, groupes_interdits, groupes_visibles FROM ".prefix_table("users")."
            WHERE login LIKE %ss",
                            "SELECT `valeur` FROM ".prefix_table("misc")." WHERE type = %s AND intitule = %s",
                                "SELECT `id` FROM ".prefix_table("roles_title")." WHERE title = %s",
                            DB::query("SELECT * FROM ".prefix_table("nested_tree")." WHERE title = %s", $params[0]);
                        DB::query("SELECT * FROM ".prefix_table("nested_tree")." WHERE id = %i", $params[3]);
                            "SELECT * FROM ".prefix_table("items")." WHERE id = %i",
                                            "SELECT * FROM ".prefix_table("tags")." WHERE tag = %s AND item_id = %i",
                        $data_folder = DB::queryfirstrow("SELECT * FROM ".prefix_table("nested_tree")." WHERE id = %s", $GLOBALS['request'][2]);
                        "SELECT `id`, `pw`, `groupes_interdits`, `groupes_visibles`, `fonction_id` FROM ".$pre."users WHERE login = %s",
                        "SELECT `id`, `pw`, `groupes_interdits`, `groupes_visibles`, `fonction_id`, `encrypted_psk` FROM ".$pre."users WHERE login = %s",
                        "SELECT `id`, `pw`, `groupes_interdits`, `groupes_visibles`, `fonction_id` FROM ".$pre."users WHERE login = %s",
                        "SELECT `id`, `pw`, `groupes_interdits`, `groupes_visibles`, `fonction_id`, `encrypted_psk` FROM ".$pre."users WHERE login = %s",
                    $.each(files, function(i, file) {
                        ImportCSV(csv_filename);
                        up.splice();    // clear the file queue
                    });
            var myData;
            try {
                myData = eval(object.response);
            } catch(err) {
                myData = eval('(' + object.response + ')');
            }
            csv_filename = myData.newfilename;
            var myData;
            try {
                myData = eval(object.response);
            } catch(err) {
                myData = eval('(' + object.response + ')');
            }
            kp_filename = myData.newfilename;
                $(this).delay(2000).queue(function() {
                });
                $("#item_upload_wait").show();
                $("#item_edit_upload_wait").show();
                <div id="item_upload_wait" class="ui-state-focus ui-corner-all" style="display:none;padding:2px;margin:5px 0 5px 0;">'.$LANG['please_wait'].'...</div>
                <div id="item_edit_upload_wait" class="ui-state-focus ui-corner-all" style="display:none;padding:2px;margin:5px 0 5px 0;">'.$LANG['please_wait'].'...</div>
                    "newFileName":"user<?php echo $_SESSION['user_id']; ?>"+tmp,
        var myData;
        try {
            myData = eval(object.response);
        } catch(err) {
            myData = eval('(' + object.response + ')');
        }


 </script>
        echo '[{"result":"db_restore"}]';
        echo '[{"result":"db_optimize"}]';
        $file = $SETTINGS['path_to_files_folder']."/".filter_input(INPUT_POST, 'file', FILTER_SANITIZE_STRING);
        if (file_exists("'".$SETTINGS['path_to_files_folder']."/".filter_input(INPUT_POST, 'file', FILTER_SANITIZE_STRING))."'") {
                $SETTINGS['path_to_files_folder']."/".filter_input(INPUT_POST, 'file', FILTER_SANITIZE_STRING)
            // Update CACHE table
            updateCacheTable("add_value", $newID);
    $aes = new SplClassLoader('Encryption\Crypt', '../includes/libraries');
    $pwgen = new SplClassLoader('Encryption\PwGen', '../includes/libraries');
                ),
    $newFileName = time()."_".$_SESSION['user_id'];
        $targetDir.DIRECTORY_SEPARATOR.$newFileName
    $newFileName = time()."_".$_SESSION['user_id'];
        $targetDir.DIRECTORY_SEPARATOR.$newFileName
    // sanitize the new file name
    $newFileName = preg_replace('/[^\w\._]+/', '_', htmlentities($post_newFileName, ENT_QUOTES));
    $newFileName = preg_replace('/[^'.$valid_chars_regex.'\.]/', '', strtolower(basename($newFileName)));

    //Connect to mysql server
    require_once '../../includes/config/settings.php';
    require_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
    $pass = defuse_return_decrypted($pass);
DB::$host = $server;
    DB::$user = $user;
    DB::$password = $pass;
    DB::$dbName = $database;
    DB::$port = $port;
    DB::$encoding = $encoding;
    DB::$error_handler = true;
    $link = mysqli_connect($server, $user, $pass, $database, $port);
    $link->set_charset($encoding);

    echo '{"filename" : "'.htmlentities($_SESSION['user_avatar'], ENT_QUOTES).'" , "filename_thumb" : "'.htmlentities($_SESSION['user_avatar_thumb'], ENT_QUOTES).'"}';
} else {
    $newFileName = time()."_".$_SESSION['user_id'];
        $targetDir.DIRECTORY_SEPARATOR.$newFileName
// Return JSON-RPC response
die('{"jsonrpc" : "2.0", "result" : null, "id" : "id" , "newfilename" : "'.$newFileName.'"}');

Fixed code:
        $("#restore_bck_encryption_key_dialog_error")
            .html("<span class='fa fa-cog fa-spin fa'>&nbsp;</span><?php echo addslashes($LANG['please_wait']); ?>")
            .attr("class","ui-corner-all ui-state-focus")
            .show();

                        $("#restore_bck_encryption_key_dialog_error")
                            .html(data[0].message)
                            .attr("class","ui-corner-all ui-state-error")
                            .show();
    var restore_operation_id = '';
                    "PHPSESSID":"<?php echo $_SESSION['user_id']; ?>",
                $("#restore_bck_fileObj").val(restore_operation_id);
                $("#restore_bck_encryption_key_dialog").dialog("open");
    uploader_restoreDB.bind('FileUploaded', function(upldr, file, object) {
        var myData = prepareExchangedData(object.response, "decode", "<?php echo $_SESSION['key']; ?>");

        restore_operation_id = myData.operation_id;
    });
    <div class="ui-state-error ui-corner-all" style="display:none;padding:5px;display:none;margin:2px;" id="restore_bck_encryption_key_dialog_error"></div>
                            $category_query .= "select id from ".prefix_table("nested_tree")." where title LIKE '".filter_var($array_category[$slot], FILTER_SANITIZE_STRING)."' AND parent_id = 0";
                            $category_query .= "select id from ".prefix_table("nested_tree")." where title LIKE '".filter_var($array_category[$slot], FILTER_SANITIZE_STRING)."' AND parent_id = (";
                    $category_query = "select id from ".prefix_table("nested_tree")." where title LIKE '".filter_var($array_category[0], FILTER_SANITIZE_STRING)."' AND parent_id = 0";
                            $category_query .= "select id from ".prefix_table("nested_tree")." where title LIKE '".filter_var($array_category[$slot], FILTER_SANITIZE_STRING)."' AND parent_id = 0";
                            $category_query .= "select id from ".prefix_table("nested_tree")." where title LIKE '".filter_var($array_category[$slot], FILTER_SANITIZE_STRING)."' AND parent_id = (";
                    $category_query = "select id from ".prefix_table("nested_tree")." where title LIKE '".filter_var($array_category[0], FILTER_SANITIZE_STRING)."' AND parent_id = 0";
                $response = DB::delete(prefix_table("items"), "id_tree = (".$category_query.") and label LIKE '".filter_var($item, FILTER_SANITIZE_STRING)."'");
                $response = DB::query(
                    "SELECT fonction_id
                    FROM ".prefix_table("users")."
                    WHERE login = %s'",
                    $username
                );
                    $response = DB::query(
                        "SELECT folder_id
                        FROM ".prefix_table("roles_values")."
                        WHERE role_id = %i",
                        $role
                    );
                $response = DB::query(
                    "SELECT fonction_id
                    FROM ".prefix_table("users")."
                    WHERE login = %s",
                    $username
                );
                    $response = DB::query(
                        "SELECT folder_id, type
                        FROM ".prefix_table("roles_values")."
                        WHERE role_id = %i",
                        $role
                    );
                            $response2 = DB::queryFirstRow(
                                "SELECT title, nlevel
                                FROM ".prefix_table("nested_tree")."
                                WHERE id = %i",
                                $folder_id
                            );
                        DB::query(
                            "SELECT *
                            FROM ".prefix_table("items")."
                            WHERE label = %s AND inactif = %i",
                            addslashes($item_label),
                            "0"
                        );
                    "SELECT id, fonction_id, groupes_interdits, groupes_visibles
                    FROM ".prefix_table("users")."
                    WHERE login LIKE %ss",
                            "SELECT `valeur`
                            FROM ".prefix_table("misc")."
                            WHERE type = %s AND intitule = %s",
                                "SELECT `id`
                                FROM ".prefix_table("roles_title")."
                                WHERE title = %s",
                            DB::query(
                                "SELECT *
                                FROM ".prefix_table("nested_tree")."
                                WHERE title = %s",
                                $params[0]
                            );
                        DB::query(
                            "SELECT *
                            FROM ".prefix_table("nested_tree")."
                            WHERE id = %i",
                            $params[3]
                        );
                            "SELECT *
                            FROM ".prefix_table("items")."
                            WHERE id = %i",
                                            "SELECT *
                                            FROM ".prefix_table("tags")."
                                            WHERE tag = %s AND item_id = %i",
                        $data_folder = DB::queryfirstrow(
                            "SELECT *
                            FROM ".prefix_table("nested_tree")."
                            WHERE id = %s",
                            $GLOBALS['request'][2]
                        );
                        "SELECT `id`, `pw`, `groupes_interdits`, `groupes_visibles`, `fonction_id`
                        FROM ".$pre."users
                        WHERE login = %s",
                        "SELECT `id`, `pw`, `groupes_interdits`, `groupes_visibles`, `fonction_id`, `encrypted_psk`
                        FROM ".$pre."users
                        WHERE login = %s",
                        "SELECT `id`, `pw`, `groupes_interdits`, `groupes_visibles`, `fonction_id`
                        FROM ".$pre."users
                        WHERE login = %s",
                        "SELECT `id`, `pw`, `groupes_interdits`, `groupes_visibles`, `fonction_id`, `encrypted_psk`
                        FROM ".$pre."users
                        WHERE login = %s",
 Improved security regarding uploading files
 Fixed issue while restoring DB from administration page
                    ImportCSV(csv_filename);
                    up.splice();    // clear the file queue
            var myData = prepareExchangedData(object.response, "decode", "<?php echo $_SESSION['key']; ?>");

            csv_filename = myData.operation_id;
            var myData = prepareExchangedData(object.response, "decode", "<?php echo $_SESSION['key']; ?>");

            kp_filename = myData.operation_id;
                /*$(this).delay(2000).queue(function() {
                });*/
                $("#item_upload_wait").removeClass("hidden");
                $("#item_edit_upload_wait").removeClass("hidden");
                <div id="item_upload_wait" class="ui-state-focus ui-corner-all hidden" style="padding:2px;margin:5px 0 5px 0;">'.$LANG['please_wait'].'...</div>
                <div id="item_edit_upload_wait" class="ui-state-focus ui-corner-all hidden" style="padding:2px;margin:5px 0 5px 0;">'.$LANG['please_wait'].'...</div>
<script type="text/javascript" src="includes/js/functions.js"></script>
        // Decode returned data
        var myData = prepareExchangedData(object.response, "decode", "<?php echo $_SESSION['key']; ?>");

        // update form
</script>
        // Get filename from database
        $data = DB::queryFirstRow(
            "SELECT valeur
            FROM ".$pre."misc
            WHERE increment_id = %i",
            $file
        );

        $file = $data['valeur'];

        // Delete operation id
        DB::delete(
            prefix_table('misc'),
            "increment_id = %i",
            $file
        );

        echo '[{"result":"db_restore" , "message":""}]';
        echo '[{"result":"db_optimize" , "message":""}]';
       // Init post variable
        $post_operation_id = filter_input(INPUT_POST, 'file', FILTER_SANITIZE_NUMBER_INT);

        // Get filename from database
        $data = DB::queryFirstRow(
            "SELECT valeur
            FROM ".$pre."misc
            WHERE increment_id = %i",
            $post_operation_id
        );

        // Delete operation id
        DB::delete(
            prefix_table('misc'),
            "increment_id = %i",
            $post_operation_id
        );
        $file = $SETTINGS['path_to_files_folder']."/".$data['valeur'];

        // Init post variable
        $post_operation_id = filter_input(INPUT_POST, 'file', FILTER_SANITIZE_STRING);

        // Get filename from database
        $data = DB::queryFirstRow(
            "SELECT valeur
            FROM ".$pre."misc
            WHERE increment_id = %i",
            $post_operation_id
        );

        // Delete operation id
        DB::delete(
            prefix_table('misc'),
            "increment_id = %i",
            $post_operation_id
        );

        // do some initializations
        $file = $data['valeur'];

        if (file_exists($SETTINGS['path_to_files_folder']."/".$file)) {
                $SETTINGS['path_to_files_folder']."/".$file
            // Add item to CACHE table if new item has been created
            if (isset($newID) === true) {
                updateCacheTable("add_value", $newID);
            }


    $aes = new SplClassLoader('Encryption\Crypt', $SETTINGS['cpassman_dir'].'/includes/libraries');
    global $SETTINGS;
    require_once $SETTINGS['cpassman_dir'].'/sources/SplClassLoader.php';

    $pwgen = new SplClassLoader('Encryption\PwGen', $SETTINGS['cpassman_dir'].'/includes/libraries');
            ),
// generate file name
$newFileName = GenerateCryptKey(40, false, true, true, false, false);

//Connect to mysql server
require_once '../../includes/config/settings.php';
require_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
$pass = defuse_return_decrypted($pass);
DB::$host = $server;
DB::$user = $user;
DB::$password = $pass;
DB::$dbName = $database;
DB::$port = $port;
DB::$encoding = $encoding;
DB::$error_handler = true;
$link = mysqli_connect($server, $user, $pass, $database, $port);
$link->set_charset($encoding);
        $targetDir.DIRECTORY_SEPARATOR.$newFileName.".".$ext
    );

    // Add in DB
    DB::insert(
        prefix_table("misc"),
        array(
            'type' => "temp_file",
            'intitule' => "filename",
            'valeur' => $newFileName.".".$ext
        )

    // return info
    echo prepareExchangedData(
        array(
            "operation_id" => DB::insertId()
        ),
        "encode"
    );

    exit();
        $targetDir.DIRECTORY_SEPARATOR.$newFileName.".".$ext
    );

    // Add in DB
    DB::insert(
        prefix_table("misc"),
        array(
            'type' => "temp_file",
            'intitule' => "filename",
            'valeur' => $newFileName.".".$ext
        )

    // return info
    echo prepareExchangedData(
        array(
            "operation_id" => DB::insertId()
        ),
        "encode"
    );

    exit();
    // return info
    echo prepareExchangedData(
        array(
            "filename" => htmlentities($_SESSION['user_avatar'], ENT_QUOTES),
            "filename_thumb" => htmlentities($_SESSION['user_avatar_thumb'], ENT_QUOTES)
        ),
        "encode"
    );

} elseif (null !== ($post_type_upload)
    && $post_type_upload === "restore_db"
) {
        $targetDir.DIRECTORY_SEPARATOR.$newFileName.".".$ext
    );

    // Add in DB
    DB::insert(
        prefix_table("misc"),
        array(
            'type' => "temp_file",
            'intitule' => "filename",
            'valeur' => $newFileName.".".$ext
        )

    // return info
    echo prepareExchangedData(
        array(
            "operation_id" => DB::insertId()
        ),
        "encode"
    );

    exit();