CVE-2014-3773 (PHP, published 2014-08-07T11:13Z)
Description: value: Multiple SQL injection vulnerabilities in TeamPass before 2.1.20 allow remote attackers to execute arbitrary SQL commands via the login parameter in a (1) send_pw_by_email or (2) generate_new_password action in sources/main.queries.php; iDisplayStart parameter to (3) datatable.logs.php or (4) a file in source/datatable/; or iDisplayLength parameter to (5) datatable.logs.php or (6) a file in source/datatable/; or allow remote authenticated users to execute arbitrary SQL commands via a sSortDir_ parameter to (7) datatable.logs.php or (8) a file in source/datatable/.
Severity: HIGH (CVSS v3 base score: )
Weakness: CWE-89 \u2014 Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
Repository: https://github.com/nilsteampassnet/TeamPass
Files changed:
admin.php
admin.settings_api.php
api/functions.php
api/index.php
includes/include.php
items.load.php
items.php
otv.php
sources/admin.queries.php
sources/datatable/datatable.item_edition.php
sources/datatable/datatable.kb.php
sources/datatable/datatable.logs.php
sources/datatable/datatable.users_logged.php
sources/find.queries.php
sources/items.queries.php
sources/users.queries.php
Commit message: Security/API (beta) improvements


Vulnerable code:
            <span class="ui-icon ui-icon-wrench" style="float: left; margin-right: .3em;">&nbsp;</span>
                For any kind of Help and Support, please use the <a target="_blank" href="http://www.teampass.net/forum" style="font-weight:bold;font-style:italic;">Forum</a><br />
    $("#api_db_type").val("admin_action_api_save_key");
<?php

// Set Teampass path
$teampass_path = "E:/xampp/htdocs/Teampass";
// Set Teampass config file
$teampass_config_file = $teampass_path."/includes/settings.php";
// Set Teampass secure keys file path
$teampass_sk_file = "/space/private/teampass/sk.php";
?>
	if (count($apiip_pool) > 0 && !array_search($_SERVER['REMOTE_ADDR'], $apiip_pool)) {
				$array_category = explode(';',$GLOBALS['request'][2]);

				foreach($array_category as $category) {
					if(!preg_match_all("/^([\w\:\'\-\sÃ Ã¡Ã¢Ã£Ã¤Ã¥Ã§Ã¨Ã©ÃªÃ«Ã¬Ã­Ã®Ã¯Ã°Ã²Ã³Ã´ÃµÃ¶Ã¹ÃºÃ»Ã¼Ã½Ã¿]+)$/i", $category,$result)) {
						rest_error('CATEGORY_MALFORMED');
					}
				}

				if(count($array_category) > 1 && count($array_category) < 5) {
					for ($i = count($array_category); $i > 0; $i--) {
						$slot = $i - 1;
						if (!$slot) {
							$category_query .= "select id from ".$GLOBALS['pre']."nested_tree where title LIKE '".$array_category[$slot]."' AND parent_id = 0";
						} else {
							$category_query .= "select id from ".$GLOBALS['pre']."nested_tree where title LIKE '".$array_category[$slot]."' AND parent_id = (";
						}
					}
					for ($i = 1; $i < count($array_category); $i++) { $category_query .= ")"; }
				} elseif (count($array_category) == 1) {
					$category_query = "select id from ".$GLOBALS['pre']."nested_tree where title LIKE '".$array_category[0]."' AND parent_id = 0";
					rest_error ('NO_CATEGORY');
				$response = $bdd->query("select id,label,login,pw from ".$GLOBALS['pre']."items where id_tree = (".$category_query.")");
//					$json[$id]['pw'] = teampass_decrypt_pw($data['pw'],SALT,$rand_key);
}

?>
require_once('config.php');
);
    echo '<input type="hidden" name="open_folder" id="open_folder" value="'.$_GET['group'].'" />';
    echo '<input type="hidden" name="open_id" id="open_id" value="'.$_GET['id'].'" />';
    echo '<input type="hidden" name="recherche_group_pf" id="recherche_group_pf" value="', in_array($_GET['group'], $_SESSION['personal_visible_groups']) ? '1' : '', '" />';
    echo '<input type="hidden" name="open_folder" id="open_folder" value="'.$_GET['group'].'" />';
    echo '<input type="hidden" name="recherche_group_pf" id="recherche_group_pf" value="', in_array($_GET['group'], $_SESSION['personal_visible_groups']) ? '1' : '', '" />';
                    <li class="jstreeopen" id="li_'.$folder->id.'">';
                        <input type="hidden" id="hid_label" value="', isset($dataItem) ? $dataItem['label'] : '', '" />
                        <div id="id_desc" style="font-style:italic;display:inline;"></div><input type="hidden" id="hid_desc" value="', isset($dataItem) ? $dataItem['description'] : '', '" />
                            <div id="id_field_'.$field[0].'" style="display:inline;" class="fields_div"></div><input type="hidden" id="hid_field_'.$field[0].'" class="fields" />
    $sLimit = "LIMIT ". $_GET['iDisplayStart'] .", ". $_GET['iDisplayLength'] ;
if (isset($_GET['iSortCol_0'])) {
    for ($i=0; $i<intval($_GET['iSortingCols']); $i++) {
    $sLimit = "LIMIT ". mysql_real_escape_string($_GET['iDisplayStart']) .", "
            . mysql_real_escape_string($_GET['iDisplayLength']) ;
if (isset($_GET['iSortCol_0'])) {
    for ($i=0; $i<intval($_GET['iSortingCols']); $i++) {
        if ($_GET[ 'bSortable_'.intval($_GET['iSortCol_'.$i]) ] == "true") {
            $sOrder .= $aColumns[ intval(mysql_real_escape_string($_GET['iSortCol_'.$i])) ]."
                    ".mysql_real_escape_string($_GET['sSortDir_'.$i]) .", ";
        }
    }
        $sLimit = "LIMIT ". mysql_real_escape_string($_GET['iDisplayStart']) .", "
                . mysql_real_escape_string($_GET['iDisplayLength']) ;
    if (isset($_GET['iSortCol_0'])) {
        for ($i=0; $i<intval($_GET['iSortingCols']); $i++) {
            if ($_GET[ 'bSortable_'.intval($_GET['iSortCol_'.$i]) ] == "true") {
                $sOrder .= $aColumns[ intval(mysql_real_escape_string($_GET['iSortCol_'.$i])) ]."
                    ".mysql_real_escape_string($_GET['sSortDir_'.$i]) .", ";
            }
        }
    $sLimit = "LIMIT ". mysql_real_escape_string($_GET['iDisplayStart']) .", "
            . mysql_real_escape_string($_GET['iDisplayLength']);
if (isset($_GET['iSortCol_0'])) {
    for ($i=0; $i<intval($_GET['iSortingCols']); $i++) {
        if ($_GET[ 'bSortable_'.intval($_GET['iSortCol_'.$i]) ] == "true") {
            $sOrder .= $aColumns[ intval(mysql_real_escape_string($_GET['iSortCol_'.$i])) ]."
            ".mysql_real_escape_string($_GET['sSortDir_'.$i]) .", ";
        }
    }

                $arrData['description'] = preg_replace('/(?<!\\r)\\n+(?!\\r)/', '', strip_tags($dataItem['description'], $allowedTags));
                    'qui' => $_SESSION['user_id'],
                    'field_1' => $_POST['id']

Fixed code:
    $("#api_db_type").val("admin_action_api_save_ip");

/**
 *
 * @file          configapi.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.
 */

$teampass_config_file = "../includes/settings.php";
	if (count($apiip_pool) > 0 && array_search($_SERVER['REMOTE_ADDR'], $apiip_pool) === false) {
				// get ids
				if (strpos($GLOBALS['request'][2],",") > 0) {
					$condition = "id_tree IN (".$GLOBALS['request'][2].")";
					$condition = "id_tree = '".$GLOBALS['request'][2]."'";
				$response = $bdd->query("select id,label,login,pw from ".$GLOBALS['pre']."items where ".$condition);
					$json[$id]['pw'] = teampass_decrypt_pw($data['pw'],SALT,$rand_key);
}
/**
 *
 * @file          indexapi.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('config.php');
);
				$("#div_dialog_message").dialog({minHeight:500,minWidth:750});
    echo '<input type="hidden" name="open_folder" id="open_folder" value="'.htmlspecialchars($_GET['group']).'" />';
    echo '<input type="hidden" name="open_id" id="open_id" value="'.htmlspecialchars($_GET['id']).'" />';
    echo '<input type="hidden" name="recherche_group_pf" id="recherche_group_pf" value="', in_array(htmlspecialchars($_GET['group']), $_SESSION['personal_visible_groups']) ? '1' : '', '" />';
    echo '<input type="hidden" name="open_folder" id="open_folder" value="'.htmlspecialchars($_GET['group']).'" />';
    echo '<input type="hidden" name="recherche_group_pf" id="recherche_group_pf" value="', in_array(htmlspecialchars($_GET['group']), $_SESSION['personal_visible_groups']) ? '1' : '', '" />';
                    <li class="jstreeopen" id="li_'.$folder->id.'" title="ID ['.$folder->id.']">';
                        <input type="hidden" id="hid_label" value="', isset($dataItem) ? htmlspecialchars($dataItem['label']) : '', '" />
                        <div id="id_desc" style="font-style:italic;display:inline;"></div><input type="hidden" id="hid_desc" value="', isset($dataItem) ? htmlspecialchars($dataItem['description']) : '', '" />
                            <div id="id_field_'.$field[0].'" style="display:inline;" class="fields_div"></div><input type="hidden" id="hid_field_'.htmlspecialchars($field[0]).'" class="fields" />

        	// get key for original pw
        	$originalKey = $db->queryFirst('SELECT rand_key FROM `'.$pre.'keys` WHERE `table` LIKE "items" AND `id` ='.intval($_GET['item_id']));
        	// unsalt previous pw
        	$pw = substr(decrypt($dataItem['pw']), strlen($originalKey['rand_key']));


					'value'     => $_POST['key'],
$aSortTypes = array('ASC', 'DESC');
    $sLimit = "LIMIT ". filter_var($_GET['iDisplayStart'], FILTER_SANITIZE_NUMBER_INT) .", ". filter_var($_GET['iDisplayLength'], FILTER_SANITIZE_NUMBER_INT)."";
if (isset($_GET['iSortCol_0']) && in_array($_GET['iSortCol_0'], $aSortTypes)) {
/*
for ($i=0; $i<intval($_GET['iSortingCols']); $i++) {
*/
	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) ]." "
			.mysql_real_escape_string($_GET['sSortDir_'.$i]) .", ";
		}
	}
$aSortTypes = array('ASC', 'DESC');
    $sLimit = "LIMIT ". filter_var($_GET['iDisplayStart'], FILTER_SANITIZE_NUMBER_INT) .", ". filter_var($_GET['iDisplayLength'], FILTER_SANITIZE_NUMBER_INT)."";
if (isset($_GET['iSortCol_0']) && in_array($_GET['iSortCol_0'], $aSortTypes)) {
	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) ]." "
			.mysql_real_escape_string($_GET['sSortDir_'.$i]) .", ";
		}
	}
	$aSortTypes = array('ASC', 'DESC');
        $sLimit = "LIMIT ". filter_var($_GET['iDisplayStart'], FILTER_SANITIZE_NUMBER_INT) .", ". filter_var($_GET['iDisplayLength'], FILTER_SANITIZE_NUMBER_INT)."";
    if (isset($_GET['iSortCol_0']) && in_array($_GET['iSortCol_0'], $aSortTypes)) {
    	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) ]." "
    			.mysql_real_escape_string($_GET['sSortDir_'.$i]) .", ";
    		}
    	}
$aSortTypes = array('ASC', 'DESC');
    $sLimit = "LIMIT ". filter_var($_GET['iDisplayStart'], FILTER_SANITIZE_NUMBER_INT) .", ". filter_var($_GET['iDisplayLength'], FILTER_SANITIZE_NUMBER_INT)."";
if (isset($_GET['iSortCol_0']) && in_array($_GET['iSortCol_0'], $aSortTypes)) {
	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) ]." "
			.mysql_real_escape_string($_GET['sSortDir_'.$i]) .", ";
		}
	}
                $arrData['description'] = preg_replace('/(?<!\\r)\\n+(?!\\r)/', '', strip_tags($dataItem['description'], $k['allowedTags']));
                    'qui' => intval($_SESSION['user_id']),
                    'field_1' => intval($_POST['id'])