刷刷先知,增长知识

复现

根据网上的poc脚本,本地测试了一波 https://seclists.org/fulldisclosure/2019/Sep/31

image.png

分析

根据routestring全局搜索代码,会搜出很多。根据ajax/render筛选出最终位置 image.png 后续将调用的handercallRender,于是全局搜索 image.png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
protected function callRender()
    {
        $routeInfo = explode('/', $_REQUEST['routestring']);

        if (count($routeInfo) < 3)
        {
            throw new vB5_Exception_Api('ajax', 'api', array(), 'invalid_request');
        }

        $params = array_merge($_POST, $_GET);
        $this->router = new vB5_Frontend_Routing();
        $this->router->setRouteInfo(array('action' => 'actionRender', 'arguments' => $params,
            'template' => $routeInfo[2], 'queryParameters' => $_GET));
        Api_InterfaceAbstract::setLight();
        $this->sendAsJson(vB5_Template::staticRenderAjax($routeInfo[2], $params));
    }

routestring的值以/分割,最终将widget_php作为staticRenderAjax的参数之一,跟进staticRenderAjax

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public static function staticRenderAjax($templateName, $data = array())
	{
		$rendered = self::staticRender($templateName, $data, true, true);

		$css = vB5_Template_Stylesheet::instance()->getAjaxCssLinks();

		return array(
			'template' => $rendered,
			'css_links' => $css,
		);
	}

跟进staticRender

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public static function staticRender($templateName, $data = array(), $isParentTemplate = true, $isAjaxTemplateRender = false)
	{
		if (empty($templateName))
		{
			return null;
		}

		$templater = new vB5_Template($templateName);

		foreach ($data as $varname => $value)
		{
			$templater->register($varname, $value);
		}

		$core_path = vB5_Config::instance()->core_path;
		vB5_Autoloader::register($core_path);

		$result = $templater->render($isParentTemplate, $isAjaxTemplateRender);
		return $result;
	}

register$data数组中的键值对绑定到$this->registered中 然后调用render进行渲染,问题应该是出在渲染步骤,跟进render

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public function render($isParentTemplate = true, $isAjaxTemplateRender = false)
{
	...
    extract(self::$globalRegistered, EXTR_SKIP | EXTR_REFS);
	extract($this->registered, EXTR_OVERWRITE | EXTR_REFS);
    ...
	$templateCache = vB5_Template_Cache::instance();
	$templateCode = $templateCache->getTemplate($this->template);
	if(is_array($templateCode) AND !empty($templateCode['textonly']))
	{
		$final_rendered = $templateCode['placeholder'];
	}
	else if($templateCache->isTemplateText())
	{
		@eval($templateCode);
	}
	else
	{
		if ($templateCode !== false)
		{
			@include($templateCode);
		}
	}
	...
    
	return $final_rendered;
}

extract$this->registered中的值注册为了变量,那么$widgetConfig[code]的值就为system('whoami');exit;,然后getTemplate获取了模板,后续为getTemplate -> fetchTemplate

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
protected function fetchTemplate($templateName)
{
	if (is_array($templateName))
	{
		$method = 'fetchBulk';
		$arguments = array('template_names' => $templateName);
	}
	else
	{
		$method = 'fetch';
		$arguments = array('name' => $templateName);
	}
	if ($styleId = vB5_Template_Stylevar::instance()->getPreferredStyleId())
	{
		$arguments['styleid'] = $styleId;
	}
	$response = Api_InterfaceAbstract::instance()->callApi('template', $method, $arguments);
    
	...
}

callApi来获取模板值,后续为core/vb/api/template.php::fetch -> core/vb/library/template.php::fetch -> core/vb/library/template.php::fetchBulk

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public function fetchBulk($template_names, $styleid = -1, $type = 'compiled', $nopermissioncheck = false)
{
    ...
    $style = false;
	...
    
	foreach ($template_names AS $template)
	{
		...
		if (empty($style))
		{
			$style = vB_Library::instance('Style')->fetchStyleRecord($styleid, $nopermissioncheck);
			$templateassoc = $style['templatelist'];
		}
		//handle bad template names -- they should be blank by default.
		if (isset($templateassoc["$template"]))
		{
			$templateids[] = intval($templateassoc["$template"]);
		}
		else
		{
			// @todo: throw an exception if the template doesn't exist and we are in debug mode?
			$response[$template] = '';
		}
	}
	if (!empty($templateids))
	{
		$result = vB::getDbAssertor()->assertQuery('template', array('templateid' => $templateids));
		...
	}
	return $response;
}

根据$template_names获取$templateid,最终从数据库中获得模板内容 image.png image.png 查询出来的值为

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
$final_rendered = '' . ''; if (empty($widgetConfig) AND !empty($widgetinstanceid)) {
					$final_rendered .= '
	' . ''; $widgetConfig = vB_Template_Runtime::parseData('widget', 'fetchConfig', $widgetinstanceid); $final_rendered .= '' . '
';
				} else {
			$final_rendered .= '';
		}$final_rendered .= '' . '
' . ''; if (!empty($widgetConfig)) {
					$final_rendered .= '
	' . ''; $widgetid = $widgetConfig['widgetid']; $final_rendered .= '' . '
	' . ''; $widgetinstanceid = $widgetConfig['widgetinstanceid']; $final_rendered .= '' . '
';
				} else {
			$final_rendered .= '';
		}$final_rendered .= '' . '

<div class="canvas-widget default-widget custom-html-widget" data-widget-id="' . $widgetid . '" data-widget-instance-id="' . $widgetinstanceid . '">
	' . vB_Template_Runtime::includeTemplate('module_title',array('widgetConfig' => $widgetConfig, 'can_use_sitebuilder' => $user['can_use_sitebuilder'])) . '
	<div class="widget-content">
		<hr class="widget-header-divider" />
		' . ''; if (!empty($widgetConfig['code']) AND !vB::getDatastore()->getOption('disable_php_rendering')) {
					$final_rendered .= '
			' . ''; $evaledPHP = vB_Template_Runtime::parseAction('bbcode', 'evalCode', $widgetConfig['code']); $final_rendered .= '' . '
			' . $evaledPHP . '
		';
				} else {
			$final_rendered .= '
			' . ''; if ($user['can_use_sitebuilder']) {
					$final_rendered .= '
				<span class="note">' . vB_Template_Runtime::parsePhrase("click_edit_to_config_module") . '</span>
			';
				} else {
			$final_rendered .= '';
		}$final_rendered .= '' . '
		';
		}$final_rendered .= '' . '
	</div>
</div>';

vB_Template_Runtime::parseAction('bbcode', 'evalCode', $widgetConfig['code']),调用了bbcode::evalCode

1
2
3
4
5
6
7
8
	function evalCode($code)
	{
		ob_start();
		eval($code);
		$output = ob_get_contents();
		ob_end_clean();
		return $output;
	}

于是就执行了$widgetConfig['code']

参考

https://xz.aliyun.com/t/6419