CodeQL小记

前言

这个文章是用来记录学习CodeQL过程中遇到的问题和一些常用的东西

创建数据库

按照自己的理解,codeql创建java类的数据库是通过编译过程中获取AST

开源

对于有开源的系统直接使用官方的就行

codeql database create <database> --language=<language-identifier>

指定build命令

codeql database create java-database --language=java --command="mvn clean install"

闭源

对于闭源项目,首先考虑是否存在已知数据库,任何再自己构建

构建闭源项目的数据库,直接使用Gitbub上的开源项目,有时候需要自己修改一些东西,遇到问题解决问题

  1. https://github.com/webraybtl/CodeQLpy
  2. https://github.com/waderwu/extractor-java

单jar/war文件

这种情况下,目前我只使用过CodeQLpy,

python3 main.py -t xxx.jar -c

然后

codeql database create qldbname --language=java --command="run.cmd" --overwrite

如果报错了可能是因为依赖的问题,自己根据依赖适当修改

多jar文件

这种情况使用的是extractor-java这个项目

第一次使用可能会因为找不到codeql路径然后报错,修改一些代码即可

image-20240226152443538

以创建weblogic的数据库为例子

首先用脚本把jar包都复制出来

import os
import shutil

# 源目录路径
source_dir = 'E:\\Oracle_Home\\wlserver'
# 目标目录路径
target_dir = 'E:\\wb'
# 确保目标目录存在,如果不存在则创建
if not os.path.exists(target_dir):
    os.makedirs(target_dir)
# 递归遍历源目录下的所有文件和子目录
for root, dirs, files in os.walk(source_dir):
    for file in files:
        if file.endswith('.jar'):
            source_path = os.path.join(root, file)
            target_path = os.path.join(target_dir, file)
            shutil.copy(source_path, target_path)
            print(f'Copied {file} to {target_path}')
print('All .jar files have been copied to the target directory.')

然后用下面命令将源码包和依赖包分别拷贝到不同目录(这里我默认将以com.oracle.weblogic.开头的视为weblogic本身,其他jar包视为依赖):

mkdir -p weblogic/lib_/
find ./wb -type f -name "*.jar" | grep "com.oracle.weblogic."| xargs -I {} cp {} ./weblogic/
find ./wb -type f -name "*.jar" | grep -v "com.oracle.weblogic."| xargs -I {} cp {} ./weblogic/lib_

将源码包解压:

cd weblogic/
ls |grep ".jar" | xargs -I {} unzip -q -n {}

反编译class(只处理包名为weblogic.application的类):

python3 ~/extractor-java/class2java.py xxx/weblogic/application/

然后就是编译CodeQL数据库:

python3 ~/extractor-java/run.py weblogic_qldb xxx/weblogic/application/ -ld weblogic/lib_/

CodeQL项目创建

新建一个文件夹,例如test

然后在test文件夹下创建一个文件qlpack.yml,注意:名字必须为qlpack.yml

内容如下:

name: xxx
version: 0.0.0
libraryPathDependencies: codeql-java

官方:编辑 name 属性,使其与格式 <scope>/<name> 匹配,其中 <scope> 是要发布到的 GitHub 组织或用户帐户的名称

说实话,日常使用name 我是随便写的

全局污点分析

想要能够看到每个节点的位置就要按照格式写

image-20240125103830296

参考下面代码格式

/**
 * @kind path-problem
*/
import java
import DataFlow2::PathGraph
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking2



class MyTaintTrackingConfiguration extends TaintTracking2::Configuration {
    MyTaintTrackingConfiguration() { 
        this = "MyTaintTrackingConfiguration" 
    }
    override predicate isSource(DataFlow::Node source) {
        ......
    }
    override predicate isSink(DataFlow::Node sink) {
        ......
    }
}

from MyTaintTrackingConfiguration config, DataFlow2::PathNode source,DataFlow2::PathNode sink
where config.hasFlowPath(source, sink)
select source.getNode(), source, sink, "fastJsonInject!"

主要是开头的@kind path-problemimport 的内容、

结果输出格式:

from MyTaintTrackingConfiguration config, DataFlow2::PathNode source,DataFlow2::PathNode sink
where config.hasFlowPath(source, sink)
select source.getNode(), source, sink, "xxxxx"

QL收集

JNDI注入:

/**
 * @kind path-problem
*/
import java
import DataFlow2::PathGraph
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking2

class Context extends  RefType{
    Context(){
        this.hasQualifiedName("javax.naming", "Context")
        or
        this.hasQualifiedName("javax.naming", "InitialContext")
        or
        this.hasQualifiedName("org.springframework.jndi", "JndiCallback")
        or 
        this.hasQualifiedName("org.springframework.jndi", "JndiTemplate")
        or
        this.hasQualifiedName("org.springframework.jndi", "JndiLocatorDelegate")
        or
        this.hasQualifiedName("org.apache.shiro.jndi", "JndiCallback")
        or
        this.getQualifiedName().matches("%JndiCallback")
        or
        this.getQualifiedName().matches("%JndiLocatorDelegate")
        or
        this.getQualifiedName().matches("%JndiTemplate")

    }
}
predicate isLookup(Expr arg) {
    exists(MethodAccess ma |
        ma.getMethod().getName() = "lookup"
        and
        ma.getMethod().getDeclaringType() instanceof Context
        and
        arg = ma.getArgument(0)
    )
}
class MyTaintTrackingConfiguration extends TaintTracking2::Configuration {
    MyTaintTrackingConfiguration() { 
        this = "MyTaintTrackingConfiguration" 
    }
    override predicate isSource(DataFlow::Node source) {
        source instanceof RemoteFlowSource
    }
    override predicate isSink(DataFlow::Node sink) {
        exists(Expr arg |
            isLookup(arg)
            and
            sink.asExpr() = arg
        )
    }
}
from MyTaintTrackingConfiguration config, DataFlow2::PathNode source,DataFlow2::PathNode sink
where config.hasFlowPath(source, sink)
select source.getNode(), source, sink, "JNDI!"

MyBatis框架SQL注入

https://todis21.github.io/2024/01/24/CodeQL%E6%8C%96%E6%8E%98MyBatis%E6%A1%86%E6%9E%B6%E7%9A%84SQL%E6%B3%A8%E5%85%A5/