01 基础数据类型
int、float、date、string、boolean
1.1 date
#计算指定天数
from date start,date end #变量定义
where start = "01/01/2023".toDate() and end = "01/10/2023".toDate()#逻辑公式
select start.daysTo(end) #表达式
结果

1.2 boolean
from boolean a,boolean b
where a = true and b = false
select a.booleanAnd(b) #与运算 booleanOr booleanXor booleanNot
结果

02 Predicates(谓词)
类似编程语言中的函数,但同时与函数又有一点不同。
开头必须是小写字母
绑定行为与绑定集
无结果返回 — 无结果谓词
#判断是否是动物
predicate isAnimal(string animal) {
animal = "dog"
or
animal = "cat"
}
from string animal
where animal = "dog" and isAnimal(animal)
select animal
结果返回 — 结果谓词
#某个数加1 参数满足在1-10之间
int addOne(int i){
result = i + 1 and
i in [1 .. 10]
}
from int x
where x = 1
select addOne(x)
反向查找 — 递归谓词
string getNeighbor(string city){
city = "China" and result = "nanjing"
or
city = "China" and result = "suzhou"
or
city = "China" and result = "France"
or
city = "France" and result = "suzhou"
or
city = getNeighbor(result)
}
from string people
where people = getNeighbor("suzhou")
select people
结果

特征谓词、非成员谓词、成员谓词
类之外定义的,不是任何类成员 - 非成员谓词
类中定义 - 成员谓词
其他类中的构造函数 - 特征谓词
//非成员谓词
int getNumber(int i){
result = i + 1 and
i in [1 .. 9]
}
//特征谓词
class FavoriteColor extends string{
FavoriteColor(){
this = "red" or
this = "green" or
this = "black"
}
//成员谓词
string getColor(){
this = "red" and result = "10"
or
this = "green" and result = "20"
or
this = "black" and result = "30"
}
}
绑定行为与绑定集合
谓词描述的集合不允许是无线的,智能包含有限数量的元组。
//正解
int addTwo(int i){
result = i + 2 and
i in [1 .. 10]
}
//错误
int addTwo(int i){
result = i + 1 and
i > 0
}
单个绑定集合。
bindingset//绑定集合
int addTwo(int i){
result = i + 2 and
i > 0
}
from int i
where i = 1
select addTwo(i)
多个绑定集合。
bindingset[x] bindingset[y]
predicate plusOne(int x, int y) {
x + 1 = y
}
from int x,int y
where x = 100 and plusOne(x,y)
select y
03 Query
from int x,int y
where x = 3 and y in [0 .. 2]
select x,y,x * y as product,"result:" + product
查询谓词
query int getId(int x,int y){
x = 3 and
y in [0 .. 2] and
result = x * y
}
class MultipleOfThree extends int{
MultipleOfThree(){ this = getId(,)}
}
from MultipleOfThree m
select m
04 Class
类似java代码
class Animal extends string{
Animal(){
this = "Dog" or this = "Cat" or this = "Bird"
}
string getAString(){
result = "Hi, " + this.toString()
}
predicate isCat(){
this = "Cat"
}
}
类主体
class RangeInt extends int{
RangeInt() {this = [1 .. 10]}
}
class DivisibleInt extends RangeInt{
RangeInt divisor;
DivisibleInt(){ this % divisor = 0}
RangeInt getADivisor(){ result = divisor}
}
from DivisibleInt i
select i,i.getADivisor()
CodeQL - Java
method.getName 获取当前方法的名称
method.getDeclaringType 获取的是当前方法所属class的名称
getASupertype 获取类对应的父类,*代表递归查找所有父类
getAModifier 获取字段对应的修饰符
hasQualifiedName 指定包中声明了此类型
asExpr 获取与此节点对应的表达式
asParameter 获取与此节点对应的参数表达式
- 查找指定类
Class相关
1.1 查询类中包含Person类
import java
from Class c
where c.getQualifiedName().indexOf("Person") >= 0
select c.getQualifiedName()
1.2 查询所有字段Field,满足条件是字段类型是public,并且字段类型继承java.lang.Throwable
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()
Access相关
1.3 查询所有继承java.util.list的变量以及变量的引用
RefType 引用类型
PrimitiveType 基础数据类型
import java
from RefType t,Variable v,VarAccess va
where t.getSouceDeclaration().getASourceSupertype*().hasQualifiedName("java.util","List") and v.getType() = t and va.getVariable() = v
select v,va
1.4 查询所有InputStream类对应的readObject方法调用
getEnclosingCallable 获取表达式的封闭可调用项。
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(0)
1.5 查询函数参数
getArgument
import java
#该方法只能查到函数调用位置
from MethodAccess call,Method method
where method.hasName("toObject") and
method.getDeclaringType().getASupertype().hasQualifiedName("org.apache.stuts2.reset.handler","ContentTypeHandler") and call.getMethod() = method
select call
#获取函数参数
...
select call.getArgument(0) #获取函数第一个参数
1.6 指定对象满足某种条件
exists( | ),满足formula返回true,否则返回false
exists(Method method |
method.hasName("toObject") and method.getDeclaringType().getAnAncestor().hasQualifiedName("org.apache.struts2.rest.handler", "ContentTypeHandler") and source.asParameter() = method.getParameter(0)
数据流(污点分析)
source - 命令行参数args属于source
sink - 危险函数(命令执行、代码执行、jndi注入、SQL注入、XML注入)
数据分析流要继承DataFlow::Configuration这个类,重载isSource和isSink方法
典型用法(自定义Source与Sink)
class MyConfig extends DataFlow::Configuration{
MyConfig(){this = "MyConfig"}
//输入流
override predicate isSource(DataFlow::Node source){
...
}
//危险函数
override predicate isSink(DataFlow::Node sink){
...
}
}
from Myconfig config,DataFlow::Node source,DataFlow::Node sink
where config.hasFlow(source,sink)
select source,sink
2.1 Struts漏洞(CVE-2017-9805)
用户控制的输入直接传到XStream.fromXML,造成反序列化漏洞。
Struts有个contenttypehandler interface方法toObject(Reader in,Object target)
数据流分析从toObject->in->fromXML->MethodAccess->argument
漏洞函数调用:
ContentTypeInterceptor.java->selector.getHandlerForRequest(request)->handler.toObject(reader,target)->xstream.fromXML(in,target)->unmarshal(创建HashMap)->populateMap->PutCurrentEntryIntoMap->readItem(获取map中的元素)>AbstractReflectionConverter.java->
2.1.1.1 寻找toObject函数
import java
from Method method
where method.hasName("toObject")
select method,method.getDeclaringType()#定义的class name列举,返回类名
#ContentTypeHandler的所有子类的toObject方法
#getASupertype
where method.hasName("toObject") and
method.getDeclaringType().getASupertype().hasQualifiedName("org.apache.struts2.reset.handler","ContentTypeHandler")
#getAnAncessor ContentTypeHanlder多了一个
where method.hasName("toObject") and
method.getDeclaringType().getAnAncestor().hasQualifiedName("org.apache.struts2.reset.handler","ContentTypeHandler")
2.1.1.2 寻找fromXML函数
import java
from Method method
where method.hasName("fromXML") and
method.getDeclaringType().hasQualifiedName("com.thoughtworks.xstream","XStream")#执行定包,类名
select method
2.2 污点分析
import java
import semmle.code.java.dataflow.DataFlow
class MyConfig extends DataFlow::Configuration {
MyConfig() { this = "MyConfig" }
override predicate isSource(DataFlow::Node source) {
exists(Method method |
method.hasName("toObject") and method.getDeclaringType().getAnAncestor().hasQualifiedName("org.apache.struts2.rest.handler", "ContentTypeHandler") and source.asParameter() = method.getParameter(0)
)
}
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess call, Method method |
method.hasName("fromXML") and method.getDeclaringType().hasQualifiedName("com.thoughtworks.xstream", "XStream") and call.getMethod() = method and sink.asExpr() = call.getArgument(0)
)
}
}
from StrutsUnsafeDeserializationConfig config, DataFlow::Node source, DataFlow::Node sink
where config.hasFlow(source, sink)
select source, sink
05总结
CodelQL作为自动化漏洞挖掘重要利器,再漏洞挖掘过程发挥了非常大的作用,后期将测试如何挖掘物联网设备存在的各式漏洞。
TIPS
CodeQL查询报错 缺少qlback.yml文件
选择SDK中ql/java/ql/examples文件创建ql文件