介绍
模板其实就是对于传入的数据进行解析,然后生成html文件,然后在返回给浏览器进行展示,而这其中就用到了模板渲染和模板引擎
模板注入的根本原因就在于客户端向服务器传输的参数被解析,由于模板中有较多函数,所以,很多情况下模板注入可以导致远程代码执行,模板注入大概分为这几种:客户端模板注入、服务端模板注入、模板引擎注入。在本题中就是flask/jinja模板注入
0x01
拿到题目后发现给出了一段代码
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/')
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)
我们来分析一下这段代码
首先代码中定义了两个类
0x02
@app.route('/')
def index():
return open(__file__).read()
这个类的作用很简单,当访问/路径时就用来阅读文件内容
0x03
@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))
这个类通过flask模板返回一个值,这个值是经过处理的
执行这段代码的时候,会传入一个值给参数s,然后参数s进行替换,会将传进去的‘(’ 和 ‘)替换成’ ’ ,然后下面blacklist是黑名单,也就是说过滤了config,self关键字,如果没有过滤可以直接{{config}}即可查看所有app.config内容,但是这里不行
0x04
return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s
这段代码把黑名单的东西遍历并设为空
这里还有个知识点就是python的一些内置函数,url_for和get_flashed_messages,通过这些python的内置函数,我们可以读取config的一些信息
/shrine/{{url_for.__globals__}}
结果如下如:
然后我们观察一下有没有重要的有用的信息,然后发现了current_app,为什么重点关注这个呢?因为这个web是用了模板引擎的,那么我们就能想到很多模板具有注入,有客户端模板注入,服务端模板注入,模板引擎注入,所以这里我们要重点关注一下这个current_app,看看这个里边的信息有没有什么猫腻,所以接下来我们查看这个里面的config
0x05
/shrine/{{url_for.__globals__['current_app'].config}}