LOADING

正在加载

CodeQL学习(壹)——介绍和简单使用

壹 CodeQL简介

1.1 介绍

在做代码审计的时候,会用到通过关键字查询的方式进行代码审计,但是这样做的弊端就是搜索出来的结果量太大了,而且可能项目里用不到开发者重写的危险函数(例如文件上传,可能项目之前有这个功能,后来换了一些架构就不用了,但是忘记删除了),这时候就在想是否是有一种方法或者规则集,进行过滤,也就是说通过自动化或者半自动化的方式对代码的漏洞点进行筛查,答案是有的。

在说这种新型的代码审计方法之前,我们需要了解一个知识点:在代码自动化安全审计的理论当中,有一个最核心的三元组概念,就是sourcesinksanitizer

  • source:漏洞污染链条的输入点,比如获取http请求的参数部分,就是非常明显的source
  • sink:漏洞污染链条的执行点,比如SQL注入漏洞,最终执行SQL语句的函数就是sink,这个函数可能是query或者exeSql,或者其它。
  • sanitizer:又叫净化函数,是指在整个的漏洞链条当中,如果存在一个方法阻断了整个传递链,使得source的恶意数据传递到sink点后,这些恶意数据已经被过滤过了或者说是被净化过了,那么这个方法就叫sanitizer

下面是大概的流程图:

# flow一般可以理解为数据从source到sink之间的流向经过的方法,有一些文章叫node(其实我们可以理解为node是一个一个的函数,然后多个node组成了flow这个数据流),如果flow存在一个方法阻断了整个传递链,那么这个方法就是sanitizer
source(污点的来源、可控的参数点)-> flow(一些对传入的参数进行编码或者过滤的过程)-> sink(漏洞触发点、触发漏洞的方法)

根据前面的知识点,我们可以通过一套规则集进行查询匹配,例如我们将出现危险方法的地方为终点(sink),用户输入的数据为起点的规则(source),进行查询,这时候就提取出满足我们需要审计的一个可疑利用链,再通过人工进行审核,即可高效的完成代码审计(当然在实际应用中并不会这么简单),这个过程类似与数据库的查询,通过SQL语句去查询我们需要的内容,这个想法就是CodeQL的核心理念所在。而CodeQL就是一个会提取源码中的相关信息以及数据之间的相互关系,并根据这些信息生成数据库的强大的静态代码分析工具,在使用CodeQL时,我们就是将代码分析过程转化成了数据库查询过程,可以执行CodeQL查询(简称QL)来查询代码库。很多人用它都会利用已知的安全漏洞来挖掘类似的漏洞,但是个人觉得我们可以通过它来进行一些关键信息搜索,然后挖掘出自己独创的漏洞,因为这个工具本质就是一个代码搜索工具,与人工不同在于它可以进行sourcesink数据流闭环。

Github为了解决其托管的海量项目的安全性问题,收购了CodeQL的创业公司,并宣布开源CodeQL的规则部分,这样全世界的安全工程师就可以贡献高效的QL审计规则给Github,帮助它解决托管项目的安全问题,所以除了官方的规则库外,我们也可以自定义规则完善我们的规则库。

1.2 原理

CodeQL的整体思路是把源代码转化成一个可查询的数据库,通过Extractor模块对源代码工程进行关键信息分析提取,提取完成后,所有分析所需的数据都会导入一个文件夹,这个就是CodeQL数据库,其中包括了源代码文件、关系数据、语言相关的database schemaschema定义了数据之间的相互关系)。CodeQL的数据库并没有使用现有的数据库技术,而是一套基于文件的自己的实现。

不同类型代码生成的逻辑也不同:

  • 对于编译型语言,Extractor会监控编译过程,编译器每处理一个源代码文件,它都会收集源代码的相关信息,如:语法信息(AST抽象语法树)、语意信息(名称绑定、类型信息、运算操作等)、控制流、数据流等,同时也会复制一份源代码文件。
  • 对于解释性语言,Extractor则直接分析源代码,得到类似的相关信息。

CodeQL的工作流程如下图所示:

微信截图_20241121161215.png

整体的工作流程分为四个模块:
提取数据库(Extraction):对源代码工程进行关键信息分析提取,构成一个关系型数据库CodeQL database
编译QL规则(Query Compilation):将QL规则结合QL标准库编译,生成查询的执行计划。
执行QL查询(Query Evaluation):在生成的CodeQL数据库上运行QL查询。
展示查询结果(Query results):最终将查询结果展示给用户,方便用户进行进一步的人工审计分析。

注意:由于CodeQL是需要项目能正常构建,然后拦截这些在构建中出现的编译命令,然后生成基于AST抽象语法树的数据库,所以当项目代码不能被正常构建的话是不能使用CodeQL的(这也是CodeQL的一个缺点之一),这里说的项目构建的意思就是Java代码只要可以打包构建起来,然后在构建过程中可以拦截所有的编译命令就能,生成AST抽象语法树的数据库,不一定需要正常运行。我们可以理解为代码是一堆电脑零件,构建就是组装成一个没有插电的电脑(不一定需要电脑运行起来),这时候CodeQL就可以将这些零件进行链接形成一个数据库,但是如果是散开的就无法进行链接索引。由于CodeQL主要做的是静态分析,只分析源代码,所以不依赖于项目是否运行起来,但是对于编译型语言来说,必须先能正确构建项目。

贰 安装

CodeQL包含两部分:解析引擎、SDK规则库。

  • 解析引擎:不开源(但可以直接在官网下载二进制文件),用于解析我们编写的规则。
  • SDK规则库:完全开源,里面包含大部分现成的漏洞规则,也可以利用其编写自定义规则。

首先我们选一个合适的路径然后创建CodeQL目录,然后在该目录下创建codeql_Toolcodeql_Ruledatabasessource目录,分别用于安装Codeql解析引擎、SDK规则库、生成的源码数据库和被分析源代码,当然databasessource目录可以在别的位置,这里我只是喜欢放在一起做归档,看个人喜好:

3d341aa3c02b78c5afa764220626b223.png

接着下载对应系统的CodeQL解析引擎,然后在codeql_Tool下解压,将CodeQL可执行程序添加到环境变量中,方便命令行运行:

8b741fbb243092bcb1dc0875fc1764dd.png

设置好后,输入Codeql出现下面内容,说明引擎安装完成:

43c3557112be0faa002bf7855ca9bdc1.png

然后下载SDK规则库,将其放到codeql_Rule中:

d5f9cb1f110f0cc6beba1f636d7d90a4.png

最后安装Visual Studio Code代码编辑器的CodeQL插件(CodeQL需要使用Visual Studio Code来开发和调试规则),配置一下上面我们安装的CodeQL引擎路径,完成CodeQL安装:

80bbaee347536ac1466fcf0722f452cc.png

叁 基础使用

3.1 准备分析的代码

首先准备要扫描的代码,这里我们用micro_service_seclab这个靶场来做测试和教学(注意:需要切换到master分支,默认为main分支,存在异常)。将源码解压放到source目录中:

f41c3ba5869c2e45a0b272e85dbcda99.png

3.2 生成数据库

由于CodeQL只可以对被解析引擎编译生成的抽象语法树(AST)结构数据库进行扫描,所以需要先生成目标数据库。而生成数据库分为两种:本地生成和在线生成。

  • 本地生成数据库:一般适合编译类语言,如goJavaGolang等,这种方式需要按照前面的安装步骤去安装CodeQL引擎和规则库。
  • 在线生成数据库:一般适合解释性语言,如Python等,而这种方式完全不需要安装CodeQL引擎,只需要将代码放到dashboard网站上生成数据库,然后下载官方规则库。

注意:运行CodeQL生成数据库,前提是需要被审计的系统能正常打包或者正常构建起来。

3.2.1 本地生成(推荐)

codeql database create 数据库名 --language=cpp --source-root=源码路径 --command="编译命令"

数据库名:代码数据库存放目录,可以相对路径也可以绝对路径,如果没有会自动创建目录
-command:参数如果不指定,会使用默认的编译命令和参数,一般编译型语言需要这个目录,python这种解释性语言可以不需要,像Java可能就需要设置mvn clean install --file pom.xml这种命令编译
-source-root:源码路径,可以相对路径也可以绝对路径
-overwrite:表示 create 的目标 database 对已有的 database 做覆盖
-language:要根据具体项目的编译语言指定

# 例子,打开终端直接运行下面命令
codeql database create 代码数据库存放目录 --language=java --command='mvn clean install --file pom.xml' --source-root=源代码目录 --overwrite

d049a7cec41ca3e7d61a177194a12893.png

出现Successfully说明生成完成(注意项目代码要能打包或者运行起来,其实我们运行完后就会发现源码下多出了打包的程序):

f74b6424631101bc045ffbdef7b7a8a3.png

3.2.2 在线生成(copy网上的教程,没用过)

没有找到对应功能,这里就用网上文章吧(后面有时间再研究): 代码分析平台CodeQL学习手记(一)

3.2.3 语言对应关系

其中语言对应关系如下:

Language Identity
C/C++ cpp
C# csharp
Go go
Java java
javascript/Typescript javascript
Python python

CodeQL支持的语言是有限的,详细可以参考:CodeQL支持的语言和框架

3.2 选择规则

再进行项目漏洞扫描前,我们需要选择规则来扫描,这里有两种:

  • 选择CodeQL内部的对应语言规则
  • 创建自定义规则

3.2.1 选择CodeQL内部的对应语言规则

如果选择CodeQL内部规则,那么我们需要在对应规则文件夹下打开Vscode,这里我们把全部规则打开,方便寻找,我的CodeQl规则在.\Codeql\codeql_Rule\下,其中.ql后缀的文件都是规则文件,就是我们用于扫描代码的:

3793f77a80108f2877b48dcea16527e8.png

到这里我们就选择了官方的CodeQL规则。

3.2.2 创建自定义规则

如果我们需要创建自定义规则,首先创建一个新的自定义规则文件夹或者叫QL包,这里我们可以设置个名字Test_rule,然后再QL包下创建一个qlpack.yml文件(必需),内容如下:

name: test-query
version: 0.0.1
libraryPathDependencies: codeql-java

04878c396aa40f9589176f23e8de2d30.png

需要注意:QL包目录的根目录下必须包含一个以qlpack.yml命名的文件,该文件包含nameversion以及libraryPathDependencies字段属性。只有当自定义的QL包有qlpack.yml文件才能对对应语言进行正常编译、执行。
参考:https://codeql.github.com/docs/codeql-cli/using-custom-queries-with-the-codeql-cli/

到这里我们就创建了自定义的CodeQL规则文件夹。

3.3 进行项目漏洞扫描

选择好规则后,我们就要通过QL语句去扫描项目,有两种方式:

  • 通过Vscode对项目进行扫描
  • 通过命令行对项目进行扫描

3.3.1 通过Vscode对项目进行扫描

这里我们用自定义的QL包文件夹来扫描,在刚刚的QL包文件夹内打开Vscode,然后选择CodeQL(如果用官方规则也是一样的步骤):

61d88ad39cc7cd3afb4339e06b6fb34f.png

添加刚刚生成的数据库到Database中:

0de6e241bdc8b6d2e3411d1278c2de09.png

会出现库信息:

9e20ba8219ef1ee44900726c7c493307.png

接着右键点击Add Database Source to Workspace将其加入到工作区中(为了方便查看以及代码定位):

9f02888235213e75cdcca08f0534b9dc.png

接着新建一个test.ql文件,内容如下:

select "hello word"

右键点击Run Query运行(这里有好几个Run Query,根据个人需求选择即可),这只是一个简单输出hello word

f4a7d2899bfb82542a1dddcf8d65bdb1.png
086f2382a70ae856546284e829ed7eb5.png

我们添加搜索Java模块,修改内容为:

// 内容可以不需要了解,只需要知道这是QL语言
import java

from Call c
select c,"This is a method call!"

右键点击Run Query运行,由于我们加入了Java模块,这里就会对我们加载的源代码数据库进行搜索:

2629bf40dc57e7619e0cb4bea6e9b9bb.png

当然也可以选择多个规则文件进行扫描,这里我们在规则所在文件夹里,右键点击CodeQL:Run Queries in Selected Files,弹窗提示是否使用这些规则,点击Yes即可。

4be7de4008db8a75d5d5c999bcb95448.png

3.3.2 通过命令行对项目进行扫描

当我们需要用命令行对项目进行扫描时,我们可以通过codeql analyze命令去执行单个QL文件、目录下所有QL文件或者查询suite(.qls),其实我们用vscode编写ql规则和验证的时候,也是用到了codeql命令行操作,只不过vscode代替我们执行了命令。

# 这里以java为例子
# 执行单个规则
codeql database analyze 生成的数据库 codeql/ql/java/ql/examples/test.ql --format=csv --output=result.csv --rerun
# 执行全部规则
codeql database analyze 生成的数据库 codeql/ql/java/ql/src/codeql-suites/java-security-extended.qls --format=csv --output=result.csv --rerun

# --format=csv:输出结果的格式,如sarif-latest可以输出json格式,csv输出csv格式,必填
# --output=result.csv:输出结果路径,必填
# 生成的数据库:分析的数据库路径,必填
# codeql/ql/java/ql/examples/test.ql:使用的查询语句,必填

7e732e62b12a4fa995a2a07e34a46f22.png

出现Interpreting results.说明扫描完成,扫描的结果在当前目录下。

e13eb9e66c134f83d5a40b5eed1daadf.png

3.4 规则富化

接下来我们就可以进行对输出的结果进行筛选,规则富化(就是丰富规则内容),来进一步筛查有价值的利用链或者漏洞。其实就是在前面写的规则基础上不断丰富内容,将噪点(干扰的东西)减少,最后会得到我们需要的利用链或者漏洞,这是一个不断迭代的过程。

注意:
对于自定义规则我们可以随便更改,但对于官方的规则,实际上已经很成熟了,但是难免需要修改,这时候建议是不要随意修改,而是新建一个副本,以防破坏原有的规则库,导致后面再用的时候恢复不了官方的,重新下载会比较麻烦。

肆 参考

avatar
小C&天天

修学储能 先博后渊


今日诗句