概述
技术细节
一、CSRF漏洞(CVE-2020-36389)
CiviCRM的管理界面使用CKEditor富文本编辑器,可以直接编辑和写入配置文件。下述代码中使用了一个run()方法,每次访问编辑器时都会调用该方法。第56行,将通过$_REQUEST['present']接收将被保存的当前配置文件名。第63行,使用攻击者控制的POST参数调用save()方法。这里缺少CSRF令牌验证机制,这将导致CSRF漏洞(CVE-2020-36389)。
civicrm/CRM/Admin/Page/CKEditorConfig.php
55 public function run() {
56 $this->preset = CRM_Utils_Array::value( 'preset', $_REQUEST, 'default' );
...
62 elseif ( ! empty( $_POST['config'] ) ) {
63 $this->save( $_POST );
64 }
save()方法会根据传递的POST参数构造一个配置文件,并将生成的配置保存到 JavaScript文件中。这允许攻击者通过CSRF在目标主机上编写JavaScript文件。
下面具体介绍了配置文件的内容是如何创建的。在下述代码的第110行中,默认文件头被添加到配置文件中,并且在第113-129行合成配置文件内容。因此,每一个POST参数名都是从第115行中的config开始处理的。POST参数的名称和值在第126行连接起来,并被添加到第127行的$config中。在第130行,saveConfigFile() 方法被攻击者控制的变量($config)和部分受控文件名($this->present)所调用。
civicrm/CRM/Admin/Page/CKEditorConfig.php
107 public function save( $params ) {
108 $config = self::fileHeader()
109 // Standardize line-endings
110 . preg_replace( '~R~u', "n", $params['config'] );
112 // Use all params starting with config_
113 foreach ( $params as $key => $val ) {
115 if ( strpos( $key, 'config_' ) === 0 && strlen( $val ) ) {
117 $val = json_encode( $val, JSON_UNESCAPED_SLASHES );
124 $pos = strrpos( $config, '};' );
125 $key = preg_replace( '/^config_/', 'config.', $key );
126 $setting = "nt{$key} = {$val};n";
127 $config = substr_replace( $config, $setting, $pos, 0 );
128 }
129 }
130 self::saveConfigFile( $this->preset, $config );
134 }
攻击者可以通过发送一个包含任意值的密钥配置的单个POST字段来跳过第115-129行,因为密钥不以config_开头。这允许攻击者在文件头之后插入任何内容。需要注意的是,文件头本身由多行注释组成,因此不会导致JavaScript语法错误,从而终止JavaScript的执行。
接下来我们来看一下写入此内容的文件的名称。在下述代码的第238行中,构成了配置文件的文件名,并在第239行中写入文件的内容。部分受攻击者控制的文件名格式为:crm-ckeditor-$preset.js。
此文件名中受攻击者控制的信息是$preset。路径遍历攻击是不可行的,因为crm-ckeditor-文件夹必须位于当前目录之上。此外还有一个限制,即文件名将始终具有.js 扩展名。
civicrm/CRM/Admin/Page/CKEditorConfig.php
237 public static function saveConfigFile( $preset, $contents ) {
238 $file = Civi::paths()->getPath( self::CONFIG_FILEPATH . $preset . '.js' );
239 file_put_contents( $file, $contents );
240 }
要成功利用此漏洞,攻击者总共需要两个CSRF请求,第一个 CSRF请求用于在配置文件中创建一个XSS有效载荷。第二个CSRF请求通过覆盖CKEditor默认配置,永久加载先前释放的XSS配置文件。
攻击者使用第一个CSRF请求,创建一个名为crm-ckeditor-xss.js的配置文件。为了在没有语法错误的情况下,执行JavaScript代码,攻击者会跳过save()方法的第115-129行。
在CKEditor配置中,允许通过customConfig指令包含另一个自定义配置文件。包含配置文件的JavaScript代码将被直接执行(CIVI-SA-2020-12)。因此,第二个CSRF请求会覆盖默认的CKEditor配置,并通过包含XSS有效载荷的指令包含先前创建的crm-ckeditor-xss.js。每次调用CKEditor时,组合这两个CSRF请求,都会在整个后端中导致存储型XSS。
二、Phar反序列化漏洞(CVE-2020-36388)
通过存储型XSS,攻击者现在可以在管理员的浏览器中执行JavaScript,从而可以在Web应用程序中执行任意操作。
但是,管理员并不总是拥有允许访问控制服务器的功能的访问权限,这取决于CMS和服务器的配置。因此在大多数情况下,攻击者会试图扩展他们的能力,例如在服务器上执行代码。下面,将介绍另一个漏洞,攻击者可以利用该漏洞提升权限。
该漏洞存在于Badge组件中,在下述代码的第22行中,来自第21行的用户控制变量$img被传递给getImageProperties()方法,无需任何进一步检查。
civicrm/CRM/Badge/Page/AJAX.php
20 public static function getImageProp() {
21 $img = $_GET['img'];
22 list($w, $h) = CRM_Badge_BAO_Badge::getImageProperties($img);
24 }
在下述代码的第399行中,用户控制的$img变量被直接传递给PHP内部函数getimagesize(),这导致Phar反序列化漏洞。因为攻击者可以控制整个字符串,所以其能够通过phar://封装器反序列化对象。
civicrm/CRM/Badge/BAO/Badge.php
398 public static function getImageProperties($img, $imgRes = 300, $w = NULL, $h = NULL) {
399 $imgsize = getimagesize($img);
404 }
为了成功利用Phar反序列化漏洞,攻击者必须将图像上传到包含Phar元数据的文件系统。然后通过Ajax请求调用getImageProp()方法,其中img参数指向先前上传图像的路径。虽然,只有管理员身份才能利用该漏洞,但这可以通过上述的存储型XSS实现。
总结
本文中,详细分析了CiviCRM (5.22.0)中存在的两个漏洞,攻击者可以组合利用这两个漏洞,以完全接管易受攻击的CiviCRM实例。CiviCRM技术团队已针对这些漏洞发布了多个补丁,建议受影响的用户尽快安装补丁进行防护。
END
本文始发于微信公众号(SecTr安全团队):组合利用CiviCRM插件中的漏洞实现任意代码执行
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论