[WesternCTF2018]shrine
[WesternCTF2018]shrine
代码整理后得到
import flask
<!-- more -->
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)
第6行看到FLAG,用os.environ取出Linux系统中的FLAG变量赋值给了app.config
所以目标是读app.config
接着又定义了shrine/path:shrine,查看路由文件,很显然需要访问/shrine,flask模板,看看是否有模板注入,在路径后添加注入,{{7*7}}
因此存在SSTI漏洞。
就存在ssti漏洞。
这个值会经过一个黑名单和替换,过滤了(),config,self
构造payload,不适用黑名单内的字符,读取app.config
判断模板类型:
大概可以知道是jinja2或者Twig。一般读取config都是直接{{config}}就能读取到配置文件
但是这里过滤了config,只能通过其他入手。然而bypass黑名单内执行命令,requests对象绕过也不行。
在flask中有4中全局变量
1:current_app
代表当前flask程序实例
2:g
g作为flask程序全局的一个临时变量。充当媒介的功能
3:requests对象
客户端发送的HTTP请求内容
4:session
用户会话
在这题中有点关系的也就current_app了。因为g是个临时变量等会就没了。而current_app是flask的实例,所有和flask有关的都会放在current_app里面
python还有一个函数叫做url_for,其作用是url是用于构建指定函数的URL,再配合globals(),该函数会以字典类型返回当前位置的全部全局变量。这样也可以实现查看的效果
/shrine/{{url_for.__globals__}}
这样我们只需要能查看到这个的config就可以看到flag了,构造payload
/shrine/{{url_for.__globals__['current_app'].config}}