codeql学习笔记1

admin 2021年11月4日23:24:58评论171 views字数 3958阅读13分11秒阅读模式

记录学习codeql的过程


看完了官方文档,感觉有点晦涩难懂,又看了下大佬们的文章,结合练习,算入了半个门


以一套简单的基于springboot靶场来学习


下面是一个简单的数据查询执行过程


codeql学习笔记1

codeql学习笔记1


codeql学习笔记1


可以很明显的发现存在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

problem
path-problem

标识查询是告警(@kind problem)还是路径(@kind path-problem)。有关这些查询类型的更多信息,请参见关于CodeQL查询。
@name <text> 定义查询标签的语句。该名称以纯文本形式编写,并使用单引号(')将代码括起来。
@tags

correctness
maintainability
readability
security

这些标签将查询按大类分组在一起,以使其更易于搜索和识别。除了此处列出的常用标签外,还有许多更具体的类别。有关更多信息,请参阅《 查询元数据样式指南》。
@precision

medium
high
very-high

表示查询结果为“真肯定”(相对于“假肯定”结果)的百分比。这与@problem.severity属性一起确定默认情况下是否在LGTM上显示结果。
@problem.severity

error
warning
recommendation

定义查询生成的任何警报的严重性级别。这与@precision属性一起确定默认情况下是否在LGTM上显示结果。



codeql类库

ifstmt //if分支 else ifMethod //方法PrimitiveType //传参类型TopLevelType //引用类型NestedClass //表示类MethodAccess //調用方法

每个类库下面都有不同的方法可以调用


例子

codeql学习笔记1



codeql谓词


谓词通过predicate定义,通过定义谓词可以将复杂语句函数化


例子

    override predicate isSource(DataFlow::Node node) {        node instanceof RemoteFlowSource    }



数据流分析(重点)

个人觉得codeql 数据流分析非常强大,下面是学习记录



本地数据流

本地数据流库位于模块中DataFlow,它定义了Node表示数据可以流经的任何元素的类Nodes 分为表达式节点 ( 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个

isSourceisSinkisSanitizerisAdditionalTaintStep

source //入口点

sink //爆发点

issanitizer //排除误报


继承该类后,通过hasflow方法来分析,codeql会自动分析source能不能走到sink


如何根据漏洞点写出一个sink


codeql学习笔记1

首先漏洞出发点是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点

codeql学习笔记1

    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.DataFlowimport semmle.code.java.dataflow.FlowSourcesimport javaimport 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"

codeql学习笔记1


原文始发于微信公众号(8ypass):codeql学习笔记1

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年11月4日23:24:58
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   codeql学习笔记1https://cn-sec.com/archives/612516.html

发表评论

匿名网友 填写信息