vBulletin是一个国外著名的商业论坛程序。前几天因官网被黑,而被爆出一个命令执行漏洞。
我们最早获得的一篇分析是在Pastie上的http://pastie.org/pastes/10527766/text?key=wq1hgkcj4afb9ipqzllsq,但是经过测试,我们可以确认漏洞存在,但是里面的POC我们在5.1.5版本中却未能测试成功。
分析:
测试版本:vBulletin 5.1.5
1.漏洞出现原因:
文件`/core/vb/api/hook.php`:
在类vB_Api_Hook的类函数decodeArguments中:
直接对`$arguments`参数直接进行反序列化操作,导致漏洞产生。
对于`unserialize`函数何种情况下会产生危害,可以查看:https://www.owasp.org/index.php/PHP_Object_Injection
2.POC:
vBulletin中没有可以直接利用的magic函数,所以我们需要结合多个类使用。
2.1.Pastie中的POC
Pastie中_cutz给出的是利用重写`vB_Database`类中的`$functions['free_result']`,然后通过`vB_dB_Result`类来调用。
2.1.1.类vB_Database
类`vB_Database`的声明在文件`/core/vb/database.php`中,里面有这么一个类函数:
`$queryresult`变量可控,只要我们能重写`$this->functions['free_result']`,就能执行任意函数。
比如`$this→functions['free_result']='system'`,`$queryresult='ifconfig'`,这样`return @$this→functions['free_result']($queryresult)`就变成了`return @system('ifconfig')`。
然后通过`unserialize`函数,我们可以直接重写`$this->functions['free_result']`,也就是_cutz给出的:
然后我们需要找个调用`vB_Database->free_result($queryresult)`的类。
2.1.2.类vB_dB_Result
类`vB_dB_Result`在文件`/core/vb/db/result.php`中。
里面有两个magic函数,__construct()和__destruct()。
调用到`vB_Database->free_result($queryresult)`的有两个类函数,`rewind()`和`free()`。
我们可以看到连个magic函数都间接调用到了`vB_Database->free_result($queryresult)`。
我们测试`unserialize`为类时,`__construct`并不会被执行,而`__desctruct`会执行。
那我们利用`__destruct`可以吗?
但 是`__destruct->free`里面有`if (isset($this->db) AND !empty($this->recordset) AND is_resource($this->recordset))`,`is_resource($this->recordset)`这个不 好绕过,即使绕过了,当我们调用 `vB_Database->free_result($queryresult)->$this->functions['free_result']($queryresult)`时,`$queryresult`参数也不好操作。
但是vB_dB_Result类实现了(implements)Iterator类,Iterator在foreach中会自动调用rewind函数(http://php.net/manual/zh/class.iterator.php)。在vB_Api_Hook类的decodeArguments函数里就有对$args的foreach操作($args = @unserialize($arguments))。
2.1.3.利用
*首先从vB_Database类开始:
$this->functions['free_result']:重写为我们想要的函数,如phpinfo, system等
但是vB_Database为抽象类,所以我们要找个继承vB_Database的类来使用,如vB_Database_MySQL类和vB_Database_MySQLi类。
*到vB_dB_Result类:
$this->db:覆盖为上面被修改过的vB_Database_MySQL。
$this→recordset:我们要传入函数的参数。
*结合上述两个类,序列化输出:
2.2.其它利用分析
这利用方法不是我们自己发现。当我们陷入死胡同后,Check Point Research Team的一篇分析给了我们光明,在此和大家分享。
http://blog.checkpoint.com/2015/11/05/check-point-discovers-critical-vbulletin-0-day/
2.2.1.vB_vURL类
我们通过搜索magic函数,在类`vB_vURL`中的`__destruct`中找到:
这时`$this->tmpfile`可被我们重写,但是这也只是个任意文件删除漏洞。难道就没有更好的利用方式了吗?这时我们想到了`__toString`。
当`$this->tmpfile`为一个对象时,`file_exists($this->tmpfile)`执行时,`$this->tmpfile->__toString`也会执行。
但是我们通过搜索了含有__toString的类,并没有找到什么有用的东西。
这时,我们开始寻找继承类,那些继承了含有__destruct或__toString的类。
找了好久,有几个类引起了我们的注意,其中一个就是vB_View_AJAXHTML。
2.2.2.vB_View_AJAXHTML类
vB_View_AJAXHTML类在文件/core/vb/view/ajaxhtml.php中。
vB_View类在文件/core/vb/view.php中。
类vB_View_AJAXHTML继承了vB_View类。
在vB_View类中有__toString:
vB_View类中的类函数render对我们来说没什么利用价值,但是在继承类vB_View_AJAXHTML中重新声明了类函数render。
vB_View_AJAXHTML类中的render():
这里,我们只要覆盖$this->content就能调用任何$obj->render()。
2.2.3. vB5_Template类
我们搜索`function render`看是否有可利用的render函数。之后在类`vB5_Template`中找到这个:
这里,我们只要覆盖$this->content就能调用任何$obj->render()。
2.2.3. vB5_Template类
我们搜索`function render`看是否有可利用的render函数。之后在类`vB5_Template`中找到这个:
我们直接按那篇报告的流程来,获取title为widget_php的代码内容。
这里$evaledPHP =vB_Template_Runtime::parseAction('bbcode', 'evalCode', $widgetConfig['code'])可当作eval($widgetConfig['code'])。
现在我们只需覆盖$widgetConfig['code']这个变量,在vB5_Template的render里有
我们可以覆盖self::$globalRegistered或者$this->registered,然后extract以后再覆盖$widgetConfig['code']。
这时就能从一个unserialize到命令执行了。
2.2.4.利用思路
*从vB5_Template开始,我们需要覆盖:
$this->registered:用在extract($this->registered, EXTR_OVERWRITE | EXTR_REFS),覆盖变量$widgetConfig['code']。
$this->template:覆盖为widget_php,使$templateCode = $templateCache->getTemplate($this->template)获取到的是template widget_php。
相关链接:
https://theadminzone.com/threads/vbulletin-com-forums-hacked.136961/
http://blog.checkpoint.com/2015/11/05/check-point-discovers-critical-vbulletin-0-day/
http://pastie.org/pastes/10527766/text?key=wq1hgkcj4afb9ipqzllsq
http://www.vbulletin.org/forum/showthread.php?p=2558144
http://archive.hack.lu/2015/They Hate Us Cause They Ain't Us.pdf