codeQL入门
https://github.com/SummerSec/learning-codeql
文档
https://codeql.github.com/docs/writing-codeql-queries/
安装
官网
分析引擎:
https://github.com/github/codeql-cli-binaries/releases
这里下载的 v2.20.6
下载插件并设置

之后设置一下环境变量
规则库
https://github.com/github/codeql

下载对应版本
创建数据库
codeql database create D:\CyberSecurity\codeQL\databases\chestnutCMS --language="java" --command="mvn clean install --file pom.xml" --source-root=D:\Code\target\ChestnutCMS-1.4.4 --overwrite
codeQL分析jar包
https://xz.aliyun.com/news/16918
codeQL入门
https://www.youtube.com/watch?v=nvCd0Ee4FgE
https://www.bilibili.com/video/BV1zT421y7Eu
基本语法
基本数据类型
| 整型 | int |
| 浮点型 | float |
| 日期型 | date |
| 布尔型 | boolean |
| 字符串 | string |
结构
类似sql查询
from ... where ... select ...
举例
import java
from IfStmt ifStmt, BlockStmt blockStmt
where blockStmt = ifStmt.getThen() and blockStmt.getNumStmt() = 0
select ifStmt, "The if statement has an empty block."
// 查询if语句then部分为空的代码块

谓词 (函数)
在where子句中用于限定的限定词, 类似于函数, 提高查询逻辑复用性
import java
predicate isEmpty(BlockStmt block) {
block.getNumStmt() = 0
}
from IfStmt ifStmt
where isEmpty(ifStmt.getThen())
select ifStmt, "If statement has an empty then branch"
查询结果与上面相同
类
用于扩展提供的ql class, 代表符合某种逻辑的值
下面的例子定义了一个Main类, 继承了java中的Method类, 用来查找所有的main方法
import java
class EmptyBlock extends BlockStmt {
EmptyBlock() {
this.getNumStmt() = 0
}
}
from IfStmt ifStmt
where ifStmt.getThen() instanceof EmptyBlock
select ifStmt, "This if statement has an empty block as its then branch."
这里虽然看起来像是一个类和他的构造函数, 但是并非如此
this指针不是指向当前, 而是父类(这里的Method)
CodeQL语法规定声明的类必须是大写字母开头,其中和类名名称相同的方法为特征谓词,特征谓词中的this代表父类而不是和java一样代表本身,我们在特征谓词中加我们的逻辑,比如名字是main。
每个类都必须继承一个父类,父类的值就是子类的初始值集,一般自定义的类都会根据需要继承库提供的类,比如例子中的Method类,一个类也可以同时继承多个类,代表同时满足父类逻辑的值。
使用一个emptyBlock的变量来比较也是可以的
import java
class EmptyBlock extends BlockStmt {
EmptyBlock() {
this.getNumStmt() = 0
}
}
from IfStmt ifStmt, EmptyBlock emptyBlock
where ifStmt.getThen() = emptyBlock
select ifStmt, "This if statement has an empty block as its then branch."
一个漏洞触发的基本要素
- source 可控的输入
- path 经过的中间路径
- sink 漏洞最终触发点
以struts2-052为例
struts2-052 数据库下载
https://github.com/githubsatelliteworkshops/codeql/blob/master/java.md
Section A
查询所有方法调用
import java
from MethodCall mc
select mc, "Hello world"

这里最新版用MethodCall, 而不是MethodAccess
查询方法调用对应的方法定义
import java
from MethodCall mc, Method method
where mc.getMethod() = method
select mc, method

MethodCall是方法调用的集合, Method是方法定义的集合
查询名为fromXML方法的调用
import java
from MethodCall mc
where mc.getMethod().getName() = "fromXML"
select mc, "fromXML method found"
import java
from MethodCall mc, Method method
where mc.getMethod() = method and
method.getName() = "fromXML"
select mc, method

查询XStream.fromXML反序列化方法第一个参数
import java
from MethodCall mc
where mc.getMethod().getName() = "fromXML" and
mc.getCaller().getName() = "toObject"
select mc, mc.getArgument(0)
MethodCall 具有 getArgument(int i) 方法, 查询其第i个参数, 返回类型为Expr
Expr是所有参数的集合
import java
from MethodCall mc, Expr arg
where mc.getMethod().getName() = "fromXML" and
mc.getArgument(0) = arg
select mc, arg
将以上查询改写为一个谓词
import java
predicate isXMLDeserialized(Expr arg) {
exists(MethodCall fromXML |
fromXML.getMethod().getName() = "fromXML" and
arg = fromXML.getArgument(0)
)
}
from Expr arg
where isXMLDeserialized(arg)
select arg
其中exsits的格式
exists(变量 | 约束条件)

Section B
查找 ContentTypeHandler 中 toObject 方法的实现
import java
class ContentTypeHandler extends RefType {
ContentTypeHandler() {
this.hasQualifiedName("org.apache.struts2.rest.handler", "ContentTypeHandler")
}
}
from RefType r, ContentTypeHandler c
where r = c
select r
所有类、接口、枚举、数组 都是引用类型,所以它们都属于 RefType

查询ContentTypeHandler 的类上的名为 toObject 的 Method及其参数
import java
class ContentTypeHandler extends RefType {
ContentTypeHandler() {
this.hasQualifiedName("org.apache.struts2.rest.handler", "ContentTypeHandler")
}
}
class ContentTypeHandlerToObject extends Method {
ContentTypeHandlerToObject() {
this.getDeclaringType().getASupertype() instanceof ContentTypeHandler and
this.hasName("toObject")
}
}
from ContentTypeHandlerToObject toObject
select toObject, toObject.getParameter(0)
**this.getDeclaringType().getASupertype() instanceof ContentTypeHandler**
**this.getDeclaringType()**获取当前方法所属的类(即它在哪个类中定义)。**getASupertype()**获取该类的一个父类。**instanceof ContentTypeHandler**检查这个 类是否是ContentTypeHandler。
Section C # 污点跟踪
Java代码示例
int func(int tainted) {
int x = tainted;
if (someCondition) {
int y = x;
callFoo(y);
} else {
return x;
}
return -1;
}
其中参数在数据库中的位置与关系可以由一个有向图表示

此图表示从污染参数开始的数据流。图的节点代表具有值的程序元素,例如函数参数和表达式。图的边代表通过这些节点的数据流。
新版中, DataFlow::Configuration已经弃用, 改为以下格式
https://github.blog/changelog/2023-08-14-new-dataflow-api-for-writing-custom-codeql-queries/
module MyConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node n) {
none()
}
predicate isSource(DataFlow::Node n) {
none()
}
}
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
class ContentTypeHandler extends RefType {
ContentTypeHandler() {
this.hasQualifiedName("org.apache.struts2.rest.handler", "ContentTypeHandler")
}
}
class ContentTypeHandlerToObject extends Method {
ContentTypeHandlerToObject() {
this.getDeclaringType().getASupertype() instanceof ContentTypeHandler and
this.hasName("toObject")
}
}
predicate isXMLDeserialized(Expr arg) {
exists(MethodCall fromXML |
fromXML.getMethod().getName() = "fromXML" and
arg = fromXML.getArgument(0)
)
}
// from ContentTypeHandlerToObject toObject
// select toObject, toObject.getParameter(0)
module StrutsXMLDeserializationConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) {
exists(Expr arg |
isXMLDeserialized(arg) and
sink.asExpr() = arg
// 将数据流中的节点转为Expr与arg进行比较
)
}
predicate isSource(DataFlow::Node source) {
exists(ContentTypeHandlerToObject toObject |
source.asParameter() = toObject.getParameter(0)
)
}
}
module MyFlow = TaintTracking::Global<StrutsXMLDeserializationConfig>;
from MyFlow::PathNode source, MyFlow::PathNode sink
where MyFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Sink is reached from $@.", source.getNode(), "here"


浙公网安备 33010602011771号