CVE-2017-6920:Drupal远程代码执行漏洞分析及POC构造

作者: 启明星辰ADLab

0x01 漏洞描述

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

0x02 漏洞分析

通过diff 8.3.3与8.3.4的文件可以发现漏洞的触发点,如下图:

可以看到,8.3.4 decode函数的开始处增加了如下的代码:

漏洞所在函数decode的触发点代码如下:

decode函数的参数$raw被直接带入了yamlparse函数中,官方文档对于yamlparse函数的描述如下:

第一个参数是需要parse成yaml的文档流。从上文来看,只有yaml_parse的第一个参数是外部可控的。官方对这个函数有一个特别的说明,也就是该漏洞的触发原理:

即可以通过!php/object来声明一个节点,然后用这个!php/object声明的节点内容会以unserialize的方式进行处理;如果要禁止这样做,就通过设置yaml.decode_php来处理,这就是官方补丁在decode函数前面加的那几行代码。因此,这个远程代码执行漏洞的罪魁祸首就是yaml_parse函数可能会用反序列化的形式来处理输入的字符串,从而导致通过反序列化类的方式来操作一些危险类,最终实现代码执行。 显然,控制decode函数的参数即可触发该漏洞。先定位decode函数的调用位置,在/core/lib/Drupal/Component/Serialization/Yaml.php中第33行发现:

该函数调用了getSerializer函数,跟踪该函数在/core/lib/Drupal/Component/ Serialization/Yaml.php中第48行发现:

如果存在yaml扩展,$serializer就使用YamlPecl类,然后调用YamlPecl这个类中的decode函数;如果不存在yaml扩展,就用YamlSymfony类中的decode函数。显然,一定要迫使代码利用YamlPecl类中的decode函数,这需要引入yaml扩展,Linux平台的步骤如下:

(1)编译yaml

在http://pecl.php.net/package/yaml下载tgz源码包,然后执行tar -zxvf yaml-1.3.0.tgz cd yaml-1.3.0 phpize ./configure make make install,执行完返回一个文件夹名字,这就是生成的扩展所在目录。

(2)引用扩展

修改php.ini中的extension_dir为该扩展所在目录,然后加上 extension=yaml.so 就可以了。

windows平台步骤更简单,在http://pecl.php.net/package/yaml中下载对应的dll文件,然后将php_yaml.dll放入php扩展文件夹下,然后修改php.ini,将extensiondir为phpyaml.dll所存放的目录,然后加上 extension=php_yaml.dll。

最后重启apache,看到phpinfo中有yaml扩展,就说明安装成功,如图: 

1498555104746788.png

现在yaml扩展已经准备好,最后定位外部可控的输入点。上文中YamlPecl::decode是在Yaml::decode函数中调用的,继续回溯全文调用Yaml::decode函数的地方,发现外部可控的地方只有一处, 在/core/modules/config/src/Form/ConfigSingleImportForm.php中第280行:

这里对外部输入的import值进行Ymal::decode操作,因此这里就是漏洞的数据触发点。

要利用该漏洞进行远程代码执行,需要一个可以利用的类。Drupal使用命名空间的方式来管理类,可以全局实例化一个类,也可以反序列化一个类;该漏洞利用了反序列,因此需要找一个反序列类。通过_destruct以及_wakeup来定位类,全局搜索可以找到几个可利用的类。

(1)/vendor/symfony/process/Pipes/WindowsPipes.php中的89行:

通过反序列化这个类可以造成一个任意文件删除。

(2)/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php中第37行:

通过反序列化这个类可以造成写入webshell。

(3)/vendor/guzzlehttp/psr7/src/FnStream.php中第48行:

通过反序列化这个类可以造成任意无参数函数执行。

0x03 漏洞验证

启明星辰 ADLab 通过对本漏洞的深度分析,构造了任意无参数函数的POC并测试验证成功,具体验证情况如下:

第一步:序列化一个GuzzleHttp\Psr7\FnStream类, 因为序列化后的字符串可能带有不可显示字符,所以采用把结果写入到文件的方式,序列化后的字符串如图:

4-6.png第二步:给该序列化字符串加上yaml的 <span style="font-family: 微软雅黑, 'Microsoft YaHei';">!php/object</span> tag(注意一定要转义),最后得到的字符串如下:

第三步:登录一个管理员账号,访问如下url:

http://localhost/drupal833/admin/config/development/configuration/single/import,然后我们进行如图所示的操作:

1498555813481315.png

然后点击import按钮,就会执行phpinfo函数。  

1498555865516043.png

0x04 漏洞修复

最新发布的Drupal 8.3.4 已经修复了该漏洞,针对低于8.3.4的版本也可以通过升级Drupal文件/core/lib/Drupal/Component/Serialization/YamlPecl.php中的decode函数进行防御(添加如下红色代码即可):

0x05 漏洞检测

针对该漏洞,可采用两种方法进行检测:

方法一:登陆Drupal管理后台,查看内核版本是8.x,且版本号低于8.3.4,则存在该漏洞;否则,不存在该漏洞;

方法二:在Drupal根目录下找到文件/core/lib/Drupal/Component/Serialization/ YamlPecl.php,定位到函数public static function decode($raw),如果该函数代码不包含" ini_set('yaml.decode_php', 0);"调用,则存在该漏洞;否则,不存在该漏洞。

相关链接: https://www.drupal.org/SA-CORE-2017-003

本文转载自:http://paper.seebug.org/334/


发表评论

(必填)

(必填)

(以便回访)