技术社区
安全培训
技术社群
积分商城
先知平台
漏洞库
历史记录
清空历史记录
相关的动态
相关的文章
相关的用户
相关的圈子
相关的话题
注册
登录
CodeQL规则编写之常用类与特殊情况
K9weirdo
历史精选
592浏览 · 2025-03-24 07:27
返回文档
关于CodeQL的使用与规则编写许多师傅都写了文章进行分享,但是关于编写任何规则时经常会用到的一些类和谓词,以及代码分析时可能会出现的常见特殊情况(漏报、误报、断链等)的分析文章相对较少。所以我在使用新版本Codeql时总结了一下相关的内容写了一篇文章,欢迎各位师傅继续补充内容。下面内容主要基于Java语言项目。
前言
Codeql
中的
Java
类库
关于
Codeql
中的
Java
类库,我按照继承关系简易列了一下
Codeql
中的常用且核心的一些类的继承图
下面总结了其中一些用的较多的类以及谓词,其余没列举的多为子类或者顶层父类。
PS
:如果遇到不熟悉的类一定要多阅读
Codeql
官方文档
以及
官方规则库
常用类及谓词
1、Expr类
作用
Expr
类用于处理
Java
语言(以及其他编程语言)中的表达式。这包括了从简单的变量引用、字面量到复杂的方法调用和运算表达式等,在
Java
语言中,表达式是由变量或常量与符号的组合。
Expr
是分析程序中运算和数据流动的
关键组件
。
常用子类
该类的子类继承了父类的大部分谓词,下面列举了一些常用的特征性比较明显的子类
●
LambdaExpr:该类
表示
隐式类实例创建的表达式(
Lambda 表达式)
,这些表达式实例化一个匿名类,该类将覆盖由其函数接口类型指定的唯一方法。asMethod()是该类常用谓词,用于获取与当前 lambda 表达式对应的隐式方法。
●
ClassInstanceExpr:ClassInstanceExpr类代表了 Java 代码中的
类实例化表达式
,通常是使用new关键字创建对象的表达式。它具体表示构造器调用,从而创建了一个新的类实例。
getConstructor()是该类常用谓词,用于获取该类实例创建时调用的构造函数。
●
ConditionalExpr:ConditionalExpr类用来表示形如
a ?b :c
的条件表达式。
getCondition()、getTrueExpr()和getFalseExpr()是该类常用谓词,分别表示此条件表达式的条件、
此条件表达式的条件计算结果为 true和
false
时计算的表达式
常用谓词
示例代码
上面示例使用如下规则查询
定义的一个表达式
Expr e
,则
e
结果如下
1
、
getEnclosingCallable()
获取出现此表达式的最近可调用对象(如果存在),返回的结果为
Callable
类型
则
e.getEnclosingCallable()
的结果如下
2
、
getEnclosingStmt()
用于获取包含当前表达式的最近的语句。可以理解表达式在代码中的位置以及它是如何被使用的
语句可以是任何形式的执行单元,例如
赋值语句、返回语句、调用语句、条件语句
等。返回的结果为
Stmt
类型
则
e.getEnclosingStmt()
的结果如下
3
、
getControlFlowNode()
将表达式与其在控制流图中的节点相关联。这对于进行控制流分析和数据流分析非常关键。返回的结果为
ControlFlowNode
类型
则
e.getControlFlowNode()
结果如下
4
、
getParent()
返回包含此表达式的更高级别的语法结构(如另一个表达式或一个语句)。返回的结果为
ExprParent
类型
此处
e.getParent()
结果如下
5
、
getAChildExpr()
返回方法调用的子表达式。返回的结果为
Expr
类型
则
e.getAChildExpr()
结果如下,为一个
MethodCall
则
e.getAChildExpr().getAChildExpr()
的结果存在三个,如下
6
、
getType()
获取此表达式的类型,返回的结果为
Type
类型
则
e.getType()
结果为
7
、
toString()
用于将表达式转换成一个字符串,常用于进行某种指定的判断。返回的结果为
string
类型
如果写
where
时发现某些判断调节不好写,可以尝试用
toString()
将条件转为字符串进行等号比较
同理,此处
e.toString()
的结果为
e
的字符串结果
2、Variable类
作用
Variable
类代表
Java
程序中的变量,包括
局部变量、字段(即类成员变量)、参数
等。这个类是对变量的抽象,使得分析变量的定义、使用和作用域变得可行。
常用子类
该类的子类继承了父类的大部分谓词,下面列举了一些常用的特征性比较明显的子类
●
LocalVariableDecl:该类
表示
局部变量
的声明
●
LocalScopeVariable:LocalScopeVariable类本地范围的变量,即局部
变量或参数
。
常用谓词
示例代码
上面示例使用如下规则查询
上面示例查询时均定义
Variable v
,
则
v
如下
1
、
getAnAssignedValue()
用于返回
给变量分配的任意一个值
的表达式。返回的结果为
Expr
类型
如
v.getAnAssignedValue()
结果如下
2
、
getAnAccess()
返回 任意一个
访问、调用、读取或写入此变量
的表达式,想要分析
所有
访问特定变量的位置,可以使用该谓词。返回的结果为
Expr
类型
如
v.getAnAccess()
结果如下
3
、
getInitializer()
如果变量在声明时被初始化,则返回
这个初始化表达式
。这对于分析字段或局部变量的初始状态非常有用。返回的结果为
Expr
类型
如
v.getInitializer()
结果如下
4
、
getName()
返回变量的名称。这个谓词非常实用,尤其在你需要根据变量的名称进行特定的检查或分析时。返回的结果为
string
类型
此处
v.getName()
的结果是
"result"
3、Annotation 类
作用
主要用来表示代码中的注解(
Annotations
),如
Java
代码里的
@Override
、
@RequestMapping
等,
CodeQL
将源代码中的注解抽象为
Annotation
对象,能够对其属性、参数和位置进行访问和分析。
该类继承
Expr
类
常用方法
示例代码
上面示例使用如下规则查询
定义
Annotation a
,则
a
结果如下
这里我们选择
@RequestHeader
进行后续分析
1
、
getType()
获取此注解的注解类型声明,返回的结果为
AnnotationType
类型
则
a.getType()
的结果如下
2
、
getValue(name)
该谓词可以直接
获取具有指定
名称
的注解元素的值,也同时包括未显式指定对应值时的默认值。
返回结果为
Expr
类型
则
a.getValue("name")
和
a.getValue("required")
结果如下,注意,此处的率筛选出来的
name
的值是包括引号的
"testId"
3
、
getAnnotatedElement()
该谓词返回的是被注解的元素,返回结果为
Element
类型
则此处
a.getAnnotatedElement()
值如下
4、Callable
作用
Callable
指一个可调用对象,
包括一个方法或构造函数
。这类是对可调用对象的抽象,另外两个常用的类
Method
和
Constructor
类均继承于该类,并且继承了该类的大部分的谓词
常用谓词
示例代码
上面示例使用如下规则查询
定义
Callable ca
,则
ca
结果如下
1
、
getAnAnnotation()
获取应用于此可调用对象(如:方法)的注释,也包括继承的注释。其中只包括直接注释,不包括间接注释,返回的结果为
Annotation
类型。
Method
和
Constructor
类可用。
则上述代码中
ca.getAnAnnotation()
的结果如下
2
、
getAParameter()
用于返回此可调用对象的形参,返回的结果为
Parameter
类型。
Method
和
Constructor
类可用。
则上述代码中
ca.getAParameter()
的结果如下,存在两个结果
3
、
getDeclaringType()
用于获取在其中声明此可调用对象的所在类型,返回的结果为
RefType
类型。
Method
和
Constructor
类可用。
则上述代码中
ca.getDeclaringType()
的结果如下
4
、
getACallee()
用于获取当前可调用对象可以调用的可调用对象。例如:如果当前可调用对象是一个方法的话,可以获取当前方法中的被调用的那个方法本身。返回的结果为
Callable
类型。
Method
和
Constructor
类可用。
则上述代码中
ca.getACallee()
的结果如下
5
、
getQualifiedName()
获取此可调用对象的完整限定名称,适用于规则调试。但常见使用过程中条件判断时,建议使用
ca.getDeclaringType().hasQualifiedName(
包名,类名
)
。
Method
和
Constructor
类可用。
则此处输出的结果为
字符串
com.example.demo.controller.FileOperateController.readFile6
5、Call 类
作用
Call
类代表的是程序中所有的
可调用对象的调用
,包括方法的调用、构造函数和超级构造函数的调用,以及通过类实例化调用的构造函数的调用。
另外两个常用的类
MethodCall
和
ConstructorCall
类均继承于该类,并且继承了该类的一部分的谓词
常用谓词
示例代码
上面示例使用如下规则查询
定义
Call c
,则
c
结果如下
1
、
getCallee()
获取当前调用当中的可调用对象。返回的结果为
Callable
类型。
Method
和
Constructor
类可用。
则
c.getCallee()
的结果为
2
、
getCaller()
获取调用当前调用的可调用对象。返回的结果为
Callable
类型。
Method
和
Constructor
类可用。
则
c.getCaller()
的结果为
3
、
getAnArgument()
这个谓词返回方法调用中的任意一个参数。返回的结果为
Expr
类型。
当你不需要关注特定参数的位置,但想要
分析所有可能的参数
时可以使用该谓词。
Method
和
Constructor
类可用。
则
c.getAnArgument()
的结果如下,存在两个结果
4
、
getQualifier()
返回
进行方法调用的对象
(如果存在的话)。返回的结果为
Expr
类型。
Method
和
Constructor
类可用。
则
c.getQualifier()
结果为
5
、
getEnclosingCallable()
返回包含这个方法调用的
最近的可调用实体
(例如方法或构造函数),该谓词继承于
Expr
类。返回的结果为
Callable
类型。
Method
和
Constructor
类可用。
则
c.getEnclosingCallable()
结果为
6、MethodCall 类
作用
MethodCall
类代表的是程序中所有的
方法调用
,该类继承于上面的
Call
类与
Expr
类
常用谓词
示例代码
上面示例使用如下规则查询
定义
MethodCall mc
,则
mc
结果如下
除了继承了父类中的常用谓词,还有如下
MethodCall
类特有的
谓词
1
、
getMethod()
用于返回
被调用的方法
本身。返回的结果为
Method
类型
则
mc.getMethod()
结果为
2
、
getArgument(i)
返回方法调用中的第
i
个参数。
返回的结果为
Expr
类型
则
mc.getArgument(1)
结果为
3
、
getType()
获取调用方法的返回结果的类型。返回的结果为
Type
类型
此处
mc.getType()
的结果是
String
4
、
getReceiverType()
获取当前方法调用中方法的限定符的类型,如果没有限定符,则获取封闭类型。返回的结果为
RefType
类型
此处
mc.getReceiverType()
的结果如下
7、ConstructorCall 类
作用
ConstructorCall
类代表
Java
代码中的构造器调用。这通常包括使用
new
关键字创建对象的实例,以及在类的构造器内部可能出现的对其他构造器的调用(例如,通过
this()
或
super()
)
该类继承于上面的
Call
类与
Expr
类,专门用于表示与构造器调用相关的表达式。
常用谓词
示例代码
上面示例使用如下规则查询
定义
ConstructorCall coc
,则
coc
结果如下
除了继承了父类中的常用谓词,还有如下
MethodCall
类特有的
谓词
1
、
getConstructedType()
返回由构造器调用创建的对象的类型,用于确定新创建的对象的类型。返回的结果为
RefType
类型
此处
coc.getConstructedType()
获取的为创建的
File
对象
2
、
getConstructor()
返回被调用的构造函数。返回的结果为
Constructor
类型
此处
coc.getConstructor()
获取的为
java.io.File#File
构造函数
常见的特殊情况
在针对项目编写
CodeQL
规则时,经常碰到数据流中断的情况。下面根据我碰到的部分情况以及其他师傅们碰到的情况来整理一下到底哪些情况会导致污点数据的传播断掉
全版本通用情况
一、第三方包调用
在
CodeQL
官方
Github
中有个
issues
:
https://github.com/github/codeql/issues/6729
。其中提到,在进行流和污点分析时,
CodeQL
仅分析通过用户代码的污点路径。对第三方包的调用被视为黑盒,也就是无法预测的,因此
CodeQL
不会将这个过程连接起来。
所以
CodeQL
官方提供了一个可以实现并描述这些数据如何传入和传出的谓词
isAdditional{Flow|Taint}Step
,用于用户自定义的将不同节点连接起来。
关于第三方包调用的话主要就两种情况
1
污点作为参数传播到第三方包的函数调用的返回结果中,即
a = "
污点
"; b = jar. Func(a)
2
污点作为参数传播到第三方包的类中实例化成一个对象,即
a = "
危险
"; B b = new Jar(a)
下面简单举例说明一下,在复杂场景下也是通用的
(1)
传播到第三方包的参与函数调用
如下图,其中CheckPathLegal是我自己定义的Check包中的类,其中的handlePath方法被用来对一个path路径进行处理(PS:此处将../置空的处理方式依旧存在安全问题)
存在漏洞的
readFile9
方法中,将
filePath
传入
CheckPathLegal
类的
handlePath
方法中进行处理,并且将处理后的
filePath
返回,再调用
new File()
生成
File
对象后,再将其传入
FileReader
方法中,这种情况,
file
对象应该是受污染的
但是默认情况下,
CodeQL
进行代码分析时,只会分析用户的代码,对于第三方包的方法调用被将认为是不可预测的。所以
CodeQL
无法扫描出结果
可以看到有问题的
readFile9
方法并没有被扫描出来
由于在
CheckPathLegal.
handlePath
(filePath)
处数据流就已经断开了。那么就需要将这两个节点连接起来,这里我们使用isAdditionalFlowStep进行连接
可以看到,将断开的两个节点连起来后,
CodeQL
就可以找到存在漏洞的
readFile9
方法了
isAdditionalFlowStep谓词的内容如下:
(2)
传播到第三方包的类中进行实例化
如下图,其中
CreateFile
是我自己定义的
Check
包中的类,用来创建一个
File
对象以及获取该对象
存在漏洞的
readFile7
方法中,将
filePath
传入
new CreateFile()
中创建
createFile
对象,再调用
createFile
对象的
getFile
方法
获取到
File
对象后,再将其传入
FileReader
方法中,这种情况,
file
对象应该是受污染的
依旧和上面一样,默认情况下,对于第三方包的方法调用下,
CodeQL
无法扫描出结果
可以看到有问题的
readFile7
方法并没有被扫描出来
因此,想让数据流进入
FileReader
中是不行的,因为在
new CreateFile ()
和
getFile()
方法就已经断开了。那么就需要将这两个节点连接起来
可以看到,将断开的两个节点连起来后,
CodeQL
就可以找到存在漏洞的
readFile7
方法了
isAdditionalFlowStep
谓词的内容
如下:
二、
fastjson
com.alibaba.fastjson
库在国内产品十分常用,用于处理
JSON
数据,它可以便捷地将
Java
对象转换为
JSON
字符串,或者将
JSON
字符串转换为
Java
对象。
JSONObject
表示一个
JSON
对象,其
是
fastjson
中的一个类。
JSONObject#get
方法常用于获取一个
Object
,
getString
常用于获取一个字符串,还有其他如
getIntValue
、
getDouble
等方法
有如下的代码案例
上面代码中其中
findPersonById2
方法存在SQL注入问题
我们使用codeql规则进行扫描,发现
getPersonById4
方法可以被检测出,
getPersonById5
对应的Controller被漏报了
这是因为数据流在
String id = jsonRequest.getString("id");
的时候断流了,但是
String id = (String) jsonRequest.get("id");
处并没有断。
这是因为
com.alibaba.fastjson.JSONObject
实现了 java.util.Map 接口,JSONObject#get 方法其实实际是调用 java.util.Map#get 方法
并且
Codeql
官方为该接口
get
方法进行了建模。
getString
等方法特定于
JSONObject
方法实现,不来自它实现的任何接口,因此该处会存在断流问题。
因此我们就需要将
String id = jsonRequest.getString("id");
所在的这两个节点连接起来,使用isAdditionalFlowStep进行连接
isAdditionalFlowStep谓词的内容如下:
三、
HashMap
问题
由于Codeql的限制,目前的java库中的DataFlow库在跟踪流时无法很好的区分HashMap键。使用一个键在 map 中存储一个值,然后在使用另一个 key 从 map 中获取值会造成误报问题。
有如下的代码案例
此处不存在
SQL
注入问题,因为
ids
参数中每一项都使用的了预编译,
order
键的值直接使用
asc
常量,并未使用外部传入的参数
但是使用codeql官方规则java/ql/src/experimental/Security/CWE/CWE-089/MyBatisMapperXmlSqlInjection.ql可以检测出此处存在SQL注入问题,这是一个误报
根据CodeQL官方Github中的issue。目前官方已经知道该限制,后期会有解决方案
历史版本存在的情况
一、Lombok问题
开发使用 Lombok 的代码通过注解极大地简化了样板代码。但是由于Lombok的工作原理和底层机制,Lombok的代码会在编译期间会使用注释处理器转换为对应的有效Java代码,老版本的CodeQL分析器会在源代码转为有效Java代码前查找源代码,因此会导致CodeQL无法访问到Lombok自动生成的代码,这可能导致使用了Lombok情况下存在的潜在漏洞也无法被检测出来。
但是该问题在codeql-cli-2.14.4版本及之后已经被解决,新版本支持直接解析使用 Lombok 的项目
该问题官方给出过解决方案,
在
CodeQL
官方
Github
中有个
issues
:
https://github.com/github/codeql/issues/4984
其中提到,Lombok 无法与 CodeQL Java 分析器一起使用,但是可以在运行 CodeQL 之前先“delombok”源文件
除此之外有一些师傅们已经给出了其他比较好的解决方法,加上目前新版Codeql-cli已经支持解析lombok,这里不再过多赘述。
二、配置文件问题
该问题相比上面的lombok问题其实也在更早些的版本,官方就已经出了解决方案
该问题codeql-cli- 2.7.2版本及之后已经默认会将XML等格式文件打包进数据库中并且建立索引
虽然官方已经解决了这个问题,但是我们依旧可以来看一下Codeql的pre-finalize.cmd文件和环境变量对打包数据库的影响
(1)pre-finalize
pre-finalize文件是codeql在创建数据库过程中,用于将build过程中生成的trap文件导入数据库以及为数据库建立索引时用到的脚本文件,win上为pre-finalize.cmd,linux、macos上为pre-finalize.sh
新版本的
CodeQL
默认会将
XML
等文件打包进数据库中,但是也会要求所有的配置文件总大小不超过
50mb
,如果超过
50mb
的话,并执行
indexbyname
中的操作,也就是只默认打包
AndroidManifest.xml, pom.xml, struts.xml, web.xml
这四个文件
因此我们可以直接修改indexbyname中的内容如下
旧版本的
CodeQL
中也可以做如上修改
(2)环境变量 LGTM_INDEX_XML_MODE
通过将环境变量 LGTM_INDEX_XML_MODE 设置为all ,Codeql在打包时就会执行indexall的内容,可以直接提取单个文件大小 10MB 以下的所有文档并打包,而不管所有文件总大小如何
通过下面命令设置对应环境变量
Linux
上:
export LGTM_INDEX_XML_MODE='ALL'
Win
上:
set LGTM_INDEX_XML_MODE=ALL
打包指定格式的文件
如果我想打包指定格式的文件并且建立索引,我们应怎么实现呢
在
CodeQL
官方的
issues
中多处提到了设置指定的
LGTM_INDEX_FILETYPES
环境变量
因此,想要在打包时将指定格式的文件也建立索引,就可以设置
LGTM_INDEX_FILETYPES
,如
set LGTM_INDEX_FILETYPES=.json:JSON .yaml:YAML
.yml:YAML
0
人收藏
1
人喜欢
转载
分享
0
条评论
某人
表情
可输入
255
字
评论
发布投稿
热门文章
1
从零掌握java内存马大全(基于LearnJavaMemshellFromZero复现重组)
2
突破网络限制,Merlin Agent助你轻松搭建跳板网络!
3
从白帽角度浅谈SRC业务威胁情报挖掘与实战
4
基于规则的流量加解密工具-CloudX
5
从0到1大模型MCP自动化漏洞挖掘实践
近期热点
一周
月份
季度
1
从零掌握java内存马大全(基于LearnJavaMemshellFromZero复现重组)
2
突破网络限制,Merlin Agent助你轻松搭建跳板网络!
3
从白帽角度浅谈SRC业务威胁情报挖掘与实战
4
基于规则的流量加解密工具-CloudX
5
从0到1大模型MCP自动化漏洞挖掘实践
暂无相关信息
暂无相关信息
优秀作者
1
T0daySeeker
贡献值:38700
2
一天
贡献值:24800
3
Yale
贡献值:21000
4
1674701160110592
贡献值:18000
5
1174735059082055
贡献值:16000
6
Loora1N
贡献值:13000
7
bkbqwq
贡献值:12800
8
手术刀
贡献值:11000
9
lufei
贡献值:11000
10
xsran
贡献值:10600
目录
前言
Codeql中的Java类库
常用类及谓词
1、Expr类
2、Variable类
3、Annotation 类
4、Callable
5、Call 类
6、MethodCall 类
7、ConstructorCall 类
常见的特殊情况
全版本通用情况
一、第三方包调用
(1)传播到第三方包的参与函数调用
(2) 传播到第三方包的类中进行实例化
二、fastjson
三、HashMap问题
历史版本存在的情况
一、Lombok问题
二、配置文件问题
(1)pre-finalize
(2)环境变量 LGTM_INDEX_XML_MODE
打包指定格式的文件
转载
标题
作者:
你好
http://www.a.com/asdsabdas
文章
转载
自
复制到剪贴板