CVE-2014-3774 (PHP, published 2014-08-07T11:13Z)
Description: value: Multiple cross-site scripting (XSS) vulnerabilities in items.php in TeamPass before 2.1.20 allow remote attackers to inject arbitrary web script or HTML via the group parameter, which is not properly handled in a (1) hid_cat or (2) open_folder form element, or (3) id parameter, which is not properly handled in the open_id form element.
Severity: MEDIUM (CVSS v3 base score: )
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:
changelog.md
includes/include.php
includes/language/english.php
index.php
items.load.php
items.php
otv.php
sources/find.queries.php
sources/items.queries.php
Commit message: Several security improvements
New feature: one time item view for anonymous user


Vulnerable code:
 * @version       2.1.19
    /*$dataLanguage =
        $db->fetchRow(
            "SELECT valeur FROM ".$pre."misc
            WHERE type = 'admin' AND intitule = 'default_language'"
        );*/
require_once $_SESSION['settings']['cpassman_dir'].'/includes/language/'.$_SESSION['user_language'].'.php';
if (isset($_GET['page']) && $_GET['page'] == "kb") {
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/language/'.$_SESSION['user_language'].'_kb.php';
// Load CORE
require_once $_SESSION['settings']['cpassman_dir'].'/sources/core.php';
echo '
        </div>
            <button title="'.$txt['share'].'" id="menu_button_share" class="" onclick="OpenDialog(\'div_item_share\', \'false\')"><img src="includes/images/share.png" id="div_share" alt="" /></button>';
    if (isset($_GET['session']) && !empty($_GET['session'])) {
        include 'otv.php?session='.$_GET['session'];
    } else {
        $('#menu_button_edit_item,#menu_button_del_item,#menu_button_add_fav,#menu_button_del_fav,#menu_button_show_pw,#menu_button_copy_pw,#menu_button_copy_login,#menu_button_copy_link,#menu_button_copy_item,#menu_button_notify,#menu_button_history,#menu_button_share').attr('disabled', 'disabled');
                    $("#menu_button_copy_item, #menu_button_add_group, #menu_button_edit_group, #menu_button_del_group, #menu_button_add_item, #menu_button_edit_item, #menu_button_del_item, #menu_button_history, #menu_button_share").attr('disabled', 'disabled');
            		        $("#menu_button_share").attr('disabled', 'disabled');
            		    	$("#menu_button_share").prop("disabled", false);
    !isset($_SESSION['CPM']) || $_SESSION['CPM'] != 1 || 
    !isset($_SESSION['user_id']) || empty($_SESSION['user_id']) || 
    !isset($_SESSION['key']) || empty($_SESSION['key'])) 
<input type="hidden" name="hid_cat" id="hid_cat" value="', isset($_GET['group']) ? $_GET['group'] : "", '" />
<input type="hidden" name="input_list_roles" id="input_list_roles" value="'.htmlentities($listRoles, ENT_QUOTES, 'UTF-8').'" />
<input type="hidden" id="nb_items_to_display_once" value="', isset($_SESSION['settings']['nb_items_by_query']) ? $_SESSION['settings']['nb_items_by_query'] : 'auto', '" />
<input type="hidden" name="jstree_group_selected" id="jstree_group_selected" value="'.$firstGroup.'" />';
if (isset($_GET['iSortCol_0'])) {
//echo $sOrder;
$allowedTags = '<b><i><sup><sub><em><strong><u><br><br /><a><strike><ul><blockquote><blockquote><img><li><h1><h2><h3><h4><h5><ol><small><font>';


Fixed code:
 New: one time view page for anonymous user
$k['otv_expiration_period'] = 604800; // 1 week
$k['allowedTags'] = "<b><i><sup><sub><em><strong><u><br><br /><a><strike><ul><blockquote><blockquote><img><li><h1><h2><h3><h4><h5><ol><small><font>";
@define('ERR_VALID_SESSION', "1004");
$txt['one_time_item_view'] = "One time view link";
$txt['one_time_view_item_url_box'] = "Share the One-Time URL with a person of Trust <br><br>#URL#<br><br>Remember that this link will only be visible one time until the #DAY#";
 * @version       2.1.20
// Load CORE
require_once $_SESSION['settings']['cpassman_dir'].'/sources/core.php';
if (in_array($_SESSION['user_language'], $languagesList)) {
	require_once $_SESSION['settings']['cpassman_dir'].'/includes/language/'.$_SESSION['user_language'].'.php';
	if (isset($_GET['page']) && $_GET['page'] == "kb") {
		require_once $_SESSION['settings']['cpassman_dir'].'/includes/language/'.$_SESSION['user_language'].'_kb.php';
	}
} else {
	$_SESSION['error']['code'] = ERR_NOT_ALLOWED; //not allowed page
	include 'error.php';

if (!isset($_GET['otv'])) {
	echo '
        </div>';
}
echo '
            <button title="'.$txt['share'].'" id="menu_button_share" class="" onclick="OpenDialog(\'div_item_share\', \'false\')"><img src="includes/images/share.png" id="div_share" alt="" /></button>
			<br />
            <button title="'.$txt['one_time_item_view'].'" id="menu_button_otv" class="" onclick="prepareOneTimeView()"><img src="includes/images/globe-share.png" id="div_otv" alt="" /></button>';
	if (
		isset($_GET['code']) && !empty($_GET['code'])
		&& isset($_GET['item_id']) && !empty($_GET['item_id'])
		&& isset($_GET['stamp']) && !empty($_GET['stamp'])
		&& isset($_GET['otv_id'])
	) {
		include 'otv.php';
	} else {
        $('#menu_button_edit_item,#menu_button_del_item,#menu_button_add_fav,#menu_button_del_fav,#menu_button_show_pw,#menu_button_copy_pw,#menu_button_copy_login,#menu_button_copy_link,#menu_button_copy_item,#menu_button_notify,#menu_button_history,#menu_button_share,#menu_button_otv').attr('disabled', 'disabled');
                    $("#menu_button_copy_item, #menu_button_add_group, #menu_button_edit_group, #menu_button_del_group, #menu_button_add_item, #menu_button_edit_item, #menu_button_del_item, #menu_button_history, #menu_button_share, #menu_button_otv").attr('disabled', 'disabled');
            		        $("#menu_button_share, #menu_button_otv").attr('disabled', 'disabled');
            		    	$("#menu_button_share, #menu_button_otv").prop("disabled", false);

/*
* Launch the redirection to OTV page
*/
function prepareOneTimeView()
{
    //Send query
    $.post(
        "sources/items.queries.php",
        {
            type    : "generate_OTV_url",
            id      : $("#id_item").val(),
            key     : "<?php echo $_SESSION['key'];?>"
        },
        function(data) {
            //check if format error
            if (data[0].error == "") {
                $("#div_dialog_message").dialog('open');
                $("#div_dialog_message_text").html(data[0].url);
            } else {
                $("#item_history_log_error").html(data[0].error).show();
            }
        },
        "json"
   );
}
    !isset($_SESSION['CPM']) || $_SESSION['CPM'] != 1 ||
    !isset($_SESSION['user_id']) || empty($_SESSION['user_id']) ||
    !isset($_SESSION['key']) || empty($_SESSION['key']))
<input type="hidden" name="hid_cat" id="hid_cat" value="', isset($_GET['group']) ? htmlspecialchars($_GET['group']) : "", '" />
<input type="hidden" name="input_list_roles" id="input_list_roles" value="'.htmlspecialchars(htmlentities($listRoles, ENT_QUOTES, 'UTF-8')).'" />
<input type="hidden" id="nb_items_to_display_once" value="', isset($_SESSION['settings']['nb_items_by_query']) ? htmlspecialchars($_SESSION['settings']['nb_items_by_query']) : 'auto', '" />
<input type="hidden" name="jstree_group_selected" id="jstree_group_selected" value="'.htmlspecialchars($firstGroup).'" />';
<?php
/**
 * @file          otv.php
 * @author        Nils Laumaillé
 * @version       2.1.20
 * @copyright     (c) 2009-2014 Nils Laumaillé
 * @licensing     GNU AFFERO GPL 3.0
 * @link          http://www.teampass.net
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

require_once('sources/sessions.php');
@session_start();
if (!isset($_SESSION['CPM']) || $_SESSION['CPM'] != 1) {
    die('Hacking attempt...');
}

$html = "";
if (
    filter_var($_GET['code'], FILTER_SANITIZE_STRING) != ""
    && filter_var($_GET['item_id'], FILTER_SANITIZE_STRING) >= 0
    && filter_var($_GET['stamp'], FILTER_SANITIZE_STRING) >= 0
    && filter_var($_GET['otv_id'], FILTER_SANITIZE_STRING) >= 0
) {
    //Include files
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/settings.php';
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/include.php';
    require_once $_SESSION['settings']['cpassman_dir'].'/sources/SplClassLoader.php';

    // connect to DB
    $db = new SplClassLoader('Database\Core', '../includes/libraries');
    $db->register();
    $db = new Database\Core\DbCore($server, $user, $pass, $database, $pre);
    $db->connect();

    // Include main functions used by TeamPass
    require_once 'sources/main.functions.php';

    // check session validity
    $data = $db->queryGetRow(
        "otv",
        array(
            "timestamp",
            "code",
            "item_id"
        ),
        array(
            "id" => intval($_GET['otv_id'])
        )
    );
    if (
        $data[0] == $_GET['stamp']
        && $data[1] == $_GET['code']
        && $data[2] == $_GET['item_id']
    ) {
        // otv is too old
        if ($data[0] < (time() - $k['otv_expiration_period']) ) {
            $html = "Link is too old!";
        } else {
            $dataItem = $db->queryFirst(
                "SELECT *
                FROM ".$pre."items as i
                INNER JOIN ".$pre."log_items as l ON (l.id_item = i.id)
                WHERE i.id=".intval($_GET['item_id'])."
                AND l.action = 'at_creation'"
            );

            // get data
            $pw = decrypt($dataItem['pw']);
            $label = $dataItem['label'];
            $email = $dataItem['email'];
            $url = $dataItem['url'];
            $description = preg_replace('/(?<!\\r)\\n+(?!\\r)/', '', strip_tags($dataItem['description'], $k['allowedTags']));
            $login = str_replace('"', '&quot;', $dataItem['login']);

            // display data
            $html = "<div style='margin:30px;'>".
            	"<div style='font-size:20px;font-weight:bold;'>Welcome to One-Time item view page.</div>".
            	"<div style='font-style:italic;'>Here are the details of the Item that has been shared to you</div>".
            	"<div style='margin-top:10px;'><table>".
				"<tr><td>Label:</td><td>" . $label . "</td</tr>".
            	"<tr><td>Password:</td><td>" . $pw . "</td</tr>".
            	"<tr><td>Description:</td><td>" . $description . "</td</tr>".
            	"<tr><td>login:</td><td>" . $login . "</td</tr>".
            	"<tr><td>URL:</td><td>" . $url ."</td</tr>".
            	"</table></div>".
            	"<div style='margin-top:30px;'>Copy carefully the data you need. This page is only visible once.</div>".
            	"</div>";

        	// delete entry
        	//$db->query("DELETE FROM ".$pre."otv WHERE id = '".intval($_GET['item_id'])."'");
        }
    } else {
        $html = "Not a valid page!";
    }
} else {
    $html = "Not a valid page!";
}

echo $html;
$aSortTypes = array('ASC', 'DESC');
if (isset($_GET['iSortCol_0']) && in_array($_GET['iSortCol_0'], $aSortTypes)) {

} else if (!in_array($_GET['iSortCol_0'], $aSortTypes)) {
	// possible attack - stop
	echo '[{}]';


        /*
        * CASE
        * Check if Item has been changed since loaded
        */
        case "generate_OTV_url":
            // Check KEY
            if ($_POST['key'] != $_SESSION['key']) {
                echo '[ { "error" : "key_not_conform" } ]';
                break;
            }

            // delete all existing old otv codes
            $rows = $db->fetchAllArray("SELECT id FROM ".$pre."otv WHERE timestamp < ".(time() - $k['otv_expiration_period']));
            foreach ($rows as $reccord) {
                $db->queryDelete(
                    'otv',
                    array(
                        'id' => $reccord['id']
                       )
                );
            }

            // generate session
            $pwgen = new SplClassLoader('Encryption\PwGen', '../includes/libraries');
            $pwgen->register();
            $pwgen = new Encryption\PwGen\pwgen();
            $pwgen->setLength(20);
            $pwgen->setSecure(true);
            $otv_code = $pwgen->generate();

            $newID = $db->queryInsert(
                "otv",
                array(
                	'id' => null,
                    'item_id' => intval($_POST['id']),
                    'timestamp' => time(),
                    'originator' => intval($_SESSION['user_id']),
                    'code' => $otv_code
                   )
            );

            $otv_session = array(
                "code"      => $otv_code,
                "item_id"   => $_POST['id'],
                "stamp" => time(),
                "otv_id"    => $newID
            );

            $url = $_SESSION['settings']['cpassman_url']."/index.php?otv=true&".http_build_query($otv_session);
        	$exp_date = date($_SESSION['settings']['date_format']." ".$_SESSION['settings']['time_format'], time() + $k['otv_expiration_period']);

        	echo '[{ "error" : "" , "url" : "'.addslashes(
            	str_replace(
            		array("#URL#", "#DAY#"),
            		array($url, $exp_date),
            		$txt['one_time_view_item_url_box']
            	)
            ).'" }]';
            break;