• 使用CodeQL寻找SSTI漏洞


    污点追踪概述

    污点追踪是指分析项目代码运行过程中可能存在安全隐患或者受污染的数据的流动情况。

    在代码自动化安全审计的理论中,有一个最核心的三元组概念,就是source,sinksanitizer

    • source 是指漏洞污染链条的输入点,比如获取http请求的参数部分
    • sink 是指漏洞污染链条的执行点,比如SQL注入漏洞,最终执行SQL语句的函数就是sink
    • sanitizer 又叫做净化函数,是指在整个漏洞链条当中,如果存在一个方法阻断了整个传递链,那么这个方法就叫sanitizer

    回忆我们平时审计代码的过程,是否是sourcesink同时存在,并且source -> sink 的链路是通的,才表示当前漏洞是存在的

    CodeQL中进行污点追踪

    这里给出一个简易版的flask SSTI漏洞的代码

    from flask import Flask, request,render_template, render_template_string
    
    app = Flask(__name__)
    app.config['SECRET_KEY'] = '\xca\x0c\x86\x04\x98@\x02b\x1b7\x8c\x88]\x1b\xd7"+\xe6px@\xc3#\\'
    
    
    @app.route('/ssti')
    def ssti():
        if request.values.get('name'):
            name = request.values.get('name')
            template = "<p>{name}<p1>".format(name=name)
            return render_template_string(template)
    
            # template = Template('<p>%s<p1>' %name)
            # return template.render()
    
            # template = "<p>{{ name }}<p1>"
            # return render_template_string(template, name=name)
        else:
            return render_template_string('<p>输入name值</p>')
    
    if __name__ == '__main__':
        app.run()
    

    我们直接使用全局污点追踪TaintTracking::Configuration

    官方文档中的模板为

    import python
    
    class MyTaintTrackingConfiguration extends TaintTracking::Configuration {
      MyTaintTrackingConfiguration() { this = "..." }
    
      override predicate isSource(DataFlow::Node source) {
        ...
      }
    
      override predicate isSink(DataFlow::Node sink) {
        ...
      }
    }
    

    我们通过isSource(DataFlow::Node source)方法来设置source,在一个典型的flask应用中,思考一下我们的source是什么。显然,作为一个web应用,source理所当然是用户的输入,在flask中有很多种方式获取用户的输入,例如:request.form.get('title')或者request.values.get('name')

    CodeQL中封装的RemoteFlowSource表示来自远程网络输入的数据流,也就是用户的网络输入,里面也包括了flask的输入,我们直接调用即可

    override predicate isSource(DataFlow::Node source) {
            source instanceof RemoteFlowSource
    }
    

    这里我们暂且把instanceof理解为这种类型的意思

    上述代码的sink在哪里呢,显然是

    return render_template_string(template)

    这里使用codeql中的API图进行定义

        override predicate isSink(DataFlow::Node sink) {
            exists(DataFlow::CallCfgNode call |
                call = API::moduleImport("flask").getMember("render_template_string").getACall() and
                sink = call.getArg(0)
              )
        }
    

    exists子查询,是CodeQL谓词语法里非常常见的语法结构,它根据内部的子查询返回true or false,来决定筛选出哪些数据。

    将存在render_template_string函数调用的第一个参数作为sink

    设置好了sourcesink后,首尾如果能够连通,一个受污染的变量,能够流转到危险函数,才证明漏洞存在。连通工作是由CodeQL引擎本身来完成的,我们使用其中的config.hasFlow(src, sink)方法来判断即可

    整体代码如下

    import python
    import semmle.python.dataflow.new.DataFlow
    import semmle.python.dataflow.new.TaintTracking
    import semmle.python.dataflow.new.RemoteFlowSources
    import semmle.python.Concepts
    import semmle.python.ApiGraphs
    
    class SSTIVulConfig extends TaintTracking::Configuration {
        SSTIVulConfig() { this = "RemoteToFileConfiguration Tracking" }
    
        override predicate isSource(DataFlow::Node source) {
            source instanceof RemoteFlowSource
        }
    
        override predicate isSink(DataFlow::Node sink) {
            exists(DataFlow::CallCfgNode call |
                call = API::moduleImport("flask").getMember("render_template_string").getACall() and
                sink = call.getArg(0)
              )
        }
    }
    
    from SSTIVulConfig config, DataFlow::Node src, DataFlow::Node sink
    where config.hasFlow(src, sink)
    select src,sink
    

    运行查询最终得到SSTI漏洞

    参考链接

    END

    建了一个微信的安全交流群,欢迎添加我微信备注进群,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注

    GIF GIF
  • 相关阅读:
    centos 7修改时间为中国时间
    颜色表及html代码
    前端日志监控体系
    测试开发必备技能:安全测试漏洞靶场实战
    Jmeter(三)----win10系统下如何修改Jmeter字体大小?
    Jmeter(二) ---Jmeter英文版本修改为中文
    Jmeter(一) ---Windows环境搭建
    跟着高手学复盘_初步理解
    小测试对 HTTP/2 的了解
    软件测试入坑建议
  • 原文地址:https://www.cnblogs.com/Cl0ud/p/15959434.html
Copyright © 2020-2023  润新知