CVE-2014-3771: Permissions, Privileges, and Access Controls (CWE-264)
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.
CWE description: Weaknesses in this category are related to the management of permissions, privileges, and other security features that are used to perform access control.
Published: 2014-08-07T11:13Z
Repository: https://github.com/nilsteampassnet/TeamPass
Language: PHP
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 date: 2014-04-26 11:52:49 +0200
Commit message: Several security improvements
New feature: one time item view for anonymous user

Diff: diff --git a/changelog.md b/changelog.md
index c5df2aa..7faadb1 100644
--- a/changelog.md
+++ b/changelog.md
@@ -18,10 +18,11 @@
  #527 - Potential security bug
  #485 - CVS Import on V 2.1.19 quotes problems
  #544 - DataTables warning: JSON data from server could not be parsed
  Fork from slimm609 - Encrypted Sessions and CSRFGuard enabled
  Issues with folder creation in "personal folder"
+ New: one time view page for anonymous user
 
 2.1.19
  #413 - fix for PHP Parse error: syntax error, unexpected '['
  #447 - fix for PHP Fatal error: Cannot redeclare getBits()
  #442 - problem edit folder
diff --git a/includes/images/globe-share.png b/includes/images/globe-share.png
new file mode 100644
index 0000000..e931afc
Binary files /dev/null and b/includes/images/globe-share.png differ
diff --git a/includes/include.php b/includes/include.php
index f11eef1..e2f960b 100644
--- a/includes/include.php
+++ b/includes/include.php
@@ -20,15 +20,18 @@ $k['one_month_seconds'] = 2592000;
 $k['image_file_ext'] = array('jpg', 'gif', 'png', 'jpeg', 'tiff', 'bmp');
 $k['office_file_ext'] = array('xls', 'xlsx', 'docx', 'doc', 'csv', 'ppt', 'pptx');
 $k['admin_full_right'] = true;
 $k['admin_no_info'] = false;
 $k['copyright'] = " &copy; 2009 - 2014";
+$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_NOT_ALLOWED', "1000");
 @define('ERR_NOT_EXIST', "1001");
 @define('ERR_SESS_EXPIRED', "1002");
 @define('ERR_NO_MCRYPT', "1003");
+@define('ERR_VALID_SESSION', "1004");
 
 // Management Pages
 $mngPages = array(
     'manage_users' => 'users.php',
     'manage_folders' => 'folders.php',
diff --git a/includes/language/english.php b/includes/language/english.php
index ef72a93..584a070 100644
--- a/includes/language/english.php
+++ b/includes/language/english.php
@@ -4,10 +4,12 @@ if (!isset($_SESSION['settings']['cpassman_url'])) {
     $TeamPass_url = '';
 } else {
     $TeamPass_url = $_SESSION['settings']['cpassman_url'];
 }
 
+$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#";
 $txt['url_copied_clipboard'] = "URL copied in clipboard";
 $txt['url_copy'] = "Copy URL in clipboard";
 
 $txt['settings_attachments_encryption'] = "Enable encryption of Items attachments";
 $txt['settings_attachments_encryption_tip'] = "THIS OPTION COULD BREAK EXISTING ATTACHMENTS, please read carefully the next. If enabled, Items attachments are stored encrypted on the server. The ecryption uses the SALT defined for Teampass. This requieres more server ressources. WARNING: once you change strategy, it is mandatory to run the script to adapt existing attachments. See tab 'Specific Actions'.";
diff --git a/index.php b/index.php
index 695b579..700210e 100644
--- a/index.php
+++ b/index.php
@@ -8,11 +8,11 @@ session_start();
 <?php
 /**
  *
  * @file          index.php
  * @author        Nils LaumaillÃ©
- * @version       2.1.19
+ * @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,
@@ -49,19 +49,16 @@ $db->register();
 $db = new Database\Core\DbCore($server, $user, $pass, $database, $pre);
 $db->connect();
 
 //load main functions needed
 require_once 'sources/main.functions.php';
+// Load CORE
+require_once $_SESSION['settings']['cpassman_dir'].'/sources/core.php';
 
 /* DEFINE WHAT LANGUAGE TO USE */
 if (!isset($_SESSION['user_id']) && !isset($_POST['language'])) {
     //get default language
-    /*$dataLanguage =
-        $db->fetchRow(
-            "SELECT valeur FROM ".$pre."misc
-            WHERE type = 'admin' AND intitule = 'default_language'"
-        );*/
     $dataLanguage = $db->queryGetRow(
         "misc",
         array(
             "valeur"
         ),
@@ -89,16 +86,20 @@ if (!isset($_SESSION['user_id']) && !isset($_POST['language'])) {
     } elseif (isset($_SESSION['settings']['default_language'])) {
         $_SESSION['user_language'] = $_SESSION['settings']['default_language'];
     }
 }
 // Load user languages files
-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';
+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';
 }
-// Load CORE
-require_once $_SESSION['settings']['cpassman_dir'].'/sources/core.php';
+
 // Load links, css and javascripts
 @require_once $_SESSION['settings']['cpassman_dir'].'/load.php';
 ?>
 
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
@@ -206,21 +207,24 @@ if (isset($_SESSION['login'])) {
                 </button>
             </div>
         </div>';
 }
 // Display language menu
-echo '
+if (!isset($_GET['otv'])) {
+	echo '
         <div style="float:right;">
             <dl id="flags" class="dropdown">
                 <dt><img src="includes/images/flags/'.$_SESSION['user_language_flag'].'" alt="" /></dt>
                 <dd>
                     <ul>
                     '.$languagesDropmenu.'
                     </ul>
                 </dd>
             </dl>
-        </div>
+        </div>';
+}
+echo '
     </div>';
 
 /* LAST SEEN */
 echo '
     <div style="display:none;" id="div_last_items" class="ui-state-active ui-corner-all">
@@ -297,11 +301,13 @@ if (isset($_SESSION['autoriser']) && $_SESSION['autoriser'] == true && isset($_G
             <br />
             <button title="'.$txt['link_copy'].'" style="margin-bottom:5px;" id="menu_button_copy_link" class="copy_clipboard"><img src="includes/images/target.png" id="div_copy_link" alt="" /></button>
             <br />
             <button title="'.$txt['history'].'" id="menu_button_history" class="" onclick="OpenDialog(\'div_item_history\', \'false\')"><img src="includes/images/report.png" id="div_history" alt="" /></button>
             <br />
-            <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>';
+            <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($_SESSION['settings']['enable_email_notification_on_item_shown']) && $_SESSION['settings']['enable_email_notification_on_item_shown'] == 1) {
         echo '
             <br />
             <button style="margin-bottom:5px;" id="menu_button_notify" class=""><img src="includes/images/alarm-clock.png" id="div_notify" alt="" /></button>';
     }
@@ -456,13 +462,18 @@ if (isset($_SESSION['validite_pw']) && $_SESSION['validite_pw'] == true && !empt
     $_SESSION['error']['code'] = ERR_SESS_EXPIRED;
     $_SESSION['initial_url'] = substr($_SERVER["REQUEST_URI"], strpos($_SERVER["REQUEST_URI"], "index.php?"));
     include 'error.php';
 } elseif ((!isset($_SESSION['validite_pw']) || empty($_SESSION['validite_pw']) || empty($_SESSION['user_id'])) && isset($_GET['otv']) && $_GET['otv'] == "true") {
     // case where one-shot viewer
-    if (isset($_GET['session']) && !empty($_GET['session'])) {
-        include 'otv.php?session='.$_GET['session'];
-    } else {
+	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 {
         $_SESSION['error']['code'] = ERR_VALID_SESSION;
         $_SESSION['initial_url'] = substr($_SERVER["REQUEST_URI"], strpos($_SERVER["REQUEST_URI"], "index.php?"));
         include 'error.php';
     }
 } elseif (empty($_SESSION['user_id']) && isset($_GET['action']) && $_GET['action'] == "password_recovery") {
diff --git a/items.load.php b/items.load.php
index 8ad8c0b..48b5203 100644
--- a/items.load.php
+++ b/items.load.php
@@ -160,11 +160,11 @@ function ListerItems(groupe_id, restricted, start)
         $("#selected_items").val("");
         $("#hid_cat").val(groupe_id);
         if ($(".tr_fields") != undefined) $(".tr_fields, .newItemCat, .editItemCat").hide();
 
         //Disable menu buttons
-        $('#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_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');
 
         // clear existing clips
         //ZeroClipboard.destroy();
 
         //ajax query
@@ -195,11 +195,11 @@ function ListerItems(groupe_id, restricted, start)
                     $('#menu_button_add_item').attr('disabled', 'disabled');
                     $("#items_list_loader, #div_loading").hide();
                 } else if (data.error == "not_authorized") {
                     //warn user
                     $("#hid_cat").val("");
-                    $("#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_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');
                     $("#item_details_nok").show();
                     $("#item_details_ok, #item_details_no_personal_saltkey").hide();
                     $("#items_list_loader").hide();
                 } else if ($("#user_is_read_only").val() == 1 && data.recherche_group_pf == 0) {
                     //readonly user
@@ -1113,13 +1113,13 @@ function AfficherDetailsItem(id, salt_key_required, expired_item, restricted, di
                         }
                         //$("#menu_button_show_pw, #menu_button_copy_pw, #menu_button_copy_login, #menu_button_copy_link, #menu_button_history").prop("disabled", false);
 
                         // disable share button for personal folder
                         if ($("#recherche_group_pf").val() == 1) {
-            		        $("#menu_button_share").attr('disabled', 'disabled');
+            		        $("#menu_button_share, #menu_button_otv").attr('disabled', 'disabled');
             		    } else {
-            		    	$("#menu_button_share").prop("disabled", false);
+            		    	$("#menu_button_share, #menu_button_otv").prop("disabled", false);
             		    }
 
                         //Manage to deleted information
                         if (data.to_be_deleted != 0 && data.to_be_deleted != null && data.to_be_deleted != "not_enabled") {
                             $('#item_extra_info').html("<i><img src=\'&nbsp;<?php echo $_SESSION['settings']['cpassman_url'];?>/includes/images/information-white.png\'><?php echo addslashes($txt['automatic_deletion_activated']);?></i>");
@@ -2598,6 +2598,32 @@ function aes_encrypt(text)
 
 function aes_decrypt(text)
 {
     return Aes.Ctr.decrypt(text, "<?php echo $_SESSION['key'];?>", 256);
 }
+
+/*
+* 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"
+   );
+}
 </script>
diff --git a/items.php b/items.php
index 4574167..8923070 100644
--- a/items.php
+++ b/items.php
@@ -12,13 +12,13 @@
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  */
 
 if (
-    !isset($_SESSION['CPM']) || $_SESSION['CPM'] != 1 || 
-    !isset($_SESSION['user_id']) || empty($_SESSION['user_id']) || 
-    !isset($_SESSION['key']) || empty($_SESSION['key'])) 
+    !isset($_SESSION['CPM']) || $_SESSION['CPM'] != 1 ||
+    !isset($_SESSION['user_id']) || empty($_SESSION['user_id']) ||
+    !isset($_SESSION['key']) || empty($_SESSION['key']))
 {
     die('Hacking attempt...');
 }
 
 /* do checks */
@@ -72,24 +72,24 @@ foreach ($rows as $reccord) {
 }
 // Build list of visible folders
 $selectVisibleFoldersOptions = $selectVisibleNonPersonalFoldersOptions = "";
 // Hidden things
 echo '
-<input type="hidden" name="hid_cat" id="hid_cat" value="', isset($_GET['group']) ? $_GET['group'] : "", '" />
+<input type="hidden" name="hid_cat" id="hid_cat" value="', isset($_GET['group']) ? htmlspecialchars($_GET['group']) : "", '" />
 <input type="hidden" id="complexite_groupe" />
 <input type="hidden" name="selected_items" id="selected_items" />
 <input type="hidden" name="input_liste_utilisateurs" id="input_liste_utilisateurs" value="'.$usersString.'" />
-<input type="hidden" name="input_list_roles" id="input_list_roles" value="'.htmlentities($listRoles, ENT_QUOTES, 'UTF-8').'" />
+<input type="hidden" name="input_list_roles" id="input_list_roles" value="'.htmlspecialchars(htmlentities($listRoles, ENT_QUOTES, 'UTF-8')).'" />
 <input type="hidden" id="bloquer_creation_complexite" />
 <input type="hidden" id="bloquer_modification_complexite" />
 <input type="hidden" id="error_detected" />
 <input type="hidden" name="random_id" id="random_id" />
 <input type="hidden" id="edit_wysiwyg_displayed" value="" />
 <input type="hidden" id="richtext_on" value="1" />
 <input type="hidden" id="query_next_start" value="0" />
 <input type="hidden" id="display_categories" value="0" />
-<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" 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" id="user_is_read_only" value="', isset($_SESSION['user_read_only']) && $_SESSION['user_read_only'] == 1 ? '1' : '', '" />
 <input type="hidden" id="request_ongoing" />
 <input type="hidden" id="request_lastItem" />
 <input type="hidden" id="item_editable" />
 <input type="hidden" id="timestamp_item_displayed" />
@@ -116,11 +116,11 @@ if (isset($_COOKIE['jstree_select']) && !empty($_COOKIE['jstree_select'])) {
     $firstGroup = str_replace("#li_", "", $_COOKIE['jstree_select']);
 } else {
     $firstGroup = "";
 }
 echo '
-<input type="hidden" name="jstree_group_selected" id="jstree_group_selected" value="'.$firstGroup.'" />';
+<input type="hidden" name="jstree_group_selected" id="jstree_group_selected" value="'.htmlspecialchars($firstGroup).'" />';
 
 echo '
 <div id="div_items">';
 // MAIN ITEMS TREE
 echo '
diff --git a/otv.php b/otv.php
new file mode 100644
index 0000000..3ab1ec3
--- /dev/null
+++ b/otv.php
@@ -0,0 +1,103 @@
+<?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;
\ No newline at end of file
diff --git a/sources/find.queries.php b/sources/find.queries.php
index 0d36b83..51ec69f 100644
--- a/sources/find.queries.php
+++ b/sources/find.queries.php
@@ -30,10 +30,11 @@ $db->register();
 $db = new Database\Core\DbCore($server, $user, $pass, $database, $pre);
 $db->connect();
 
 //Columns name
 $aColumns = array('id', 'label', 'description', 'tags', 'id_tree', 'folder', 'login');
+$aSortTypes = array('ASC', 'DESC');
 
 //init SQL variables
 $sOrder = $sLimit = "";
 $sWhere = "id_tree IN(".implode(', ', $_SESSION['groupes_visibles']).")";    //limit search to the visible folders
 
@@ -78,13 +79,14 @@ $sLimit = "";
 if (isset($_GET['iDisplayStart']) && $_GET['iDisplayLength'] != '-1') {
     $sLimit = "LIMIT ". filter_var($_GET['iDisplayStart'], FILTER_SANITIZE_NUMBER_INT) .", ". filter_var($_GET['iDisplayLength'], FILTER_SANITIZE_NUMBER_INT)."";
 }
 
 //Ordering
-if (isset($_GET['iSortCol_0'])) {
+if (isset($_GET['iSortCol_0']) && in_array($_GET['iSortCol_0'], $aSortTypes)) {
     $sOrder = "ORDER BY  ";
     for ($i=0; $i<intval($_GET['iSortingCols']); $i++) {
+
         if (
             $_GET[ 'bSortable_'.filter_var($_GET['iSortCol_'.$i], FILTER_SANITIZE_NUMBER_INT)] == "true" &&
             preg_match("#^(asc|desc)\$#i", $_GET['sSortDir_'.$i])
         ) {
             $sOrder .= "".$aColumns[ filter_var($_GET['iSortCol_'.$i], FILTER_SANITIZE_NUMBER_INT) ]." "
@@ -94,12 +96,15 @@ if (isset($_GET['iSortCol_0'])) {
 
     $sOrder = substr_replace($sOrder, "", -2);
     if ($sOrder == "ORDER BY") {
             $sOrder = "";
     }
+} else if (!in_array($_GET['iSortCol_0'], $aSortTypes)) {
+	// possible attack - stop
+	echo '[{}]';
 }
-//echo $sOrder;
+
 /*
  * Filtering
  * NOTE this does not match the built-in DataTables filtering which does it
  * word by word on any field. It's possible to do here, but concerned about efficiency
  * on very large tables, and MySQL's regex functionality is very limited
diff --git a/sources/items.queries.php b/sources/items.queries.php
index 23aeb29..1cac462 100644
--- a/sources/items.queries.php
+++ b/sources/items.queries.php
@@ -50,12 +50,10 @@ $pwComplexity = array(0 => array(0, $txt['complex_level0']),
     70 => array(70, $txt['complex_level4']),
     80 => array(80, $txt['complex_level5']),
     90 => array(90, $txt['complex_level6'])
    );
 
-$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>';
-
 //Class loader
 require_once $_SESSION['settings']['cpassman_dir'].'/sources/SplClassLoader.php';
 
 // Connect to mysql server
 $db = new SplClassLoader('Database\Core', '../includes/libraries');
@@ -2674,10 +2672,70 @@ if (isset($_POST['type'])) {
                 echo '{ "modified" : "1" }';
             } else {
                 echo '{ "modified" : "0" }';
             }
             break;
+
+        /*
+        * 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;
     }
 }
 // Build the QUERY in case of GET
 if (isset($_GET['type'])) {
     switch ($_GET['type']) {

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;