使用CodeQL寻找SSTI漏洞
污点追踪概述
污点追踪是指分析项目代码运行过程中可能存在安全隐患或者受污染
的数据的流动情况。
在代码自动化安全审计的理论中,有一个最核心的三元组概念,就是source
,sink
和sanitizer
- source 是指漏洞污染链条的输入点,比如获取http请求的参数部分
- sink 是指漏洞污染链条的执行点,比如SQL注入漏洞,最终执行SQL语句的函数就是sink
- sanitizer 又叫做净化函数,是指在整个漏洞链条当中,如果存在一个方法阻断了整个传递链,那么这个方法就叫sanitizer
回忆我们平时审计代码的过程,是否是source
和sink
同时存在,并且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
点
设置好了source
和sink
后,首尾如果能够连通,一个受污染的变量,能够流转到危险函数,才证明漏洞存在。连通工作是由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漏洞
参考链接
- https://rmb122.com/2020/03/30/Codeql-踩坑记录/
- https://rmb122.com/2020/03/31/Codeql-踩坑记录-二/
- https://www.freebuf.com/articles/web/283795.html
- https://codeql.github.com/docs/codeql-language-guides/using-api-graphs-in-python/
- https://codeql.github.com/docs/codeql-language-guides/analyzing-data-flow-in-python/
END
建了一个微信的安全交流群,欢迎添加我微信备注进群
,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注 😃