前情提要
vBulletin是一款商业论坛和博客平台,其是由PHP开发距今约有10年的历史了。这款论坛程序十分流行,在全球TOP 100K站点中约有78%的站点使用该平台。
一个月前Check Point秘密向vBulletin报告了一个危险的未经身份验证RCE漏洞,该漏洞(CVE-2015-7808)是由Netanel Rubin独立发现。
该漏洞允许攻击者不需进行用户身份验证在任意vBulletin服务器上执行PHP代码。与此前漏洞不同,该漏洞不是利用默认安装的主题或者模块。
正如前几天我们报道的《 知名论坛程序vBulletin 5曝远程命令执行漏洞,可能已泄露千万用户数据 》论坛主站vBulletin.org就被攻破,且vBulletin 0-day已经在进行售卖了。
之后vBulletin官方发布了补丁修复这个漏洞,遗憾的是我们没有看到任何报告,另外也没有也没有提到相关的CVE编号。
随着补丁的发布,我们现在可以尽情的讨论下细节问题了。
技术细节
vBulletin通过内部API处理大量的信息,不幸的是部分API可以使用Ajax请求,当然这些API也是可以通过正常CGI访问的。
该API根本没有验证请求源,我们能够毫无阻碍的访问接口。事实上这个接口,允许我们调用继承于vB_Api类(位于/core/vb/api/)中的任意公共方法。该文件夹(/core/vb/api/)中包含的数十个类,数百个方法完全可以被我们拿来进行攻击,需要注意的是,其中部分被认为是内部方法。此外,由于这个API没有参数白名单,我们还可以使用任意参数来调用这些方法。
我们先从存在漏洞的方法vB_Api_Hook::decodeArguments()开始,没有对请求进行身份验证,且在代码中包含一个unserialize调用
因为我们控制$arguments参数,也就是说我们控制了传入unserialize()调用的内容,事实上这也意味着只需要我们脑子够灵活,就能构造并且将对象注入$args变量。
当触发特定的事件时,PHP会自动调用那几个魔术方法。例如,一个对象被破环(‘__destruct()’ 方法就会被调用),或者尝试将一个对象作为一个字符串使用就会触发‘__toString()’方法。
这也意味着我们现在可以通过任意定义的类触发__destruct()方法。我们只设置对象的类型,当decodeArguments()函数返回时这个对象就会遭到破坏。
检测下面这个有趣的构析函数(vB_vURL):
该方法需要tmpfile属性且将其看作一个文件名,如果存在就删除。
这显然可以用来在WEB服务器上删除可访问的文件,其预想便是导致服务器瘫痪,甚至说造成永久性的数据丢失。然而我们对此并不满足,继续利用这段代码在对象中使用更多的受保护方法。如果我们将another对象插入tmpfile,尝试将其转换成字符串将会产生一个file_exists()调用。事实上,现在我们增加__toString()方法来扩大攻击面。
接下来我们看看vB_View的__toString()函数:
__toString()快速调用vB_View的render()方法。不幸的是,这个基础render()方法对于我们的利用没有什么太大帮助。考虑到vB_View_AJAXHTML类继承于vB_View,以及与此完全不同的render()方法:
如上图所示,在该对象下的content属性中,render()调用另外一个render()。由于我们能够控制其中一个render(),现在我们可以通过render()调用任意类(不局限于vB_View objects)
以下为在vB5_Template类中实现render()方法:
基本上该方法从缓存或者数据库中加载一个template,并且使用的template name为template属性中进行指定的,之后基于template类型进行eval或者include操作。
由于仅仅只能在一个正确转义的SQL查询中使用,我们不能对其进行LFI测试的。尽管如此,我们也就可以从数据库中加载我们想要的template。
接下来看看widget_php中的template代码:
$widgetConfig中的code值是函数最终evals操作后的内容,如果能够控制这个变量,我们可以在服务器上执行任意PHP代码。
vB5_Template类中的render()方法,将$this->registered做为参数调用extract()函数。可能一些朋友已经观察到extract()接收一个键值字典作为参数并且添加一个键值对作为当前范围的变量。因为我们控制了$this->registered,我们可以添加任意变量和值到当前范围。
最后将变量$widgetConfig添加到registered属性,将值设置为一个字典,code作为key,我们的PHP利用代码作为值。
难忘的POC