codeQL入门

https://github.com/SummerSec/learning-codeql

文档

https://codeql.github.com/docs/writing-codeql-queries/

安装

官网

https://codeql.github.com/

分析引擎:

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." 

一个漏洞触发的基本要素

  1. source 可控的输入
  2. path 经过的中间路径
  3. 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 的类上的名为 toObjectMethod及其参数

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"

posted @ 2025-06-01 18:32  n4c1  阅读(100)  评论(0)    收藏  举报