0x01脆弱性はじめに
dolibarr erp crm=15.0.3は、評価注入に対して脆弱です。デフォルトでは、任意の管理者をdolibarrのインストールページに追加でき、正常に追加された場合、悪意のあるコードをデータベースに挿入してから、評価によって実行できます。
CVE番号:CVE-2022-2633
脆弱性の説明:dolibarr edit.phpには、リモートコマンド実行の脆弱性があります。攻撃者が論理的な脆弱性を通じて管理者を作成した後、バックグラウンドの脆弱性を介してサーバーの権限を取得できます。
影響を受けるバージョン:=15.0.3
0x02脆弱性分析
1.環境構造
ソースコードダウンロードアドレス:3https://github.com/dolibarr/dolibarr/archive/refs/tags/15.0.3.zip
それをWebディレクトリに解凍して直接アクセスする〜/htdocs/
次に、インストールするようにconf/conf.phpを構成します
2。管理者ユーザーを登録
これは実際には論理的な脆弱性です。システムをインストールした後、ロックしませんが、ユーザーがドキュメントディレクトリに手動で追加する必要があるため、管理者アカウントを追加するためにいつでも入力できます。
たとえば、ここにAAAユーザーを追加します
背景を正常に入力できます
3.BackEnd RCE
バックグラウンドRCEの最後のポイントは、htdocs/core/lib/functions.lib.phpのdol_eval()関数にあります
しかし、ここにはWAFがあり、危険な機能のほとんどは禁止されています
//PHP execまたはPHPファイル機能の使用をブロックします
$ 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('。prode( '|'、$ forbiddenphpfunctions)。 ')\ b';
する {
$ oldstringtoclean=$ s;
$ s=str_ireplace($ forbiddenphpstrings、 '__forbiddenstring__'、$ s);
$ s=preg_replace( '/'.forbiddenphpregex。'/i '、' __forbiddenstring__ '、$ s);
//$ s=preg_replace( '/\ $ [a-za-z0-9 _ \ - \ $]+\(/i'、 ''、$ s); //削除$ function(call and $ mycall-mythod(
} while($ oldstringtoclean!=$ s);
if(strpos($ s、 '__forbiddenstring__')!==false){
dol_syslog( 'bad string syntax to Evaluate3:'。$ s、log_warning);
if($ returnValue){
'bad string構文を返して、'を評価します。$ s;
} それ以外{
dol_syslog( 'bad string syntax to Evaluate:'。$ s);
戻る '';
}
}
//$ s.'br \ n 'を印刷します。
if($ returnValue){
if($ hiderrors){
return @eval( 'return'。$ s。 ';');
} それ以外{
return eval( 'return'。$ s。 ';');
}
} それ以外{
if($ hiderrors){
@eval($ s);
} それ以外{
eval($ s);
}
}
ここでは、dol_eval()の呼び出しを探し、上記のverifcond()が呼び出されます
そして、ここにスプライシングがあり、これについては後で説明します。
関数verifcond($ stroevaluate)
{
グローバル$ユーザー、$ conf、$ langs;
グローバル$ leftmenu;
グローバル$ rights; //dol_eval関数にエクスポートします
//$ strtoevaluate.'br \ n ';
$ rights=true;
if(ISSET($ sttoevaluate)$ stroevaluate!==''){
$ str='if(!('。$ sttoevaluate。 '))$ rights=false;';
dol_eval($ str、0、1、 '2');
}
$ rightsを返します。
}
次に、Verifcond関数のグローバルパラメーター制御可能な呼び出しを探します。 menubase.class.phpのmenuload()関数にポイントがあります。
Verifcondコードは制御可能ですが、データベースのクエリ結果から取得されることがわかります。
パームと有効化に注意してください。どちらも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);
//$右を定義します
$ perms=true;
if(isset($ menu ['perms'])){
$ tmpcond=$ menu ['perms'];
if($ leftmenu=='all'){
$ tmpcond=preg_replace( '/\ $ leftmenu \ s*==\ s*[' \ 'a-za-z _]+/'、 '1==1'、$ tmpcond); //条件の部分を真に強制します
}
$ perms=verifcond($ tmpcond);
//'verifcond rowid='。$ menu ['rowid']。 'を印刷します。 '。$ tmpcond.':'。$ perms.'br \ n ';
}
//$ 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); //条件の部分を真に強制します
}
$ enabled=verifcond($ tmpcond);
}
ここで実行されているSQLステートメントを見るために正面に行きましょう。 「.main_db_prefix」のデータを照会しています。メニューテーブルですが、条件付きステートメントがあります
(0、 '。$ conf-entity。')のm.entity
m.menu_handler in( ''。$ this-db-escape($ menu_handler)。 ''、 'all')
したがって、「.main_db_prefix」に挿入ステートメントを見つけることができる場合。メニューでは、パームを制御し、フィールドとエンティティとmenu_handlerを有効にして、条件を満たすことができます。エンティティは$ 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.Leftmenu、M.Position';
$ sql。='from' .main_db_prefix.'menu as m ';
$ sql。='ここで、m.entity in(0、'。$ conf-entity。 ')';
$ sql。='およびm.menu_handler in(' '。$ this-db-escape($ menu_handler)。' '、' all ')';
if($ type_user==0){
$ sql。='およびm.usertype in(0,2)';
}
if($ type_user==1){
$ sql。='およびm.usertype in(1,2)';
}
$ sql。='M.Position、M.Rowid'による注文;
ここでは、定期的な検索を検索してください。実際、同じファイルにcreate()関数、そのような点があります。
次に、パラメーターが制御可能かどうかを確認する必要があります。ここの値はメンバー属性として設定されますが、エンティティは$ conf-entityであり、上記のSQLクエリもこれであるため、条件を直接満たします。
次に、menuload関数を実行するときにmenu_handlerが自動的に入力されることがわかりました。
したがって、両方の条件が解決された場所。残りは、パーマとイネーブルが制御されているかどうかを確認することです。クラス内のメンバー変数を割り当てる場所はないため、グローバルに検索する必要があります。
メニュー/edit.phpでパーマとイネーブルを直接制御できることがわかります
デバッグ後、MenuIDは一意である必要があることがわかりました。そうしないと、競合し、データベースに書き込むことができません。ここでのタイプは1に設定する必要があります。そうしないと、エラーが報告されます。
次に、WAFをバイパスしてevalを実行する方法を研究します。ここで、著者のアプローチは、PHP:変数関数の特性を使用することです
//file_put_contents
$ a=base64_decode( 'zmlszv9wdxrfy29udgvudhm=');
//shellcode
$ a( '。1234.php'、base64_decode( 'pd9wahagcghwaw5mbygpoz8+cg=='));
Verifcond関数を見る
これが文字列のスプライシングです。 evalが実行されているため、ブラケットを閉じて次のコードをコメントすることができます。
関数verifcond($ stroevaluate)
{
グローバル$ユーザー、$ conf、$ langs;
グローバル$ leftmenu;
グローバル$ rights; //dol_eval関数にエクスポートします
//$ strtoevaluate.'br \ n ';
$ rights=true;
if(ISSET($ sttoevaluate)$ stroevaluate!==''){
$ str='if(!('。$ sttoevaluate。 '))$ rights=false;';
dol_eval($ str、0、1、 '2');
}
$ rightsを返します。
}
これはそのようなペイロードです(無害なペイロード
1==1)
次に、有効なパラメーターを配置してデータベースに保存すると、最終的にパッケージは次のとおりです。
データベースに正常に保存されます
デバッグしてVerifcondを入力します
Verifcond、悪意のあるコンストラクトステッチバイパスでフォローアップ、Dol_Evalを入力します
コード実行に正常に
成功したけいれん
脆弱性コールスタック
0x03脆弱性の概要
ここでのこのRCEの脆弱性の原則は、実際には二次注射に似ています。まず、悪意のあるコードがデータベースに保存され、次にデータベースからデータを抽出するときに悪意のあるコードがトリガーされます。ここでもWAFがバイパスされ、PHP機能——変数関数を使用します
脆弱性修正
ここでは、著者の脆弱性に対する修正は、Verifcond関数を強化することです
ここで、文字列のスプライシングがキャンセルされ、dol_evalの4番目のパラメーターは「1」です
これは、次の判断になります。ここでのコメントを見てください。ここのルールは、RCEを防ぐように設計されています。
1つは、DOL_EVAL関数の強化です。ここで、ForbiddenPhpFunctionsはVerifcond関数を追加します。これはVerifcondの実行を直接禁止しますが、このHHHの意味がわかりません
著者:Huamang
元のテキスト接続から転載:https://blog.huamang.xyz/post/cve-2022-40871/
Recommended Comments