用户
搜索

[web安全] SSTI分析与防御

  • TA的每日心情
    开心
    2022-1-12 17:18
  • 签到天数: 42 天

    连续签到: 1 天

    [LV.5]常住居民I

    i春秋-见习白帽

    Rank: 3Rank: 3

    8

    主题

    73

    帖子

    2225

    魔法币
    收听
    0
    粉丝
    0
    注册时间
    2018-7-19
    发表于 2021-12-31 15:07:30 0854

    SSTI分析与防御

    SST(模板引擎)

    模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档

    ---百度百科

    ​   模板引擎用于使用动态数据呈现内容。此上下文数据通常由用户控制并由模板进行格式化,以生成网页、电子邮件等。模板引擎通过使用代码构造(如条件语句、循环等)处理上下文数据,允许在模板中使用强大的语言表达式,以呈现动态内容。如果攻击者能够控制要呈现的模板,则他们将能够注入可暴露上下文数据,甚至在服务器上运行任意命令的表达式。

    ​   通过模板可以将输入转换成特定的格式,在HTML网页中即使没有内容,但是有预先定义好的变量,通过访问该变量转换成想要的内容。

    image-20211115200306057

    image-20211115200405805

    当我们没有传入参数的时候,是默认的guest,当我们对该网址进行请求:

    http://127.0.0.1:5000/?name=aaa

    image-20211115200508474

    模板渲染

    • 前端渲染
    • 后端渲染
    • 浏览器渲染

    模板只是一种提供给程序来解析的一种语法,换句话说,模板就是用于从数据(变量)到实际的视觉表现(HTML代码)这项工作的实现手段,在前后端都有应用

    ​   个人理解:就是将数据交给模板处理,然后让渲染引擎去将数据生成为HTML的文本,返回给浏览器

    后端渲染

    后端渲染的话,浏览器会直接将接收到HTML字符串,这中间是由服务器经过解析存放在服务器端的模板文件来完成的,这种情况下,浏览器只是进行了HTML的解析,以及显示给用户

    前端渲染

    浏览器从后端得到信息,将信息组织并排列形成HTML代码,然后进行显示

    Flask SSTI漏洞

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

    基础认识

    • Flask

      • 是一个用Python编写的Web应用程序框架。Flask基于Werkzeug WSGI工具包和Jinja2模板引擎。
    • WSGI

      • Web Server Gateway Interface(Web服务器网关接口,WSGI)已被用作Python Web应用程序开发的标准。 WSGI是Web服务器和Web应用程序之间通用接口的规范。
    • Werkzeug

      • 它是一个WSGI工具包,它实现了请求,响应对象和实用函数。 这使得能够在其上构建web框架。 Flask框架使用Werkzeug作为其基础之一。
    • Jinja2

      • jinja2是Python的一个流行的模板引擎。Web模板系统将模板与特定数据源组合以呈现动态网页。
    • render_tempalte()

      • 用来渲染一个指定的文件的
      • return render_tempalte('index.html')
    • render_template_string()

      • 用来渲染一个字符串的

      • html = '<h1>This is index page</h1>'
        return render_template_string(html)
    • 模版

      • Flask 使用Jinja2作为渲染引擎
      • Jinja2模板使用以下分隔符从HTML转义
      • {% ... %}用于语句
      • {{ ... }}用于表达式可以打印到模板输出
      • {# ... #}用于未包含在模板输出中的注释
      • #... #用于行语句
    • Flask Request对象

      • 来自客户端网页的数据作为全局请求对象发送到服务器。为了处理请求数据,应从Flask模块导入
      • 重要属性:
      • Form    -   字典对象,包含表单参数及其值得键和值对
      • args    -   解析查询字符串的内容,它是问号(?)之后的URL的一部分。
      • Cookies - 保存Cookie名称和值的字典对象。
      • files- 与上传文件有关的数据。
      • method - 当前请求方法。
      • 在渲染模板时,不需要手动分配,可以直接在模板中使用的模板变量及函数:configrequesturl_for()get_flashed_messages()
    • 魔法方法

      • 列举CTF中的SSTI注入常用的
      • __dict__: 保存类实例或对象实例的属性变量键值对字典
      • __class__:返回调用的参数类型
      • __mro__:返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析
      • __bases__:返回类型列表
      • __subclasses__:返回object的子类
      • __init__:类初始化方法
      • __globals__:函数会以字典类型返回当前位置的全部全局变量,等同于func_globals

    看完上面的几个知识点再去看上面的代码,当浏览器去请求的时候,render_tempalte()函数的第一个参数为渲染目标的HTML字符串、第二个参数为需要加载到字符串指定标签位置的内容

    image-20211118162448341

    ​   

    ​   正常的访问:

    image-20211118162556545

    当我们给name一个指定的值

    image-20211118162638041

    他的结果就会是这样,参考上面讲到的渲染部分

    漏洞测试

    ​   那么可以测试获取配置

    image-20211118162802345

    可以看到他不仅将a渲染出来了,而且将配置参数也渲染出来了。

    获取整个person 字典中的内容

    ​   由于在模板中使用的是 % 字符串模板,所以它对任何传递给 python 表达式的内容进行了求值。

    Payload:

    {%for%20item%20in%20person%20%}{{item,%20person[item]}}%20{%endfor%}

    image-20211118162956783

    {%endfor%}必须加,否则出错。可以看到,整个 person 字典中的内容全被显示在页面中。

    为什么需要服务器模板

    ​   为了方便,用模板引擎进PHP或者Python等与HTML代码进行分离。服务器端模板提供了更加简单的方法管理动态生成的HTML代码,最大的优点就是可以在服务器动态生成HTML界面。

    什么是服务器模板注入

    Server-Side Template Injection SST信任了用户的输入,并且执行这些内容,包括执行本机函数。就像 eval 函数对传入的内容未加任何过滤一样。因此模板注入很容易导致远程代码执行(RCE)、信息泄露等漏洞。

    ​   SSTI是获取一个用户可控的输入点,在后端的渲染处理上进行拼接。就像开头那一部分

    $twig = new Twig_Environment(new Twig_Loader_String());  // Twig是php里面的一个模板引擎
    
    $output = $twig->render("Hello {$_GET['name']}");  // 将用户输入作为模版内容的一部分
    
    echo $output;   //输出在网页上
        // 该案例使用了PHP的Twig模板,上面用的Python jinja2模板

    ​      

    ​   使用Twig模板引擎渲染页面,因为其中模板有{{name}}变量,其模板变量值来自于GET请求参数$_GET['name'],这是一个用户输入点,可以对其进行控制;可以输出传递的变量之外,还可以执行基本表达式,然后将其结果作为模板的值,示例

    image-20211115202405600

    ​   每一个==模板引擎都有着自己的语法==,Payload要根据具体情况构造。
    ​   简单来说,就是更改请求参数使之承载含有模板引擎语法的 Payload,通过页面渲染返回的内容检测承载的 Payload 是否有得到编译解析,有解析则可以判定含有 Payload 对应模板引擎注入,否则不存在 SSTI。

    from flask import Flask
    from flask import request
    from flask import render_template_string
    from flask import render_template
    
    app = Flask(__name__)
    
    @app.route('/login')
    def hello_ssti():
        person = {
            'name': 'hello',
            'secret': '7d793037a0760186574b0282f2f435e7'
        }
        if request.args.get('name'):
            person['name'] = request.args.get('name')#获取查询参数name的值
    
        template = '<h2>Hello {{ person.name }}!</h2>'
    
        return render_template_string(template, person=person)
    
    if __name__ == "__main__":
        app.run(debug=True)

    ​   使用Jinja2中的render_template_string模版引擎渲染页面,其中模版含有{{ person.name }}变量,其模版变量值来自于 GET 请求参数request.args.get('name')

    ​   上述代码中,进行XSS的话,模板引擎一般都默认对渲染的变量值进行编码和转义,但是在第17行,将其改为用户可控

    ​   修改为template = '<h2>Hello %s!</h2>' % person['name'] # 插入到返回值

    ​   由于修改为用户控制的了,那么在构建模板时,拼接了用户输入作为模板的内容,如果向服务端直接传递JavaScript代码,输入的内容原样输出。

    BUUCTF--[CISCN2019 华东南赛区]Web11

    访问网址

    image-20211115203454688

    一开始啥也看不出来,直到看到文中有个X-Forwarded-For,于是去抓包

    image-20211115203630635

    image-20211115203920023

    image-20211115203944702

    当我们在X-Forwarded-For里传入值,他的网页上的IP也会更改,尝试SSTI

    image-20211115204004935

    image-20211115203754442

    可行

    直接读取Flag

    image-20211115204037217

    payload:

    X-Forwarded-For: {{system("cat ../../../../../../flag")}}

    防御

    ​   可以像使用eval()函数一样处理字符串加载功能,尽可能加载静态模板文件。也要防范本地文件包含,该功能类似于require()函数调用,不允许用户控制此类文件或者内容的路径

    参考链接

    发新帖
    您需要登录后才可以回帖 登录 | 立即注册