CodeQL基础语法
基础QL语法
CodeQL的查询语法有点像SQL,如果你学过基本的SQL语句,基本模式应该不会陌生。
结构
1 | import java // 导入使用的库 |
import java
,导入使用的库,因为我们分析的项目是java的
from int i
,表示我们定义一个变量i,它的类型是int,表示我们获取所有的int类型的数据;
where i = 100
, 表示当i等于1的时候,符合条件;(这是=
是一个等于的意思 ==
,并不是赋值)
select i
,就是输出 i
我们经常会用到的ql类库大体如下:
名称 | 解释 |
---|---|
Method | 方法类,Method method表示获取当前项目中所有的方法 |
MethodAccess | 方法调用类,MethodAccess call表示获取当前项目当中的所有方法调用 |
Parameter | 参数类,Parameter表示获取当前项目当中所有的参数 |
所有方法:
1 | import java |
我们再通过Method类内置的一些方法,把结果过滤一下。比如我们获取名字为 CommandExec 的方法名称。
1 | import java |
谓词
和SQL一样,where部分的查询条件如果过长,会显得很乱。CodeQL提供一种机制可以让你把很长的查询语句封装成函数。
这个函数,就叫谓词。
比如上面的案例,我们可以写成如下,获得的结果跟上面是一样的:
1 | import java |
语法解释
predicate 表示当前方法没有返回值。
exists子查询,是CodeQL谓词语法里非常常见的语法结构,它根据内部的子查询返回true or false,来决定筛选出哪些数据。
设置Source和Sink
什么是source和sink
在代码自动化安全审计的理论当中,有一个最核心的三元组概念,就是(source,sink和sanitizer)。
source是指漏洞污染链条的输入点。比如获取http请求的参数部分,就是非常明显的Source。
sink是指漏洞污染链条的执行点,比如SQL注入漏洞,最终执行SQL语句的函数就是sink(这个函数可能叫query或者exeSql,或者其它)。
sanitizer又叫净化函数,是指在整个的漏洞链条当中,如果存在一个方法阻断了整个传递链,那么这个方法就叫sanitizer。
只有当source和sink同时存在,并且从source到sink的链路是通的,才表示当前漏洞是存在的。
设置Source
在CodeQL中我们通过这个格式去设置
1 | override predicate isSource(DataFlow::Node src) { |
例如
正常的Spring Boot 框架
1 |
|
本例中我们设置Source的代码为:
1 | override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource } |
这是SDK
自带的规则,里面包含了大多常用的Source入口。我们使用的SpringBoot也包含在其中, 我们可以直接使用。
注: instanceof 语法是CodeQL提供的语法,后面在CodeQL进阶部分我们会讲到。
设置Sink
在CodeQL中我们通过 isSink
1 | override predicate isSink(DataFlow::Node sink) { |
这里的 sink我们就正常的去定义一个construtorCall,然后这个construtorCall限定在processBuilder下就行。
codeql官方有一个ExternalProcess.qll库里面有一个ArgumentToExec类,这个类会覆盖到这个sink
那么就直接写一个
1 | override predicate isSink(DataFlow::Node sink) { |
这样就设置了一个命令执行的 Sink
Flow数据流
设置好Source和Sink后,如果一个受污染的变量,能够毫无阻拦的流转到危险函数,就表示存在漏洞。
比如如下代码:
1 | from VulConfig config, DataFlow::PathNode source, DataFlow::PathNode sink |
我们传递给config.hasFlowPath(source, sink)
我们定义好的source和sink,系统就会自动帮我们判断是否存在漏洞了。
这一段其实基本上都是固定的,都是从Source到sink,不必深究。
简单demo
1 | /** |
isSanitizer方法
从上面的demo中存在的一个数据,就是误报,这个参数其实是通过了安全函数过滤了,但是还是扫出来了,这种情况就得消除这种误报。就得使用isSanitizer
。
isSanitizer是CodeQL的类TaintTracking::Configuration
提供的净化方法。
1 | override predicate isSanitizer(DataFlow::Node node) { |
isAdditionalTaintStep方法
isAdditionalTaintStep方法是CodeQL的类TaintTracking::Configuration
提供的的方法,它的原型是:
1 | override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { |
它的作用是将一个可控节点
A强制传递给另外一个节点B,那么节点B也就成了可控节点。
批量检测命令
CodeQL除了提供VSCode的检测插件,也提供了大量的命令行,来实现项目的集成检测。
比如:
1 | codeql database analyze /Users/zy/Documents/project/codeql/vscode-codeql-starter-main/database/codeql_java-sec-code /Users/zy/Documents/project/codeql/vscode-codeql-starter-main/ql/java/ql/src/codeql-suites/java-security-extended.qls --format=csv --output=java-results.csv |
查询制定类的的方法
1 | import java |
根据Method name 和 interface name 查询
比如我想查询ContentTypeHandler
的所有子类toObject
方法
1 | import java |