CodeQL从入门到放弃(转载)

为什么要写这篇文章?

自从Github宣布推出CodeQL,国外越来越多安全人员使用这个项目做代码安全评估工作,截止到此刻,CodeQL在Github上已经有超过3100个Star。

但是国内了解CodeQL的安全人员并不多,能google到的关于codeql的中文文章比较少。大部分中文文章,都是介绍CodeQL是什么之后,用简单的代码片段说明CodeQL的某个功能,很少有非常全面的介绍使用CodeQL对一个项目做漏洞分析的文章。这让想学习的读者一头雾水,还是不知道该如何在自己的项目上使用CodeQL。

所以我想写一篇文章,从安装开始,到编写QL规则实现漏洞的自动化扫描,再到解决误报,漏报问题,让读者能够真正的了解该如何使用CodeQL自动化审计自己项目的安全性。

靶场介绍

我使用SpringBoot简单的实现了一个靶场。这个靶场里面包含注入漏洞和一些其它的漏洞。我们本篇文章的目的,就是要使用CodeQL来自动检索出里面的注入漏洞,并且排除误报,解决漏报,还有解决一些其它问题。

OS: Mac

Java JDK: 1.8

Maven: Apache Maven 3.6.3

您可以点击此处下载到这个简单的测试靶场系统(micro-service-lab)。

CodeQL是什么?

如果您已经了解CodeQL是什么,可以直接跳过这个章节。

在回答这个问题之前,我们来看一下安全人员做代码审计的进化史。

在代码审计的早期,安全研究人员会是通过人工审计的方式来做作项目的代码审计,主要是通过查找用户可控的参数和危险函数,然后跟进危险函数的参数是否可控,如果用户可控的参数没有做处理,那么项目就可能存在安全漏洞。

但是随着业务快速的发展,项目迭代周期变短以及代码数量的增加,如果是纯靠人工的方式很难实现所有项目漏洞的覆盖测试。所以出现了一些辅助人工审计的工具,比如前几年比较火的rips,cobra,通过这些工具,可以把危险函数代码代码检索出来,再通过人工审计来判断是否存在安全漏洞。

上面的方式主要还是需要人工来介入进来进行判断,工作量还是很大,并且非常依赖安全工程师的个人能力。但是近些年出现了不少优秀的自动化代码安全审计产品,比如非常有名的Checkmarx,Fortify SCA。这些软件可以自动化的帮我们审计出安全漏洞,大大减少了人工工作量,并加快了安全审计速度。但是这些软件都是商业的,价格比较贵,一般企业可能没有这么多预算购买。

与此同时,Github为了解决其托管的海量项目的安全性问题,收购了CodeQL的创业公司,并宣布开源CodeQL的规则部分,这样全世界的安全工程师就可以贡献高效的QL审计规则给Github,帮助它解决托管项目的安全问题。

对于安全工程师,也就多了一个非商业的开源代码自动化审计工具。

CodeQL支持非常多的语言,在官网有如下支持的语言和框架列表

CodeQL for research | GitHub Security Lab

注:CodeQL被禁止用于企业内部的CI/CD流程,我们可以用来做安全研究。同时我还是建议企业购买一款商业的SAST代码审计工具,原因我们最后说。

CodeQL安装

CodeQL本身包含两部分解析引擎+SDK

解析引擎用来解析我们编写的规则,虽然不开源,但是我们可以直接在官网下载二进制文件直接使用。

SDK完全开源,里面包含大部分现成的漏洞规则,我们也可以利用其编写自定义规则。

引擎安装

首先在系统上选定CodeQL的安装位置,我的位置为:Home/CodeQL。

然后我们去地址:https://github.com/github/codeql-cli-binaries/releases 下载已经编译好的codeql执行程序,解压之后把codeql文件夹放入~/CodeQL。

为了方便测试我们需要把ql可执行程序加入到环境变量当中:

export PATH=/Home/CodeQL/codeql:$PATH

然后source一下/etc/profile之后,我们在命令行输入codeql,出现如下内容就表示引擎设置完成。

SDK安装

我们使用Git下载QL语言工具包,也放入~/CodeQL文件夹。

cd ~/CodeQL&git clone https://github.com/Semmle/ql

这样在~/CodeQL目录下就包含了2个文件夹,引擎文件夹(codeql)和SDK文件夹(ql)。

➜  CodeQL ls
codeql ql

VSCode开发插件安装自行百度

到此,我们就设置好了CodeQL的开发环境,是不是很简单?

后面我们将进入CodeQL规则实质性的东西,我们会随着项目进展一起,说明使用Visual Studio Code的方方面面。

测试"Hello World"

生成Database

为了测试我们刚才的开发环境是否可以正常调试,我们实现一个简单的"Hello World"。

由于CodeQL的处理对象并不是源码本身,而是中间生成的AST结构数据库,所以我们先需要把我们的项目源码转换成CodeQL能够识别的CodeDatabase

我们使用如下命令进行CodeDatabase的生成工作。

database create ~/CodeQL/databases/micro-service-seclab-database  --language="java"  --command="mvn clean install --file pom.xml" --source-root=~/CodeQL/micro-service-seclab/

我们来解释一下上面生成database命令的语句:

codeql database create ~/CodeQL/databases/codeql_demo 当然是指我们要创建的database为~/CodeQL/databases/codeql_demo(注意:要先创建~/CodeQL/databases/目录)。

--language="java" 表示当前程序语言为Java。

--command="mvn clean install --file pom.xml" 编译命令(因为Java是编译语言,所以需要使用–command命令先对项目进行编译,再进行转换,python和php这样的脚本语言不需要此命令)

--source-root=~/CodeQL/micro-service-seclab/ 这个当然指的是项目路径

我们执行以上命令后,首先会对项目进行编译,然后就会提示创建数据库成功,访问~/CodeQL/databases/codeql_demo存在即可。

导入Database

和SQL语言一样,我们执行QL查询,肯定是要先指定一个数据库才可以。

我们通过如下方式来导入刚刚生成的数据库,选择目录~/CodeQL/micro-service-seclab/

编写"Hello World"查询

编写QL查询,我们需要使用Visual Studio Code打开我们开始下载的SDK,也就是~/CodeQL/ql文件夹, 然后在图示的目录里新建demo.ql文件,然后写入

select "Hello World",

然后在当前文件上点击鼠标邮件,选择
CodeQL: Run Query即可执行。

执行成功之后,看到了右侧的"Hello World",恭喜你,你已经开始了CodeQL之旅!

CodeQL基本语法

我们上面提到过,CodeQL的核心引擎是不开源的,这个核心引擎的作用之一是帮助我们把micro-service-seclab转换成CodeQL能识别的中间层数据库。

然后我们需要编写QL查询语句来获取我们想要的数据。

由于CodeQL开源了所有的规则和规则库部分,所以我们能够做的就是编写符合我们业务逻辑的QL规则,然后使用CodeQL引擎去跑我们的规则,发现靶场的安全漏洞。

CodeQL的基本语法:

QL语法

CodeQL的查询语法有点像SQL,如果你学过基本的SQL语句,基本模式应该不会陌生。

import java
 
from int i
where i = 1
select i

第一行表示我们要引入CodeQl的类库,因为我们分析的项目是java的,所以在ql语句里,必不可少。

from int i,表示我们定义一个变量i,它的类型是int,表示我们获取所有的int类型的数据;

where i = 1, 表示当i等于1的时候,符合条件;

select i,表示输出i。

一句话总结就是:在所有的整形数字i中,当i==1的时候,我们输出i。

QL查询的语法结构为:

from [datatype] var
where condition(var = something)
select var

类库

上面我们提到,我们需要把我们的靶场项目,使用CodeQL引擎转换成CodeQL可以识别的database(micro-service-seclab-database),这个过程当中,CodeQL引擎把我们的java代码转换成了可识别的AST数据库。

我们的类库实际上就是上面AST的对应关系。

怎么理解呢?比如说我们想获得所有的类当中的方法,在AST里面Method代表的就是类当中的方法;比如说我们想过的所有的方法调用,MethodAccess获取的就是所有的方法调用。

我们经常会用到的ql类库大体如下:

名称解释
Method方法类,Method method表示获取当前项目中所有的方法
MethodAccess方法调用类,MethodAccess call表示获取当前项目当中的所有方法调用
Parameter参数类,Parameter表示获取当前项目当中所有的参数

结合ql的语法,我们尝试获取micro-service-seclab项目当中定义的所有方法:

import java
 
from Method method
select method

 

我们再通过Method类内置的一些方法,把结果过滤一下。比如我们获取名字为getStudent的方法名称。

import java
 
from Method method
where method.hasName("getStudent")
select method.getName(), method.getDeclaringType()


method.getName() 获取的是当前方法的名称
method.getDeclaringType() 获取的是当前方法所属class的名称。

谓词

和SQL一样,where部分的查询条件如果过长,会显得很乱。CodeQL提供一种机制可以让你把很长的查询语句封装成函数。

这个函数,就叫谓词。

比如上面的案例,我们可以写成如下,获得的结果跟上面是一样的:

import java
 
predicate isStudent(Method method) {
exists(|method.hasName("getStudent"))
}
 
from Method method
where isStudent(method)
select method.getName(), method.getDeclaringType()

语法解释

predicate 表示当前方法没有返回值。

exists子查询,是CodeQL谓词语法里非常常见的语法结构,它根据内部的子查询返回true or false,来决定筛选出哪些数据。

设置Source和Sink

什么是source和sink

在代码自动化安全审计的理论当中,有一个最核心的三元组概念,就是(source,sink和sanitizer)。

source是指漏洞污染链条的输入点。比如获取http请求的参数部分,就是非常明显的Source。

sink是指漏洞污染链条的执行点,比如SQL注入漏洞,最终执行SQL语句的函数就是sink(这个函数可能叫query或者exeSql,或者其它)。

sanitizer又叫净化函数,是指在整个的漏洞链条当中,如果存在一个方法阻断了整个传递链,那么这个方法就叫sanitizer。

只有当source和sink同时存在,并且从source到sink的链路是通的,才表示当前漏洞是存在的。

...........

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值