记录学习codeql的过程
看完了官方文档,感觉有点晦涩难懂,又看了下大佬们的文章,结合练习,算入了半个门
以一套简单的基于springboot靶场来学习
下面是一个简单的数据查询执行过程
可以很明显的发现存在sql注入
自动化代码审计的一些定义
source 指的是漏洞输入点 可以直接理解为入参点
sink 指的是漏洞执行点 比如sql注入 最终执行sql语句的函数就是sink
sanitizer 净化函数 用于过滤误报
只有当source和sink同时存在时,并且source和sink链路是通的 才表示当前漏洞存在
codeql一些定义和语法
ql语法很像sql语句,不过是条件在前 查询在后
import /* ... CodeQL libraries or modules ... */
/* ... Optional, define CodeQL classes and predicates ... */
from /* ... variable declarations ... */
where /* ... logical formula ... */
select /* ... expressions ... */
元数据
元数据用于告诉codeql cli如何处理查询结果
目前只用到了@kind
@description |
<text> |
用一段简单的话描述查询的目的以及查询结果的重要性。该描述以纯文本编写,并使用单引号(' )将代码括起来。 |
@id |
<text> |
是一串小写字母或数字,通过/ 或- 分隔构成的单词序列,用于识别和分类查询。每个查询必须具有唯一的 ID。为确保这一点,对每个ID使用固定的结构可能会有所帮助。例如,标准LGTM查询具有以下格式:<language>/<brief-description> 。 |
@kind |
|
标识查询是告警(@kind problem )还是路径(@kind path-problem )。有关这些查询类型的更多信息,请参见关于CodeQL查询。 |
@name |
<text> |
定义查询标签的语句。该名称以纯文本形式编写,并使用单引号(' )将代码括起来。 |
@tags |
|
这些标签将查询按大类分组在一起,以使其更易于搜索和识别。除了此处列出的常用标签外,还有许多更具体的类别。有关更多信息,请参阅《 查询元数据样式指南》。 |
@precision |
|
表示查询结果为“真肯定”(相对于“假肯定”结果)的百分比。这与@problem.severity 属性一起确定默认情况下是否在LGTM上显示结果。 |
@problem.severity |
|
定义查询生成的任何警报的严重性级别。这与@precision 属性一起确定默认情况下是否在LGTM上显示结果。 |
codeql类库
ifstmt //if分支 else if
Method //方法
PrimitiveType //传参类型
TopLevelType //引用类型
NestedClass //表示类
MethodAccess //調用方法
每个类库下面都有不同的方法可以调用
例子
codeql谓词
谓词通过predicate定义,通过定义谓词可以将复杂语句函数化
例子
override predicate isSource(DataFlow::Node node) {
node instanceof RemoteFlowSource
}
数据流分析(重点)
个人觉得codeql 数据流分析非常强大,下面是学习记录
本地数据流
本地数据流库位于模块中DataFlow
,它定义了Node
表示数据可以流经的任何元素的类。Node
s 分为表达式节点 ( ExprNode
) 和参数节点 ( ParameterNode
)。您可以使用成员谓词asExpr
和在数据流节点和表达式/参数之间进行映射asParameter
class Node {
/** Gets the expression corresponding to this node, if any. */
Expr asExpr() { ... }
/** Gets the parameter corresponding to this node, if any. */
Parameter asParameter() { ... }
...
全局数据流
全局数据流在整个程序中跟踪数据流,因此比本地数据流更强大。然而,全局数据流不如本地数据流精确,而且分析通常需要更多的时间和内存来执行。
通过扩展DataFlow::Configuration来使用全局数据流
import semmle.code.java.dataflow.DataFlow
class MyDataFlowConfiguration extends DataFlow::Configuration {
MyDataFlowConfiguration() { this = "MyDataFlowConfiguration" }
override predicate isSource(DataFlow::Node source) {
...
}
override predicate isSink(DataFlow::Node sink) {
...
}
}
全局污点跟踪 //这个算是用懂了
它里面有几个方法,目前只学到3个
isSource
isSink
isSanitizer
isAdditionalTaintStep
source //入口点
sink //爆发点
issanitizer //排除误报
继承该类后,通过hasflow方法来分析,codeql会自动分析source能不能走到sink
如何根据漏洞点写出一个sink
首先漏洞出发点是query函数 这算一个条件 然后他是jdbctemplate类的方法 这也算一个 触发参数是第一位,根据以上条件构造下面的sink点
override predicate isSink(DataFlow::Node node) {
exists(Method m,MethodAccess access | m.getDeclaringType().hasQualifiedName("org.springframework.jdbc.core", "JdbcTemplate") and access.getMethod()=m and m.hasName("query") and node.asExpr() = access.getArgument(0))
}
构造source点
override predicate isSource(DataFlow::Node node) {
node instanceof RemoteFlowSource //RemoteFlowSource代表外部输入
}
排除误报
override predicate isSanitizer(DataFlow::Node node) {
node.getType() instanceof NumberType or
node.getType() instanceof BoxedType or
node.getType() instanceof PrimitiveType or
exists(ParameterizedType pt | node.getType() = pt and pt.getTypeArgument(0) instanceof NumberType)
}
最终成品
/**
* @kind path-problem
*/
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSources
import java
import DataFlow::PathGraph
class VulConfig extends TaintTracking::Configuration {
VulConfig() { this = "VulConfig" }
override predicate isSource(DataFlow::Node node) {
node instanceof RemoteFlowSource
}
override predicate isSink(DataFlow::Node node) {
exists(Method m,MethodAccess access | m.getDeclaringType().hasQualifiedName("org.springframework.jdbc.core", "JdbcTemplate") and access.getMethod()=m and m.hasName("query") and node.asExpr() = access.getArgument(0))
}
override predicate isSanitizer(DataFlow::Node node) {
node.getType() instanceof NumberType or
node.getType() instanceof BoxedType or
node.getType() instanceof PrimitiveType or
exists(ParameterizedType pt | node.getType() = pt and pt.getTypeArgument(0) instanceof NumberType)
}
}
from VulConfig vul,DataFlow::PathNode source,DataFlow::PathNode sink
where vul.hasFlowPath(source, sink)
select source.getNode(), source, sink, "source"
原文始发于微信公众号(8ypass):codeql学习笔记1
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论