CVE-2014-3771 (PHP, published 2014-08-07T11:13Z)
Description: value: TeamPass before 2.1.20 allows remote attackers to bypass access restrictions via the language file path in a (1) request to index.php or (2) "change_user_language" request to sources/main.queries.php.
Severity: HIGH (CVSS v3 base score: )
Weakness: CWE-264 \u2014 Permissions, Privileges, and Access Controls
Weaknesses in this category are related to the management of permissions, privileges, and other security features that are used to perform access control.
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;