CodeQL学习笔记(一)
CodeQL学习笔记(一)
CodeQL简介
CodeQL是一种用于查找潜在漏洞和安全问题的查询语言。它可以帮助开发人员更轻松地检查代码,以确保它们的应用程序没有安全漏洞。CodeQL可以检查源代码,以及编译后的代码,以及可执行文件,以及其他形式的代码。它还可以检查源代码以外的元数据,例如文档和注释。
CodeQL历史
该项目的历史为:Semmle公司最早独创性的开创了一种QL语言,Semmle QL,并且运行在自家LGTM平台上。
LGTM平台上存放的就是一些开源项目,用户可以选择分析的语言,编写ql语句进行程序安全性查询。
2019年,GitHub为了解决其托管的海量项目的安全性问题,收购了Semmle公司,并宣布开源CodeQL的部分规则,这样全世界的安全工程师就可以贡献高效的QL审计规则给github,帮助它解决托管项目的安全问题,而对于安全人员也多了一个非商业的开源代码自动化审计工具。
环境配置
CodeQL本身包含两部分:解析引擎和SDK
安装解析引擎
解析引擎
用来解析我们编写的规则,虽然不开源,但是我们可以直接在官网下载二进制文件直接使用。
https://github.com/github/codeql-cli-binaries/releases/download/v2.12.2/codeql-win64.zip
将codeql.exe添加到环境变量PATH中
安装SDK
SDK
完全开源,里面包含大部分现成的漏洞规则,我们也可以利用其编写自定义规则。
解压,然后重命名为ql
安装插件
如下,windows的Executable path
要指定到codeql.exe
AST
抽象语法树(Abstract Syntax Tree,简称 AST
抽象语法树(abstract syntax tree,AST) 是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,这所以说是抽象的,是因为抽象语法树并不会表示出真实语法出现的每一个细节,比如说,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现。 抽象语法树并不依赖于源语言的语法,也就是说语法分析阶段所采用的上下文无文文法,因为在写文法时,经常会对文法进行等价的转换(消除左递归,回溯,二义性等),这样会给文法分析引入一些多余的成分,对后续阶段造成不利影响,甚至会使合个阶段变得混乱。因些,很多编译器经常要独立地构造语法分析树,为前端,后端建立一个清晰的接口。 抽象语法树在很多领域有广泛的应用,比如浏览器,智能编辑器,编译器。
生成AST数据库
运行CodeQL之前,需要将源码生成对应AST结构数据库
对于java这种需要编译的语言,加上--command="mvn clean install --file pom.xml"
,在pom.xml所在目录执行如下命令
1 | codeql database create codeql_java_sec_code --language=java --command="mvn clean install --file pom.xml" |
其中
codeql database create xxx
表示生成xxx数据库--language=java
表示指定程序语言为java--command="mvn clean install --file pom.xml"
表示编译(python和php无需执行)--source-root=path
指定项目路径(此处未使用)、
如下,生成了对应的AST数据库
在CodeQL插件中添加数据库
如下,添加成功
编写简单的hello world
vscode打开之前的ql文件夹(SDK),创建demo.ql,路径java\ql\examples\demo.ql
测试经典的hello world
1 | select "hello world" |
如下
语法
名称 | 解释 |
---|---|
Method | 方法类,Method method表示获取当前项目中所有的方法 |
MethodAccess | 方法调用类,MethodAccess call表示获取当前项目当中的所有方法调用 |
Parameter | 参数类,Parameter表示获取当前项目当中所有的参数 |
method.getName() | 获取的是当前方法的名称 |
method.getDeclaringType() | 获取的是当前方法所属class的名称 |
查找所有的方法
1 | import java |
谓词
个人理解为函数,比如要找方法commonHttpClient所在的类
1 | import java |
运行结果
其中
predicate 表示当前方法没有返回值。
exists子查询,它根据内部的子查询返回true or false,从而删选数据
污点分析
污点分析可以抽象成一个三元组<sources, sinks, sanitizers>的形式,
sources:污点源,代表直接引入不受信任的数据或者机密数据到系统中
sink:污点汇聚点,代表直接产生安全敏感操作或者泄露隐私数据到外界
sanitizer:无害处理,代表通过数据加密或者移除危害操作等手段使数据传播不再对软件系统的信息安全产生危害
只有当source和sink同时存在,并且从source到sink的链路是通的,才表示当前漏洞是存在的。
设置source
instance作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型
src instanceof RemoteFlowSource
表示src 必须是 RemoteFlowSource类型。RemoteFlowSource是在SDK中已经定义好的,包含了大多数常用的source节点。
1 | override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource } |
尝试对rce漏洞进行测试
设置sink
设置方法
1 | override predicate isSink(DataFlow::Node sink) {} |
可以将sink设置为eval函数的方法调用,代码如下
1 | /** |
运行结果
上面的ql代码,关键看exists子查询,如下
1 | exists(Method method, MethodAccess call | |
意思是将方法访问的具体方法设置为eval
,且将方法访问(也就是engine.eval(cmd, bindings)
)的第一个参数(也就是cmd
)作为sink点
具体的验证方法
1 | import java |
运行结果