CVE-2018-9019 (PHP, published 2018-05-22T20:29Z)
Description: value: SQL Injection vulnerability in Dolibarr before version 7.0.2 allows remote attackers to execute arbitrary SQL commands via the sortfield parameter to /accountancy/admin/accountmodel.php, /accountancy/admin/categories_list.php, /accountancy/admin/journals_list.php, /admin/dict.php, /admin/mails_templates.php, or /admin/website.php.
Severity: HIGH (CVSS v3 base score: 9.8)
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/Dolibarr/dolibarr
Files changed:
htdocs/accountancy/admin/accountmodel.php
htdocs/accountancy/admin/categories.php
htdocs/accountancy/admin/categories_list.php
htdocs/accountancy/admin/journals_list.php
htdocs/admin/dict.php
htdocs/admin/mails_templates.php
htdocs/admin/website.php
htdocs/core/lib/functions.lib.php
Commit message: FIX CVE-2018-9019

Vulnerable code:
$id=GETPOST('id','int');
$sortfield = GETPOST("sortfield",'alpha');
$sortorder = GETPOST("sortorder",'alpha');
$tabname[32]= MAIN_DB_PREFIX."c_accounting_category";
$tablib[32]= "DictionaryAccountancyCategory";
$tabsql[32]= "SELECT a.rowid as rowid, a.code as code, a.label, a.range_account, a.sens, a.category_type, a.formula, a.position as position, a.fk_country as country_id, c.code as country_code, c.label as country, a.active FROM ".MAIN_DB_PREFIX."c_accounting_category as a, ".MAIN_DB_PREFIX."c_country as c WHERE a.fk_country=c.rowid and c.active=1";
$tabsqlsort[32]="position ASC";
$tabfield[32]= "code,label,range_account,sens,category_type,formula,position,country_id,country";
$tabfieldvalue[32]= "code,label,range_account,sens,category_type,formula,position,country";
$tabfieldinsert[32]= "code,label,range_account,sens,category_type,formula,position,fk_country";
$tabrowid[32]= "";
$tabcond[32]= ! empty($conf->accounting->enabled);
$tabhelp[32] = array('code'=>$langs->trans("EnterAnyCode"));
$tabfieldcheck[32] = array();
	print $form->formconfirm($_SERVER["PHP_SELF"].'?'.($page?'page='.$page.'&':'').'sortfield='.$sortfield.'&sortorder='.$sortorder.'&rowid='.$rowid.'&code='.$code.'&id='.$id, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_delete','',0,1);
	if ($sortfield)
	{
		// If sort order is "country", we use country_code instead
		if ($sortfield == 'country') $sortfield='country_code';
		$sql.= " ORDER BY ".$sortfield;
		if ($sortorder)
		{
			$sql.=" ".strtoupper($sortorder);
		}
		$sql.=", ";
		// Clear the required sort criteria for the tabsqlsort to be able to force it with selected value
		$tabsqlsort[$id]=preg_replace('/([a-z]+\.)?'.$sortfield.' '.$sortorder.',/i','',$tabsqlsort[$id]);
		$tabsqlsort[$id]=preg_replace('/([a-z]+\.)?'.$sortfield.',/i','',$tabsqlsort[$id]);
	}
	else {
		$sql.=" ORDER BY ";
	}
	$sql.=$tabsqlsort[$id];
$linkback = '<a href="'.DOL_URL_ROOT.'/accountancy/admin/categories_list.php?search_country_id='.$mysoc->country_id.'">'.$langs->trans("BackToList").'</a>';
$sortfield = GETPOST("sortfield",'alpha');
$sortorder = GETPOST("sortorder",'alpha');
    $sql = "DELETE from ".$tabname[$id]." WHERE ".$rowidcol." = '".$this->db->escape($rowid)."'";
        $sql = "UPDATE ".$tabname[$id]." SET active = 1 WHERE ".$rowidcol." = '".$this->db->escape($rowid)."'";
    	$sql = "UPDATE ".$tabname[$id]." SET active = 1 WHERE code = '".$this->db->escape($code)."'";
    	$sql = "UPDATE ".$tabname[$id]." SET active = 0 WHERE ".$rowidcol." = '".$this->db->escape($rowid)."'";
    	$sql = "UPDATE ".$tabname[$id]." SET active = 0 WHERE code = '".$this->db->escape($code)."'";
    	$sql = "UPDATE ".$tabname[$id]." SET favorite = 1 WHERE ".$rowidcol." = '".$this->db->escape($rowid)."'";
    	$sql = "UPDATE ".$tabname[$id]." SET favorite = 1 WHERE code = '".$this->db->escape($code)."'";
    	$sql = "UPDATE ".$tabname[$id]." SET favorite = 0 WHERE ".$rowidcol." = '".$this->db->escape($rowid)."'";
    	$sql = "UPDATE ".$tabname[$id]." SET favorite = 0 WHERE code = '".$this->db->escape($code)."'";
    if ($sortfield)
    {
        // If sort order is "country", we use country_code instead
    	if ($sortfield == 'country') $sortfield='country_code';
        $sql.= " ORDER BY ".$sortfield;
        if ($sortorder)
        {
            $sql.=" ".strtoupper($sortorder);
        }
        $sql.=", ";
        // Clear the required sort criteria for the tabsqlsort to be able to force it with selected value
        $tabsqlsort[$id]=preg_replace('/([a-z]+\.)?'.$sortfield.' '.$sortorder.',/i','',$tabsqlsort[$id]);
        $tabsqlsort[$id]=preg_replace('/([a-z]+\.)?'.$sortfield.',/i','',$tabsqlsort[$id]);
    }
    else {
        $sql.=" ORDER BY ";
    }
    $sql.=$tabsqlsort[$id];
                        print '<a href="'.DOL_URL_ROOT.'/accountancy/admin/categories.php?action=display&account_category='.$obj->rowid.'">';
$id=GETPOST('id','int');
	if ($sortfield)
	{
		// If sort order is "country", we use country_code instead
		if ($sortfield == 'country') $sortfield='country_code';
		$sql.= " ORDER BY ".$sortfield;
		if ($sortorder)
		{
			$sql.=" ".strtoupper($sortorder);
		}
		$sql.=", ";
		// Clear the required sort criteria for the tabsqlsort to be able to force it with selected value
		$tabsqlsort[$id]=preg_replace('/([a-z]+\.)?'.$sortfield.' '.$sortorder.',/i','',$tabsqlsort[$id]);
		$tabsqlsort[$id]=preg_replace('/([a-z]+\.)?'.$sortfield.',/i','',$tabsqlsort[$id]);
	}
	else {
		$sql.=" ORDER BY ";
	}
	$sql.=$tabsqlsort[$id];
$param = '&id='.$id;
if ($search_country_id > 0) $param.= '&search_country_id='.$search_country_id;
if ($sortorder) $paramwithsearch.= '&sortorder='.$sortorder;
if ($sortfield) $paramwithsearch.= '&sortfield='.$sortfield;
if (GETPOST('from')) $paramwithsearch.= '&from='.GETPOST('from','alpha');
        $sql.= " ORDER BY ".$sortfield;
            $sql.=" ".strtoupper($sortorder);
if ($sortfield)
{
    // If sort order is "country", we use country_code instead
	if ($sortfield == 'country') $sortfield='country_code';
    $sql.= " ORDER BY ".$sortfield;
    if ($sortorder)
    {
        $sql.=" ".strtoupper($sortorder);
    }
    $sql.=", ";
    // Clear the required sort criteria for the tabsqlsort to be able to force it with selected value
    $tabsqlsort[$id]=preg_replace('/([a-z]+\.)?'.$sortfield.' '.$sortorder.',/i','',$tabsqlsort[$id]);
    $tabsqlsort[$id]=preg_replace('/([a-z]+\.)?'.$sortfield.',/i','',$tabsqlsort[$id]);
}
else {
    $sql.=" ORDER BY ";
}
$sql.=$tabsqlsort[$id];

    if ($sortfield)
    {
        // If sort order is "country", we use country_code instead
        $sql.= " ORDER BY ".$sortfield;
        if ($sortorder)
        {
            $sql.=" ".strtoupper($sortorder);
        }
        $sql.=", ";
        // Clear the required sort criteria for the tabsqlsort to be able to force it with selected value
        $tabsqlsort[$id]=preg_replace('/([a-z]+\.)?'.$sortfield.' '.$sortorder.',/i','',$tabsqlsort[$id]);
        $tabsqlsort[$id]=preg_replace('/([a-z]+\.)?'.$sortfield.',/i','',$tabsqlsort[$id]);
    }
    else {
        $sql.=" ORDER BY ";
    }
    $sql.=$tabsqlsort[$id];
			// We save search key only if:
			// - not empty, or
			// - if value is empty and a default value exists that is not empty (it means we did a filter to an empty value when default was not).
			if (! empty($out))

Fixed code:
$id=31;
$sortfield = GETPOST("sortfield",'aZ09comma');
$sortorder = GETPOST("sortorder",'aZ09comma');
	print $form->formconfirm($_SERVER["PHP_SELF"].'?'.($page?'page='.urlencode($page).'&':'').'sortfield='.urlencode($sortfield).'&sortorder='.urlencode($sortorder).'&rowid='.urlencode($rowid).'&code='.urlencode($code).'&id='.urlencode($id), $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_delete','',0,1);
	// If sort order is "country", we use country_code instead
	if ($sortfield == 'country') $sortfield='country_code';
	$sql.=$db->order($sortfield,$sortorder);
$linkback = '<a href="'.DOL_URL_ROOT.'/accountancy/admin/categories_list.php?restore_lastsearch_values=1">'.$langs->trans("BackToList").'</a>';
$sortfield = GETPOST("sortfield",'aZ09comma');
$sortorder = GETPOST("sortorder",'aZ09comma');
    $sql = "DELETE from ".$tabname[$id]." WHERE ".$rowidcol." = '".$db->escape($rowid)."'";
        $sql = "UPDATE ".$tabname[$id]." SET active = 1 WHERE ".$rowidcol." = '".$db->escape($rowid)."'";
    	$sql = "UPDATE ".$tabname[$id]." SET active = 1 WHERE code = '".$db->escape($code)."'";
    	$sql = "UPDATE ".$tabname[$id]." SET active = 0 WHERE ".$rowidcol." = '".$db->escape($rowid)."'";
    	$sql = "UPDATE ".$tabname[$id]." SET active = 0 WHERE code = '".$db->escape($code)."'";
    	$sql = "UPDATE ".$tabname[$id]." SET favorite = 1 WHERE ".$rowidcol." = '".$db->escape($rowid)."'";
    	$sql = "UPDATE ".$tabname[$id]." SET favorite = 1 WHERE code = '".$db->escape($code)."'";
    	$sql = "UPDATE ".$tabname[$id]." SET favorite = 0 WHERE ".$rowidcol." = '".$db->escape($rowid)."'";
    	$sql = "UPDATE ".$tabname[$id]." SET favorite = 0 WHERE code = '".$db->escape($code)."'";
    // If sort order is "country", we use country_code instead
    if ($sortfield == 'country') $sortfield='country_code';
    $sql.=$db->order($sortfield,$sortorder);
                        print '<a href="'.DOL_URL_ROOT.'/accountancy/admin/categories.php?action=display&save_lastsearch_values=1&account_category='.$obj->rowid.'">';
$id=35;
	// If sort order is "country", we use country_code instead
	if ($sortfield == 'country') $sortfield='country_code';
	$sql.=$db->order($sortfield,$sortorder);
$param = '&id='.urlencode($id);
if ($search_country_id > 0) $param.= '&search_country_id='.urlencode($search_country_id);
if ($sortorder) $paramwithsearch.= '&sortorder='.urlencode($sortorder);
if ($sortfield) $paramwithsearch.= '&sortfield='.urlencode($sortfield);
if (GETPOST('from')) $paramwithsearch.= '&from='.urlencode(GETPOST('from','alpha'));
        $sql.= " ORDER BY ".$db->escape($sortfield);
        	$sql.=" ".strtoupper($db->escape($sortorder));
// If sort order is "country", we use country_code instead
if ($sortfield == 'country') $sortfield='country_code';
$sql.=$db->order($sortfield,$sortorder);
    $sql.=$db->order($sortfield,$sortorder);
			// We save search key only if $out not empty that means:
			// - posted value not empty, or
			// - if posted value is empty and a default value exists that is not empty (it means we did a filter to an empty value when default was not).
			if ($out != '')		// $out = '0' like 'abc' is a search criteria to keep