I have done some preliminary research into this bug and so far it does not seem like a backdoor. Just some really weird logic when handling routes, and rendering templates. | |
As to why widgetConfig[code] executes via a POST request, it is because of the following code located in /includes/vb5/frontend/applicationlight.php | |
$serverData = array_merge($_GET, $_POST); | |
if (!empty($this->application['handler']) AND method_exists($this, $this->application['handler'])) | |
{ | |
$app = $this->application['handler']; | |
call_user_func(array($this, $app), $serverData); | |
return true; | |
} | |
$this->application['handler'] is set via some logic in the class constructor that matches the string value of $_REQUEST['routestring'] to an array of "quick routes". | |
/**Standard constructor. We only access applications through init() **/ | |
protected function __construct() | |
{ | |
if (empty($_REQUEST['routestring'])) | |
{ | |
return false; | |
} | |
if (isset(self::$quickRoutes[$_REQUEST['routestring']])) | |
{ | |
$this->application = self::$quickRoutes[$_REQUEST['routestring']]; | |
return true; | |
} | |
foreach (self::$quickRoutePrefixMatch AS $prefix => $route) | |
{ | |
if (substr($_REQUEST['routestring'], 0, strlen($prefix)) == $prefix) | |
{ | |
$this->application = $route; | |
return true; | |
} | |
} | |
return false; | |
} | |
The quick routes array looks like this: | |
* @var array Quick routes that match the beginning of the route string | |
*/ | |
protected static $quickRoutePrefixMatch = array( | |
'ajax/apidetach' => array( | |
'handler' => 'handleAjaxApiDetached', | |
'static' => false, | |
'requirePost' => true, | |
), // note, keep this before ajax/api. More specific routes should come before | |
// less specific ones, to allow the prefix check to work correctly, see constructor. | |
'ajax/api' => array( | |
'handler' => 'handleAjaxApi', | |
'static' => false, | |
'requirePost' => true, | |
), | |
'ajax/render' => array( | |
'handler' => 'callRender', | |
'static' => false, | |
'requirePost' => true, | |
), | |
); | |
After that the handler from the quick routes is executed, and the process of setting up the template rendering begins. | |
/** | |
* Renders a template from an ajax call | |
* | |
* @param array Array of server data (from $_POST and/or $_GET, see execute()) | |
*/ | |
protected function callRender($serverData) | |
{ | |
$routeInfo = explode('/', $serverData['routestring']); | |
if (count($routeInfo) < 3) | |
{ | |
throw new vB5_Exception_Api('ajax', 'render', array(), 'invalid_request'); | |
} | |
$this->router = new vB5_Frontend_Routing(); | |
$this->router->setRouteInfo(array( | |
'action' => 'actionRender', | |
'arguments' => $serverData, | |
'template' => $routeInfo[2], | |
// this use of $_GET appears to be fine, | |
// since it's setting the route query params | |
// not sending the data to the template | |
// render | |
'queryParameters' => $_GET, | |
)); | |
Api_InterfaceAbstract::setLight(); | |
$this->sendAsJson(vB5_Template::staticRenderAjax($routeInfo[2], $serverData)); | |
} | |
The template code that is executed is as follows: | |
<template name="widget_php" templatetype="template" date="1452807873" username="vBulletin" version="5.2.1 Alpha 2"><![CDATA[<vb:if condition="empty($widgetConfig) AND !empty($widgetinstanceid)"> | |
{vb:data widgetConfig, widget, fetchConfig, {vb:raw widgetinstanceid}} | |
</vb:if> | |
<vb:if condition="!empty($widgetConfig)"> | |
{vb:set widgetid, {vb:raw widgetConfig.widgetid}} | |
{vb:set widgetinstanceid, {vb:raw widgetConfig.widgetinstanceid}} | |
</vb:if> | |
<div class="b-module{vb:var widgetConfig.show_at_breakpoints_css_classes} canvas-widget default-widget custom-html-widget" id="widget_{vb:raw widgetinstanceid}" data-widget-id="{vb:raw widgetid}" data-widget-instance-id="{vb:raw widgetinstanceid}"> | |
{vb:template module_title, | |
widgetConfig={vb:raw widgetConfig}, | |
show_title_divider=1, | |
can_use_sitebuilder={vb:raw user.can_use_sitebuilder}} | |
<div class="widget-content"> | |
<vb:if condition="!empty($widgetConfig['code']) AND !$vboptions['disable_php_rendering']"> | |
{vb:action evaledPHP, bbcode, evalCode, {vb:raw widgetConfig.code}} | |
{vb:raw $evaledPHP} | |
<vb:else /> | |
<vb:if condition="$user['can_use_sitebuilder']"> | |
<span class="note">{vb:phrase click_edit_to_config_module}</span> | |
</vb:if> | |
</vb:if> | |
</div> | |
</div>]]></template> | |
Here is the backtrace for anyone interested. | |
0 eval() called at [/var/www/vbulletin/includes/vb5/frontend/controller/bbcode.php:227] | |
1 vB5_Frontend_Controller_Bbcode::evalCode(debug_print_backtrace();exit;) called at [/var/www/vbulletin/includes/vb5/template/runtime.php:1039] | |
2 vB5_Template_Runtime::parseAction(bbcode, evalCode, debug_print_backtrace();exit;) called at [/var/www/vbulletin/includes/vb5/template.php(369) : eval()'d code:24] | |
3 eval() called at [/var/www/vbulletin/includes/vb5/template.php:369] | |
4 vB5_Template->render(1, 1) called at [/var/www/vbulletin/includes/vb5/template.php:688] | |
5 vB5_Template::staticRender(widget_php, Array ([routestring] => ajax/render/widget_php,[widgetConfig] => Array ([code] => debug_print_backtrace();exit;)), 1, 1) called at [/var/www/vbulletin/includes/vb5/template.php:701] | |
6 vB5_Template::staticRenderAjax(widget_php, Array ([routestring] => ajax/render/widget_php,[widgetConfig] => Array ([code] => debug_print_backtrace();exit;))) called at [/var/www/vbulletin/includes/vb5/frontend/applicationlight.php:302] | |
7 vB5_Frontend_ApplicationLight->callRender(Array ([routestring] => ajax/render/widget_php,[widgetConfig] => Array ([code] => debug_print_backtrace();exit;))) called at [/var/www/vbulletin/includes/vb5/frontend/applicationlight.php:186] | |
8 vB5_Frontend_ApplicationLight->execute() called at [/var/www/vbulletin/index.php:41] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment