Smarty <= 3.1.32=""></=>

[ 复制链接 ]
admin | 2020-1-28 23:46:37 | 显示全部楼层 | 阅读模式 打印 上一主题 下一主题

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
smarty简介
smarty是一个php模板引擎,其项目地址:https://github.com/smarty-php/smarty。
smarty据有模板编译功能。当访问一个模板文件时,smarty会根据模板文件在设置的编译目录中生成对应的php脚本(即编译文件),此后若再次访问该模板文件时,倘若模板文件未更新,则smarty会直接读取第一次生成的php脚本,而不是重新生成另一个。倘若访问另一个模板,则会生成另一个新的php脚本(编译文件)
环境搭建
测试环境:linux

Smarty <= 3.1.32=""></=>

Smarty <=3.1.32=""></=>


根据commit信息,我们检出 6768340,此时漏洞还未修复。

               
               
                       
                        Default
                       
                       
git clone https://github.com/smarty-php/smarty.git
cd smarty\
git checkout 6768340
cd ..
                       
                              

                                        1234
                               
git clone https://github.com/smarty-php/smarty.git cd smarty\ git checkout 6768340 cd ..

                       
               
index.php:

               
               
                       
                        Default
                       
                       
php_functions = null;
$my_security_policy->php_handling = Smarty:HP_REMOVE;
$my_security_policy->modifiers = array();
$smarty->enableSecurity($my_security_policy);
$smarty->setCacheDir(SMARTY_CACHE_DIR);
$smarty->setCompileDir(SMARTY_COMPILE_DIR);
$smarty->registerResource('test',new test);
$smarty->display('test:'.$_GET['chybeta']);
?>
                       
                               

                                        1234567891011121314151617181920212223242526
                               
include_once('./smarty/libs/Smarty.class.php');define('SMARTY_COMPILE_DIR','/tmp/templates_c');define('SMARTY_CACHE_DIR','/tmp/cache'); class test extends Smarty_Resource_Custom{    protected function fetch($name,&$source,&$mtime)    {        $template = "CVE-2017-1000480 smarty PHP code injection";        $source = $template;        $mtime = time();    }} $smarty = new Smarty();$my_security_policy = new Smarty_Security($smarty);$my_security_policy->php_functions = null;$my_security_policy->php_handling = Smarty:HP_REMOVE;$my_security_policy->modifiers = array();$smarty->enableSecurity($my_security_policy);$smarty->setCacheDir(SMARTY_CACHE_DIR);$smarty->setCompileDir(SMARTY_COMPILE_DIR);$smarty->registerResource('test',new test);$smarty->display('test:'.$_GET['chybeta']);?>

                       
               

漏洞分析
参数通过$smarty->display('test:'.$_GET['chybeta']);传入,display定义在 smarty_internal_templatebase.php 中,它调用了 _execute。
_execute定义在libs\sysplugins\smarty_internal_compile_assign.phpsmarty_internal_templatebase.php的 156 行左右,在该方法定义中,也即整个文件的174行左右:

               
               
                       
                        Default
                       
                       
# smarty_internal_templatebase.php
# line about 175
$template = $smarty->createTemplate($template, $cache_id, $compile_id, $parent ? $parent : $this, false);
                       
                               

                                        1234
                               
# smarty_internal_templatebase.php# line about 175 $template = $smarty->createTemplate($template, $cache_id, $compile_id, $parent ? $parent : $this, false);

                       
               
会调用createTemplate方法,将我们的传入的参数创建成一个模板,

Smarty <= 3.1.32=""></=>

Smarty <=3.1.32=""></=>


接着会调用render方法,进行模板渲染。

               
               
                       
                        Default
                       
                       
# smarty_internal_templatebase.php
# line about 174
$result = $template->render(false, $function);
                       
                               

                                        1234
                               
# smarty_internal_templatebase.php# line about 174 $result = $template->render(false, $function);

                       
               
render方法定义在libs\sysplugins\smarty_template_compiled.php中,第105行开始对前面生成的模板进行处理:

               
               
                       
                        Default
                       
                       
# smarty_template_compiled
# line about 104
if (!$this->processed) {
    $this->process($_template);
}
                       
                               

                                        123456
                               
# smarty_template_compiled# line about 104 if (!$this->processed) {    $this->process($_template);}

                       
               
process方法定义在第131行。现在初次访问,也即文件的第138行会对模板文件进行编译,即如简介中所言开始生成编译文件:

               
               
                       
                        Default
                       
                       
if (!$this->exists || $smarty->force_compile ||
    ($smarty->compile_check && $source->getTimeStamp() > $this->getTimeStamp())
) {
    $this->compileTemplateSource($_smarty_tpl);
    $compileCheck = $smarty->compile_check;
    $smarty->compile_check = false;
    $this->loadCompiledTemplate($_smarty_tpl);
    $smarty->compile_check = $compileCheck;
}
                       
                               

                                        123456789
                               
if (!$this->exists || $smarty->force_compile ||    ($smarty->compile_check && $source->getTimeStamp() > $this->getTimeStamp())) {    $this->compileTemplateSource($_smarty_tpl);    $compileCheck = $smarty->compile_check;    $smarty->compile_check = false;    $this->loadCompiledTemplate($_smarty_tpl);    $smarty->compile_check = $compileCheck;}

                       
               
compileTemplateSource方法定义在同文件的第169行,在第181行装载完编译器后(loadCompiler()),调用write方法进行写操作:

               
               
                       
                        Default
                       
                       
public function compileTemplateSource(Smarty_Internal_Template $_template)
{
    ...
    try {
        // call compiler
        $_template->loadCompiler();
        $this->write($_template, $_template->compiler->compileTemplate($_template));
    }
    ...
                       
                               

                                        123456789
                               
public function compileTemplateSource(Smarty_Internal_Template $_template){    ...    try {        // call compiler        $_template->loadCompiler();        $this->write($_template, $_template->compiler->compileTemplate($_template));    }    ...

                       
               
跟入compileTemplate方法,定义libs\sysplugins\smarty_internal_templatecompilerbase.php第334行:

               
               
                       
                        Default
                       
                       
public function compileTemplate(Smarty_Internal_Template $template, $nocache = null,
                                Smarty_Internal_TemplateCompilerBase $parent_compiler = null)
{
    // get code frame of compiled template
    $_compiled_code = $template->smarty->ext->_codeFrame->create($template,
                                                                 $this->compileTemplateSource($template, $nocache,
                                                                                              $parent_compiler),
           &nbsp;                                                     $this->postFilter($this->blockOrFunctionCode) .
                                                                 join('', $this->mergedSubTemplatesCode), false,
                                                                 $this);
    return $_compiled_code;
}
                       
              &nbsp;                

                                        123456789101112
                               
public function compileTemplate(Smarty_Internal_Template $template, $nocache = null,                                Smarty_Internal_TemplateCompilerBase $parent_compiler = null){    // get code frame of compiled template    $_compiled_code = $template->smarty->ext->_codeFrame->create($template,                                                                 $this->compileTemplateSource($template, $nocache,                                                                                              $parent_compiler),                     &nbsp;                                           $this->postFilter($this->blockOrFunctionCode) .                                                                 join('', $this->mergedSubTemplatesCode), false,                                                                 $this);    return $_compiled_code;}

                       
               
create是生成编译文件代码的方法,定义在libs\sysplugins\smarty_internal_runtime_codeframe.php第28行,为显示变量情况,这里我加了一句var_dump

Smarty <= 3.1.32=""></=>

Smarty <=3.1.32=""></=>


在第44行,在生成output内容时有如下代码:

               
               
                       
                        Default
                       
                       
$output .= "/* Smarty version " . Smarty::SMARTY_VERSION . ", created on " . strftime("%Y-%m-%d %H:%M:%S") .
                   "\n  from \"" . $_template->source->filepath . "\" */\n\n";
                       
                               

                                        12
                               
$output .= "/* Smarty version " . Smarty::SMARTY_VERSION . ", created on " . strftime("%Y-%m-%d %H:%M:%S") .                   "\n  from \"" . $_template->source->filepath . "\" */\n\n";

                       
               
将 $_template->source->filepath的内容直接拼接到了$output里。这段代码是为了生成编译文件中的注释,$output的头尾有注释符号/*和*/。

Smarty <= 3.1.32=""></=>

Smarty <=3.1.32=""></=>


现在考虑如何利用,我们需要闭合前面的注释符号,即payload的最前面需要加上*/。同时还要把后面的*/给注释掉,可以在payload最后加上//。中间填上php代码即可。另外需要注意的是,在win平台下,文件名中不允许有*,而smarty框架的生成的编译文件的名字会含有我们的payload,所以在win下时会直接提示创建文件失败。
在linux平台下即可利用成功。

Smarty <= 3.1.32=""></=>

Smarty <=3.1.32=""></=>


漏洞修补
查看commit记录:https://github.com/smarty-php/smarty/commit/614ad1f8b9b00086efc123e49b7bb8efbfa81b61
添加了过滤,将可能闭合的*/变为* /:

Smarty <= 3.1.32=""></=>

Smarty <=3.1.32=""></=>


在另外几处文件中也进行了过滤,要求只能出现字母和数字:

               
               
                       
                        Default
                       
                       
substr(preg_replace('/[^A-Za-z0-9.]/','',$source->name),0,25);
                       
                               

                                        1
                               
substr(preg_replace('/[^A-Za-z0-9.]/','',$source->name),0,25);

                       
               

题外话
直接看生成的编译文件,会发现有两个输出点,第二个输出点在单引号内,但这个无法逃逸。在libs\sysplugins\smarty_internal_runtime_codeframe.php的第47行,使用的是var_export来导出变量内容的值:

               
               
                       
                        Default
                       
                       
$dec = "\$_smarty_tpl->_decodeProperties(\$_smarty_tpl, " . var_export($properties, true) . ',' .
       ($cache ? 'true' : 'false') . ")";
$output .= "if ({$dec}) {\n";
                       
                               

                                        123
                               
$dec = "\$_smarty_tpl->_decodeProperties(\$_smarty_tpl, " . var_export($properties, true) . ',' .       ($cache ? 'true' : 'false') . ")";$output .= "if ({$dec}) {\n";

                       
               

Smarty <= 3.1.32=""></=>

Smarty <=3.1.32=""></=>


而如漏洞修补一节中所言,添加过滤后,引号会被直接去除。

作者:chybeta
温馨提示:
1、在论坛里发表的文章仅代表作者本人的观点,与本网站立场无关。
2、论坛的所有内容都不保证其准确性,有效性,时间性。阅读本站内容因误导等因素而造成的损失本站不承担连带责任。
3、当政府机关依照法定程序要求披露信息时,论坛均得免责。
4、若因线路及非本站所能控制范围的故障导致暂停服务期间造成的一切不便与损失,论坛不负任何责任。
5、注册会员通过任何手段和方法针对论坛进行破坏,我们有权对其行为作出处理。并保留进一步追究其责任的权利。
回复 推荐到N格

使用道具 举报

大神点评(1)

您需要登录后才可以回帖 登录 | 立即注册
    云凌阁
    阿月减肥 | 2020-6-25 19:51:40 | 显示全部楼层
    确实是好帖啊,顶先!
    回复 支持 反对

    使用道具 举报

    相关推荐
    云凌阁

    关注1

    粉丝0

    帖子94526

    发布主题