1.1.基本语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/**
* 元数据:元数据作为QL文件注释的内容包含在每个查询文件的顶部
* 元数据告诉Vscode CodeQL插件如何处理查询并正确显示结果。他还向其它用户提供有关查询结果含义的信息
*
*/
/**
* @name 简单查询示例
* @kind problem
* @problem.severity warning
*/
@name:查询名称(显示在结果中)
@kind:problem(报告问题)、path-problem(路径问题)、table(表格结果)
@problem.severity:error / warning / recommendation
import java //import 导入模块
/** 这里可以自定义一些类(Class)和谓词(predicate) */
from //变量声明 :from子句:声明查询中使用的变量,声明格式为<类型><变量名>
where //定义一些逻辑条件,就像SQL查询中的where条件,提取出我们想要的结果
select //显示满足where子句的结果
|
from //变量声明 :from子句:声明查询中使用的变量,声明格式为<类型><变量名>
where //定义一些逻辑条件,就像SQL查询中的where条件,提取出我们想要的结果
select //显示满足where子句的结果
1
2
3
4
5
|
import java
from int x,int y
where x=6 and y=7
select "x与y相乘的结果:",x*y
|

1.2.谓词(函数)—predicate
谓词一词来自离散数学,谓词用来描述个体的性质或个体间关系的部分,封装自己的查询语法可以理解为程序的函数,谓词名称应以小写字母开头
如何定义谓词
1
2
3
4
5
6
7
8
9
10
|
无返回值谓词
predicate test(int a){
xxx
}
有返回值谓词
int test(int a){
xxx
result xxx
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import java
predicate demo(int i) {
//里面写查询的语法
i in [1..9]
}
from int x
where demo(x)
select x,"无返回值predicate demo"
cd D:\Java_Project\soft\mushroom
D:\tools\codeql\codeql.exe database create ^
"D:\Java_Project\soft\CodeQL\database\mushroom-db" ^
--language=java ^
--command="mvn clean compile" ^
--source-root=src
|

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
int demo1(int i){
result = i+1 and i in [1..9]
}
from int x
select demo1(x),"有返回值谓词 demo"
string demo2(string test){
test = "demo1" and result = "测试1"
or
test = "demo2" and result = "测试2"
or
test = "demo3" and result = "测试3"
or
test = "demo4" and result = "测试4"
}
select demo2("demo1")
|
类型—Type
类型是一个数值集合,例如类型int是整数的集合。一个值属于多个集合,这意味着它可以具有多个类型
Type类有两个直接派生类PrimitiveType,RefType
**PrimitiveType**代表Java中的基础数据类型,派生类有boolean/byte/char/double/float/int/long/short/void/null
**RefType**代表Java中的引用类型,有派生类Class,Interface,EnumType,Array
1
2
3
4
5
6
7
|
Reftype常见谓词
getACallable() ----获取所有可以调用方法(其中包括构造方法)
getAmember() ----获取所有成员,其中包括调用方法,字段,内部类这些
getAField() ----获取所有字段
getAmethod() ----获取所有方法
getAsupertype() ----获取父类
getAnAncestor() ----获取所有父类相当于递归的getAsupertype*()
|
1.3.类—Class
类(Class)是 CodeQL 中定义新类型和逻辑的主要方式。它不仅仅是数据容器,更是逻辑谓词的封装。
类是属于类型的一种,通过关键字class来实现,类不会创建对象,他只会表示逻辑属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class <ClassName> extends <SuperClass> {
<ClassName>() {
// characteristic predicate(特征谓词)
}
// 可选:额外谓词或属性
string getDescription() { ... }
}
// class为定义类的关键字,OneTwoThree为类名,extends表示该类是int的子类型
class OneTwoThree extends int {
OneTwoThree() { // 特征谓词,写法类似于构造函数
this = 1 or this = 2 or this = 3
}
string getAString() { // 成员谓词,类似于成员方法
result = "One, two or three: " + this.toString()
}
predicate isEven() { // 成员谓词,类似于成员方法
this = 2
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
/**
* @name Test DemoOne class
*/
import java
class DemoOne extends int {
DemoOne() {
this = [1 .. 10]
}
string getAll() {
result = "测试值: " + this.toString()
}
}
class DemoTwo extends DemoOne{
DemoOne d;
DemoTwo(){
this % d = 0
}
DemoOne getTest(){
result = 2 + d
}
}
from DemoTwo i
select i, i.getAll(),i.getTest()
|
Class类包含方法Method,Method包含字段Field
1.3.1.常用的类
1
2
3
4
5
6
7
8
|
---查询类的全限定名中包含user的类,其中getQualifiedName()方法代表获取类对应的全限定类名
//从类里面获取User
import java
from Class c
where c.getQualifiedName().indexOf("user") >= 0
select c.getQualifiedName()
|

根据类名就可以去对应代码段找到User

**Method:一个包含所有方法的集合 **
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
import java
from Method m
where m.hasName("Class") and
m.getDeclaringType().getASubtype().
hasQualifiedName(package, type)
//package ===> 项目的包名如:com.example.controller.admin
//type ====> 项目的类名:如 Useradd.java
select m
import java
from Method m
where m.hasName("index") and
m.getDeclaringType().getASubtype().
hasQualifiedName("org.marker.mushroom.controller", "AdminController.java")
select m
import java
// from Class c
// where c.getQualifiedName().indexOf("Assignment") >=0
// select c.getQualifiedName()
--查询所有名称为resetPassword的方法,且定义该方法的类或者其超类为Assignment7
from Method m
where m.hasName("resetPassword") and
m.getDeclaringType().getASubtype*
().hasQualifiedName("org.owasp.webgoat.lessons.challenges.challenge7",
"Assignment7")
select m
|

**Field: 一个包含所有字段的集合。 **
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
--查询所有字段Field,满足条件是字段类型是public,并且字段所属的类继承java.lang.Throwable。
(Fastjson1.2.80漏洞利用链的查找方式)。
import java
from Class c, Field f
where c.getASupertype*().hasQualifiedName("java.lang", "Throwable") and
f.getDeclaringType() = c and
f.getAModifier().getName() = "public"
select c.getQualifiedName(),f.getName()
--getASupertype代表获取类对应的父类,*代表递归查找所有父类。
--getDeclaringType代表获取字段对应的定义类型,即定义该字段的类。
--getAModifier代表获取字段对应的修饰符。
import java
//找出所有继承自 java.lang.Throwable(直接或间接)的类中,声明为 public 的字段(field)。
from Field f,Class c
where c.getASupertype*().hasQualifiedName("java.lang", "Throwable") and
f.getDeclaringType() = c and f.getAModifier().getName() = "public"
select c.getQualifiedName(),f.getName()
|

1.4.Access相关概念
access代表对变量或者方法的调用,主要有VarAccess和MethodAccess。
1.4.1. VarAccess
**代表对变量的调用。 **
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
--查询所有继承自java.util.list的变量及变量的引用。
import java
from RefType t,Variable v,VarAccess va
where t.getSourceDeclaration().getASourceSupertype*
().hasQualifiedName("java.util", "List") and
v.getType() = t and
va.getVariable() = v
select v,va
-- getSourceDeclaration获取该类型的源声明。对于泛型类型和原始类型的参数化实例,源声明是相应
的泛型类型。对于在泛型类型的参数化实例内声明的非参数化类型,源声明是泛型类型中的相应类型。对于所
有其他类型,源声明是类型本身。
-- getASourceSupertype获取该类型的直接超类型(不包括自身)的源声明。
-- getType 获得该变量所属的类型
-- getVariable 获取此变量调用所访问的变量。
|

1.4.2.MethodAccess
**代表对方法的调用 **
1
2
3
4
5
6
7
8
9
10
11
|
--查询所有InputStream类对应的readObject方法调用(遍历反序列化漏洞的基础)。
import java
from MethodAccess ma,Class c
where ma.getMethod().hasName("readObject") and
ma.getQualifier().getType() = c and
c.getASupertype*().hasQualifiedName("java.io", "InputStream")
select ma,ma.getEnclosingCallable()
-- getMethod 获取此方法调用所访问的方法。
-- getQualifier 获取该方法调用的限定表达式。
-- getEnclosingCallable 获取包含此方法调用的可调用方法
|
1.5.抽象语法树—AST
抽象语法树表示程序的语法结构。AST上的节点表示语句(Stmt)和表达式(Expr)等元素
1
2
3
4
5
6
7
|
Stmt
/**找到某个语句的父级为if语句*/
import java
from Stmt s
where s.getParent() instanceof IfStmt
select s
|

1
2
3
4
5
6
|
import java
// 查询所有父类为返回语句的表达式
from Expr e
where e.getParent() instanceof ReturnStmt
select e
|

或者选择ASTView可直接查看

1.6.数据流分析—FLow
在CodeQL中有四种数据流分析:本地数据流分析/全局数据流分析/本地污点分析/全局污点分析
**Flow数据流是**CodeQL中最重要的概念,代表数据流,与此对应的概念包括source和sink
**source**代表用户可控的输入点,通常指Web站点中输入的参数信息
**sink**代表危险的函数,通常指一些危险的操作,包括SQL注入,代码执行,JNDI注入,CodeQL也预置了部分的sink点
1.6.1.全局污点分析
**污点分析可以抽象成一个三元组的形式,其中,source即污点源,代表直接引入不受信任的数据或者机密数据到系统中,sink即污点汇聚点,代表直接产生安全敏感操作(违反数据完整性)或者泄露隐私数据到外界(违反数据保密性),sanitizer即无害处理,代表通过数据加密或者移除危害操作等手段使数据传播不再对软件系统的信息安全产生危害。 污点分析就是分析程序中由污点源引入的数据是否能够不经无害处理,而直接传播到污点汇聚点。 如果不能,说明系统是信息流安全的;否则,说明系统产生了隐私数据泄露或危险数据操作等安全问题。 **
** 我们可以自定义类继承 TaintTracking2::Configuration (新版本建议使用 TaintTracking2 代替 TaintTracking ),从而来进行污点分析 **
1
2
3
4
5
6
7
8
9
10
11
|
import semmle.code.java.dataflow.TaintTracking2
class MyTaintTrackingConfiguration extends TaintTracking2::Configuration {
MyTaintTrackingConfiguration() { this = "MyTaintTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
...
}
override predicate isSink(DataFlow::Node sink) {
...
}
}
|
**使用谓词 hasFlowPath(DataFlow::Node source, DataFlow::Node sink) 执行数据流分析: **
1
2
3
4
5
6
7
8
9
|
/**
* @kind path-problem
*/
import DataFlow2::PathGraph
from MyTaintTrackingConfiguration config, DataFlow2::PathNode source,
DataFlow2::PathNode sink
where config.hasFlowPath(source, sink)
select source.getNode(), source, sink, "danger!!!"
|