刷刷先知,增长知识
复现
根据网上的poc脚本,本地测试了一波
https://seclists.org/fulldisclosure/2019/Sep/31
分析
根据routestring
全局搜索代码,会搜出很多。根据ajax/render
筛选出最终位置
后续将调用的hander
为callRender
,于是全局搜索
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
,最终从数据库中获得模板内容
查询出来的值为
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