Drupal YAML 反序列化代码执行漏洞(CVE-2017-6920)

事件背景

框架漏洞收集

老外的CMS框架,比较复杂,数据流向太长,调试需要消耗较多的时间。

漏洞说明

1. 漏洞原理:2017年6月21日,Drupal官方发布了一个编号为CVE-2017- 6920 的漏洞,影响为Critical。这是Drupal Core的YAML解析器处理不当所导致的一个远程代码执行漏洞,影响8.x的Drupal Core。

2. 组件描述:Drupal是使用PHP语言编写的开源内容管理框架(CMF),它由由内容管理系统和PHP开发框架共同构成,在GPL2.0及更新协议下发布。连续多年荣获全球最佳CMS大奖,是基于PHP语言最著名的WEB应用程序。

3. 影响版本: 8.0.0~8.3.3

漏洞复现

环境搭建:GitHub - drupal/drupal at 8.3.3

这里可能需要利用docker环境的命令配置一下,我自己的环境为:windows10、PHP7.1、电脑带有docker可使用composer命令生成vendor目录、需要提供yaml环境(https://www.cnblogs.com/yycode/p/16039854.html)、在php.ini文件中添加”yaml.decode_php=On“

然后访问/core/install.php进入安装界面,简单填数据库的信息安装即可

登录后访问路径/admin/config/development/configuration/single/import

向输入框输入POC

点击import,可以看到phpinfo界面,说明POC攻击成功

漏洞分析

drupal8.3.3为开源框架,可以直接审计代码进行调试,根据网上相关漏洞的信息,可以得知本漏洞是yaml_parse()触发的yaml php反序列化,于是先找到该函数的出现的点

有且仅有一个,因此可以断定就是这里,进入看看

这里可以在PHP手册看到该函数的作用

可以看到第一个参数为YAML格式字符串,而函数的作用为将全部或部分 YAML 文档流转换为 PHP 变量,还有一个注意事项

大概意思为:如果使用!php/object则会进行一次反序列化,这个开关由ini的yaml.decode_php控制,因此大概能知道这个函数如果在特定条件下会触发反序列化,而我们现在需要的是控制第一个参数,即$raw

步入Yaml.php::decode查看是否能实现与YamlPecl.php::decode进行链接,步入getSerializer查看一下

可以看到,只要存在yaml环境即可步入第一个条件判断,这也是为什么需要yaml环境了,再去看有没有可控参数,最后定位到ConfigSingleImportForm.php文件

可以看到Yaml::decode()中参数可控因此直接在这利用即可,不过还有一个问题就是利用哪个文件进行反序列化,这个需要查看多个php类文件,最后定位在

vendor\guzzlehttp\guzzle\src\Cookie\FileCookieJar.php

\vendor\guzzlehttp\psr7\src\FnStream.php

一个可以直接写webshell,另一个是可以执行无参函数

最后POC为:

<?php

namespace GuzzleHttp\Psr7;

class FnStream {

    public function __construct(array $methods){

        $this->methods = $methods;

        // Create the functions on the class

        foreach ($methods as $name => $fn) {

        $this->{'_fn_' . $name} = $fn;

        }

    }

    public function __destruct(){

        if (isset($this->_fn_close)) {

        call_user_func($this->_fn_close);

        }

    }

}

$fn = new FnStream(array('close'=>'phpinfo'));

echo(serialize($fn))

?>

最后在生成的payload前面增加 !php/object 修改为yaml格式即可,除了yaml的一个触发点,其它和普通的PHP反序列化没什么区别