CVE-2017-15051 (PHP, published 2017-11-27T19:29Z)
Description: value: Multiple stored cross-site scripting (XSS) vulnerabilities in TeamPass before 2.1.27.9 allow authenticated remote attackers to inject arbitrary web script or HTML via the (1) URL value of an item or (2) user log history. To exploit the vulnerability, the attacker must be first authenticated to the application. For the first one, the attacker has to simply inject XSS code within the URL field of a shared item. For the second one however, the attacker must prepare a payload within its profile, and then ask an administrator to modify its profile. From there, whenever the administrator accesses the log, it can be XSS'ed.
Severity: LOW (CVSS v3 base score: 5.4)
Weakness: CWE-79 \u2014 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
Repository: https://github.com/nilsteampassnet/TeamPass
Files changed:
index.php
items.load.php
sources/core.php
sources/items.queries.php
sources/main.functions.php
Commit message: 2.1.27

Several fixes


Vulnerable code:
    require_once $SETTINGS['cpassman_dir'].'/includes/language/'.$session_user_language.'.php';
                    $("#display_categories").val(data.displayCategories);
                        if ($(".tr_fields") != undefined && data.displayCategories != "") {
                        if ($(".tr_fields") != undefined && data.displayCategories != "") {
                                        type     : "move_item",
                                        item_id : ui.draggable.attr("id"),
                                        key        : "<?php echo $_SESSION['key']; ?>"
                    if (data.error == "ERR_JSON_FORMAT") {
                        $("#edit_show_error").html(data.error + ' ERROR (JSON is broken)!!!!!');
                        $("#edit_show_error").show();
                    } else if (data.error == "ERR_KEY_NOT_CORRECT") {
                        $("#edit_show_error").html('Key verification for Query is not correct!');
                        $("#edit_show_error").show();
                    }else if (data.error == "ERR_ENCRYPTION_NOT_CORRECT") {
                        $("#edit_show_error").html('Item password could not be correctly encrypted!');
                        $("#edit_show_error").show();
                    } else if (data.error == "ERR_PWD_TOO_LONG") {
                        $("#edit_show_error").html('<?php echo addslashes($LANG['error_pw_too_long']); ?>');
                        $("#edit_show_error").show();
                    } else if (data.error == "ERR_NOT_ALLOWED_TO_EDIT") {
                        $("#new_show_error").html('User not allowed to edit this Item!');
                        $("#new_show_error").show();
                    } else if (data.error != "") {
                        $("#edit_show_error").html('<?php echo addslashes($LANG['error_not_allowed_to']); ?>');
                        $("#edit_show_error").show();
                    type                : 'show_details_item',
                    id                  : id,
                    folder_id           : $('#hid_cat').val(),
                    salt_key_required   : $('#recherche_group_pf').val(),
                    salt_key_set        : $('#personal_sk_set').val(),
                    expired_item        : expired_item,
                    restricted          : restricted,
                    page                : "items",
                    key                 : "<?php echo $_SESSION['key']; ?>"
                    } else if (data.show_details == "1" && data.show_detail_option == "2") {
                            type    : "update_rep",
        require_once $SETTINGS['cpassman_dir'].'/includes/language/'.$_SESSION['user_language'].'.php';
            $label = noHTML(htmlspecialchars_decode($dataReceived['label']));
            $url = htmlspecialchars_decode($dataReceived['url']);
            $login = htmlspecialchars_decode($dataReceived['login']);
                $label = noHTML(($dataReceived['label']));
                $url = noHTML(htmlspecialchars_decode($dataReceived['url']));
                $login = noHTML(htmlspecialchars_decode($dataReceived['login']));
                $email = noHTML(htmlspecialchars_decode($dataReceived['email']));
                    (@in_array(
                        $post_id,
                        $_SESSION['list_folders_limited'][$post_folder_id]
                    ))
                $returnValues = '[{"error" : "not_allowed"}, {"error_text" : "2'.addslashes($LANG['error_not_allowed_to']).'"}]';
            if (null !== $post_item_id
                && empty($post_item_id) === false
            $rows_tmp = DB::query("SELECT role_id FROM ".prefix_table("restriction_to_roles")." WHERE item_id=%i", $post_id);
            $myTest = 0;
            if (in_array($_SESSION['user_id'], $rows_tmp)) {
                $myTest = 1;
            if (null !== filter_input(INPUT_POST, 'salt_key_required', FILTER_SANITIZE_STRING)
                && filter_input(INPUT_POST, 'salt_key_required', FILTER_SANITIZE_STRING) === '1'
                && null !== filter_input(INPUT_POST, 'salt_key_set', FILTER_SANITIZE_STRING)
                && filter_input(INPUT_POST, 'salt_key_set', FILTER_SANITIZE_STRING) === '1'
            if (null !== filter_input(INPUT_POST, 'expired_item', FILTER_SANITIZE_STRING)
                && filter_input(INPUT_POST, 'expired_item', FILTER_SANITIZE_STRING) === '1'
                    && isset($_SESSION['list_folders_limited'][$post_folder_id])
                    && in_array($post_id, $_SESSION['list_folders_limited'][$post_folder_id]))
                if (null !== filter_input(INPUT_POST, 'restricted', FILTER_SANITIZE_STRING)) {
                    $arrData['restricted'] = filter_input(INPUT_POST, 'restricted', FILTER_SANITIZE_STRING);
                    "has_change_proposal" => DB::count()
        case "update_rep":
            $title = htmlspecialchars_decode($dataReceived['title']);
                $dataReceived['folder']
                    $dataReceived['folder']
                    $dataReceived['folder'],
                $dataReceived['source_folder_id']
                $dataReceived['target_folder_id']
            if ($tree->isChildOf($dataReceived['target_folder_id'], $dataReceived['source_folder_id']) === true) {
                        'parent_id' => $dataReceived['target_folder_id']
                    $dataReceived['source_folder_id']
        /*
          * CASE
          * WANT TO CLIPBOARD PW/LOGIN OF ITEM
        */
        case "get_clipboard_item":
            $dataItem = DB::queryfirstrow(
                "SELECT pw,login,perso FROM ".prefix_table("items")." WHERE id=%i",
                $post_id
            );

            if (filter_input(INPUT_POST, 'field', FILTER_SANITIZE_STRING) === "pw") {
                if ($dataItem['perso'] === '1') {
                    $data = cryption(
                        $dataItem['pw'],
                        $_SESSION['user_settings']['session_psk'],
                        "decrypt"
                    );
                } else {
                    $data = cryption(
                        $dataItem['pw'],
                        "",
                        "decrypt"
                    );
                }
            } else {
                $data = $dataItem['login'];
            }
            // Encrypt data to return
            echo prepareExchangedData($data, "encode");
            break;

                "SELECT name,id_item,file
        $tmp = explode(";", $_SESSION['fonction_id']);

Fixed code:
    if (file_exists($SETTINGS['cpassman_dir'].'/includes/language/'.$session_user_language.'.php') === true) {
        require_once $SETTINGS['cpassman_dir'].'/includes/language/'.$session_user_language.'.php';
    }
                    if (data.displayCategories !== undefined) {
                        $("#display_categories").val(data.displayCategories);
                    }
                        if ($(".tr_fields") !== undefined && data.displayCategories !== undefined && data.displayCategories !== "") {
                        if ($(".tr_fields") != undefined && data.displayCategories !== undefined && data.displayCategories != "") {
                                        type      : "move_item",
                                        item_id   : ui.draggable.attr("id"),
                                        key       : "<?php echo $_SESSION['key']; ?>"
                    if (data.error === "ERR_JSON_FORMAT") {
                        $("#edit_show_error")
                            .html(data.error + ' ERROR (JSON is broken)!!!!!')
                            .show();
                    } else if (data.error === "ERR_KEY_NOT_CORRECT") {
                        $("#edit_show_error")
                            .html('Key verification for Query is not correct!')
                            .show();
                    }else if (data.error === "ERR_ENCRYPTION_NOT_CORRECT") {
                        $("#edit_show_error")
                            .html('Item password could not be correctly encrypted!')
                            .show();
                    } else if (data.error === "ERR_PWD_TOO_LONG") {
                        $("#edit_show_error")
                            .html('<?php echo addslashes($LANG['error_pw_too_long']); ?>')
                            .show();
                    } else if (data.error === "ERR_NOT_ALLOWED_TO_EDIT") {
                        $("#new_show_error")
                            .html('User not allowed to edit this Item!')
                            .show();
                    } else if (data.error !== "") {
                        $("#edit_show_error")
                            .html('<?php echo addslashes($LANG['error_not_allowed_to']); ?>')
                            .show();
            var data = {
                "id" : id,
                "folder_id" : $('#hid_cat').val(),
                "salt_key_required" : $('#recherche_group_pf').val(),
                "salt_key_set" : $('#personal_sk_set').val(),
                "expired_item" : expired_item,
                "restricted" : restricted,
                "page" : "items"
            };

                    type : 'show_details_item',
                    data : prepareExchangedData(JSON.stringify(data), "encode", "<?php echo $_SESSION['key']; ?>"),
                    key  : "<?php echo $_SESSION['key']; ?>"
                    } else if (data.show_details === "1" && data.show_detail_option === "2") {
            if (data.error !== "") {
                $("#div_dialog_message_text").html(data.error_text);
                $("#div_dialog_message").show();
            }

                            type    : "update_folder",
        if (file_exists($SETTINGS['cpassman_dir'].'/includes/language/'.$_SESSION['user_language'].'.php') === true) {
            require_once $SETTINGS['cpassman_dir'].'/includes/language/'.$_SESSION['user_language'].'.php';
        }
            $label = filter_var(htmlspecialchars_decode($dataReceived['label']), FILTER_SANITIZE_STRING);
            $url = filter_var(htmlspecialchars_decode($dataReceived['url']), FILTER_SANITIZE_STRING);
            $login = filter_var(htmlspecialchars_decode($dataReceived['login']), FILTER_SANITIZE_STRING);
                $label = filter_var(($dataReceived['label']), FILTER_SANITIZE_STRING);
                $url = filter_var(htmlspecialchars_decode($dataReceived['url']), FILTER_SANITIZE_STRING);
                $login = filter_var(htmlspecialchars_decode($dataReceived['login']), FILTER_SANITIZE_STRING);
                $email = filter_var(htmlspecialchars_decode($dataReceived['email']), FILTER_SANITIZE_STRING);
                    (null !== $post_folder_id
                    && isset($_SESSION['list_restricted_folders_for_items'][$post_folder_id])
                    && in_array($post_id, $_SESSION['list_restricted_folders_for_items'][$post_folder_id])
                    && $post_restricted === '1'
                    && $restrictionActive === false)
                $returnValues = '[{"error" : "not_allowed"}, {"error_text" : "'.addslashes($LANG['error_not_allowed_to']).'"}]';
            if (empty($post_item_id) === false

                // Check if the folder where this item is, is accessible to the user
                if (in_array($originalRecord['id_tree'], $_SESSION['groupes_visibles'])) {
                    $returnValues = '[{"error" : "not_allowed"}, {"error_text" : "'.addslashes($LANG['error_not_allowed_to']).'"}]';
                    echo $returnValues;
                    break;
                }

                // Load the destination folder record into an array
            // Decrypt and retreive data in JSON format
            $dataReceived = prepareExchangedData($post_data, "decode");
            // Init post variables
            $post_id = filter_var(htmlspecialchars_decode($dataReceived['id']), FILTER_SANITIZE_NUMBER_INT);
            $post_folder_id = filter_var(htmlspecialchars_decode($dataReceived['folder_id']), FILTER_SANITIZE_NUMBER_INT);
            $post_salt_key_required = filter_var(htmlspecialchars_decode($dataReceived['salt_key_required']), FILTER_SANITIZE_STRING);
            $post_salt_key_set = filter_var(htmlspecialchars_decode($dataReceived['salt_key_set']), FILTER_SANITIZE_STRING);
            $post_expired_item = filter_var(htmlspecialchars_decode($dataReceived['expired_item']), FILTER_SANITIZE_STRING);
            $post_restricted = filter_var(htmlspecialchars_decode($dataReceived['restricted']), FILTER_SANITIZE_STRING);
            $post_page = filter_var(htmlspecialchars_decode($dataReceived['page']), FILTER_SANITIZE_STRING);

            $user_in_restricted_list_of_item = false;
                    if ($_SESSION['user_id'] === $record['id']) {
                        $user_in_restricted_list_of_item = true;
                    }


            $rows_tmp = DB::query(
                "SELECT role_id
                FROM ".prefix_table("restriction_to_roles")."
                WHERE item_id=%i",
                $post_id
            );
            foreach ($rows_tmp as $rec_tmp) {
                if (in_array($rec_tmp['role_id'], explode(';', $_SESSION['fonction_id']))) {
                    $restrictionActive = false;
                }
            if (null !== $post_salt_key_required
                && $post_salt_key_required === '1'
                && null !== $post_salt_key_set
                && $post_salt_key_set === '1'
            if (null !== $post_expired_item
                && $post_expired_item === '1'


                    && isset($_SESSION['list_restricted_folders_for_items'][$post_folder_id])
                    && in_array($post_id, $_SESSION['list_restricted_folders_for_items'][$post_folder_id])
                    && $post_restricted === '1'
                    && $user_in_restricted_list_of_item === true)
                ||
                (isset($SETTINGS['restricted_to_roles']) && $SETTINGS['restricted_to_roles'] === '1'
                    && $restrictionActive === false
                )
                if (null !== $post_restricted) {
                    $arrData['restricted'] = $post_restricted;
            // Check if the folder where this item is, is accessible to the user
            if (in_array($dataItem['id_tree'], $_SESSION['groupes_visibles'])) {
                $returnValues = '[{"error" : "not_allowed"}, {"error_text" : "'.addslashes($LANG['error_not_allowed_to']).'"}]';
                echo prepareExchangedData($returnValues, "encode");
                break;
            }

                    "has_change_proposal" => DB::count(),
                    "error" => ""
            // Check that user can access this item
            $granted = accessToItemIsGranted($post_id);
            if ($granted !== true) {
                echo prepareExchangedData(array("error" => $granted), "encode");
                break;
            }

            // Load item data
            $data = DB::queryFirstRow(
                "SELECT id_tree
                FROM ".prefix_table("items")."
                WHERE id = %i",
                $post_id
            );

        case "update_folder":
            $title = filter_var(htmlspecialchars_decode($dataReceived['title']), FILTER_SANITIZE_STRING);
            $post_folder_id = filter_var(htmlspecialchars_decode($dataReceived['folder']), FILTER_SANITIZE_NUMBER_INT);

            // Check if user is allowed to access this folder
            if (!in_array($post_folder_id, $_SESSION['groupes_visibles'])) {
                echo '[{"error" : "'.addslashes($LANG['error_not_allowed_to']).'"}]';
                break;
            }

                $post_folder_id
                    $post_folder_id
                    $post_folder_id,
            $post_source_folder_id = filter_var(htmlspecialchars_decode($dataReceived['source_folder_id']), FILTER_SANITIZE_NUMBER_INT);
            $post_target_folder_id = filter_var(htmlspecialchars_decode($dataReceived['target_folder_id']), FILTER_SANITIZE_NUMBER_INT);

            // Check that user can access this folder
            if (!in_array($post_source_folder_id, $_SESSION['groupes_visibles'])
                || !in_array($post_target_folder_id, $_SESSION['groupes_visibles'])
            ) {
                $returnValues = '[{"error" : "'.addslashes($LANG['error_not_allowed_to']).'"}]';
                echo $returnValues;
                break;
            }
                $post_source_folder_id
                $post_target_folder_id
            if ($tree->isChildOf($post_target_folder_id, $post_source_folder_id) === true) {
                        'parent_id' => $post_target_folder_id
                    $post_source_folder_id
                "SELECT name, id_item, file

            // Load item data
            $data_item = DB::queryFirstRow(
                "SELECT id_tree
                FROM ".prefix_table("items")."
                WHERE id = %i",
                $data['id_item']
            );

            // Check that user can access this folder
            if (!in_array($data_item['id_tree'], $_SESSION['groupes_visibles'])) {
                echo prepareExchangedData(array("error" => "ERR_FOLDER_NOT_ALLOWED"), "encode");
                break;
            }

            // Check that user can access this folder
            if (!in_array($dataSource['id_tree'], $_SESSION['groupes_visibles'])
                || !in_array($post_folder_id, $_SESSION['groupes_visibles'])
            ) {
                echo '[{"error" => "ERR_FOLDER_NOT_ALLOWED"}]';
                break;
            }


                    // Check that user can access this folder
                    if (!in_array($dataSource['id_tree'], $_SESSION['groupes_visibles'])
                        || !in_array($post_folder_id, $_SESSION['groupes_visibles'])
                    ) {
                        echo '[{"error":"not_allowed" , "status":"ok"}]';
                        break;
                    }

                    // Check that user can access this folder
                    if (!in_array($dataSource['id_tree'], $_SESSION['groupes_visibles'])
                        || !in_array($post_folder_id, $_SESSION['groupes_visibles'])
                    ) {
                        echo prepareExchangedData(array("error" => "ERR_FOLDER_NOT_ALLOWED"), "encode");
                        break;
                    }

        $tmp = explode(";", $idFonctions);
        $_SESSION['fonction_id'] = $idFonctions;

/**
 * Check if user can access to this item
 * @param $item_id
 */
function accessToItemIsGranted($item_id)
{
    // Load item data
    $data = DB::queryFirstRow(
        "SELECT id_tree
        FROM ".prefix_table("items")."
        WHERE id = %i",
        $item_id
    );

    // Check if user can access this folder
    if (!in_array($data['id'], $_SESSION['groupes_visibles'])) {
        return "ERR_FOLDER_NOT_ALLOWED";
    }

    return true;
}