代码分析平台CodeQL学习手记(十二) - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

代码分析平台CodeQL学习手记(十二)

fanyeee 技术 2020-01-22 10:00:00
2318613
收藏

导语:在本文中,我们将为读者详细介绍如何利用CodeQL平台的Visual Studio Code插件,即CodeQL for VS Code在本地编写和运行查询,并直接在工作区中展示查询结果,同时,我们还将详细介绍如何编写路径查询。

代码分析平台CodeQL入门(一)

代码分析平台CodeQL学习手记(二)

代码分析平台CodeQL学习手记(三)

代码分析平台CodeQL学习手记(四)

代码分析平台CodeQL学习手记(五)

代码分析平台CodeQL学习手记(六)

代码分析平台CodeQL学习手记(七)

代码分析平台CodeQL学习手记(八)

代码分析平台CodeQL学习手记(九)

代码分析平台CodeQL学习手记(十)

代码分析平台CodeQL学习手记(十一)

在前面的文章中,我们为读者介绍了如何分析数据流,以及如何进行污点跟踪和指向分析。在本文中,我们将为读者详细介绍如何利用CodeQL平台的Visual Studio Code插件,即CodeQL for VS Code在本地编写和运行查询,并直接在工作区中展示查询结果,同时,我们还将详细介绍如何编写路径查询。

概述

CodeQL for VS Code是CodeQL平台的VS Code插件,通过它,我们可以在VS Code中分析各种语言编写的代码,从而挖掘代码库中的安全问题。这款插件的功能包括:

· 通过CodeQL查询从源代码生成的数据库。

· 显示路径查询结果中的数据流,该功能对于筛选安全结果来说至关重要。

· 提供了一种运行大型开源CodeQL安全查询库的各种查询的简便方法。

· 提供了IntelliSense(智能提示)功能,使得CodeQL查询的编写和编辑变得更加轻松。

在Visual Studio Code中搭建CodeQL分析环境

接下来,我们将为读者详细介绍如何为Visual Studio Code安装和配置CodeQL插件,具体分三个步骤:

· 安装插件。

· 可选:配置特定版本的CodeQL命令行接口。

· 设置工作区。

安装插件

首先,要想安装CodeQL插件,必须将Visual Studio Code 升级到1.39版本以上,否则的话,是无法安装的。

安装合适版本的Visual Studio Code软件后,就可以通过下列方法来安装这个插件了:

· 在浏览器中访问Visual Studio Code Marketplace(地址为https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-codeql),然后,单击相应的Install按钮即可。

· 在Extensions视图(可以通过Ctrl+Shift+X或Cmd+Shift+X组合键调出该视图)中搜索CodeQL,然后,单击相应的Install按钮即可。

· 下载CodeQL VSIX文件(地址为https://github.com/github/vscode-codeql/releases)。然后,在Extensions视图中,单击More actions > Install from VSIX选项,选择CodeQL VSIX文件即可。

配置CodeQL CLI

实际上,该扩展是通过CodeQL CLI来编译和运行查询的。所以,如果读者此前已经安装了该CLI并将其添加到相应的路径中的好,该插件将会使用该版本的CodeQL CLI。对于创建了自己的CodeQL数据库而不是从LGTM.com下载它们的读者来说,通常就属于这种情况。

如果尚未安装CodeQL CLI,该插件将自动管理对CLI可执行文件的访问情况。这样做的好处是,可以确保CLI与CodeQL插件互相兼容。

需要注意的是,我们是无法从终端访问该插件所管理的CLI的。如果想在该插件之外使用CLI(例如创建数据库),最好安装自己的CLI副本。为了避免机器上有两个 CLI 副本,可以在 codeql.CLI.executablepath 设置中指定相应的 CLI 版本(详情见下文)。

如果想让该插件使用特定版本的 CodeQL CLI 可执行文件,请在 VS Code 用户设置中将 CodeQL.CLI.executablepath 设置为相应的CLI 可执行文件的位置。通常来说,这个可执行文件在Linux/Mac平台中名为 codeql ,在Windows平台中名为codeql.cmd 。

配置CodeQL工作区

使用CodeQL时,通常都需要访问标准CodeQL库。这样的话,我们就可以利用各种现成的查询来分析目标代码了。为此,我们可以通过两种方式来访问它们:

· 最简单的方法就是使用“starter”工作区。它是作为一个Git存储库进行维护的,这使得库更新变得更加容易。

· 更高级的访问方法是,将CodeQL库和查询代码添加到现有的工作区中。

需要注意的是,对于CLI用户来说,实际上还有第三种选择:如果已经使用CodeQL CLI创建了包含CodeQL库的CodeQL目录(例如CodeQL-home),那么可以直接在VS Code中打开该目录,并访问CodeQL库。

使用“starter”工作区

“starter”工作区实际上就是一个Git存储库,包含下列内容:

· 用于存放分析C/C++、C#、Java、JavaScript和Python代码的CodeQL库和查询的存储库。由于它是作为一个子模块独立存在的,因此,即使更新了这个存储库,也不会影响我们自定义的查。

· 用于存放分析Go代码的CodeQL库和查询的存储库。它也是作为一个子模块独立存在的。

· 一系列名为codeql-custom-queries-

要使用“starter”工作区,我们需要:

· 将https://github.com/github/vscode-codeql-starter/repository克隆到我们自己的计算机上:

- 运行git clone-recursive命令,或在克隆后使用by git submodule update-init-remote命令,以确保已经包含了这些子模块。

- 定期使用git submodule Update-Remote命令更新这些子模块。

· 在VS Code中,使用File>Open Workspace选项从工作区存储库的签出中打开vscode-codeql-starter.code-workspace文件。

更新 CodeQL 的现有工作区

实际上,我们也可以直接创建 CodeQL 存储库的本地克隆,从而将 CodeQL 库添加到现有的工作区:https://github.com/semmle/ql。

要使这些标准库可用于我们的工作区:

· 选择File > Add Folder to Workspace,然后选择Semmle/ql存储库的本地签出。

· 为每种目标语言创建一个新文件夹,使用New Folder 或Add Folder to Workspace选项来保存自定义的查询和库。

· 在每个目标语言文件夹中创建一个qlpack.yml文件,以告诉CodeQL CLI 该文件夹的目标语言及其依赖项是什么。实际上,Semmle/ql的主分支也提供了这些文件。这样的话,CodeQL就会在所有打开的工作区文件夹或用户的搜索路径中查找相关的依赖项。

例如,要使自定义 CodeQL 文件夹 my-custom-cpp-pack 依赖于C++的CodeQL 标准库,需要创建一个包含以下内容的 qlpack.yml 文件:

name: my-custom-cpp-pack
version: 0.0.0
libraryPathDependencies: codeql-cpp

此外,关于为什么要添加qlpack.yml文件,请参阅这里的详细解释:https://help.semmle.com/codeql/codeql-cli/reference/qlpack-overview.html。还需要注意的是,用于分析Go语言编写的代码的CodeQL库并没有包含在Semmle/ql存储库中,而是单独存储的。在需要分析Go语言编写的项目时,请通过https://github.com/github/codeql-go地址克隆存储库,并将其添加到我们的工作区中。

插件的用法

配置好该插件后,我们就可以通过运行查询来分析项目代码了。

选择数据库

首先,我们需要获取待分析项目的CodeQL数据库。为此,我们既可以从LGTM.com下载一个数据库,也可以使用CodeQL CLI创建自己的数据库。

从LGTM.com下载数据库:

1. 登录LGTM.com。

2. 找到感兴趣的项目并打开Integrations选项卡。

3. 滚动到页面底部的CodeQL databases for local analysis部分。

4. 下载用于目标语言的数据库。

5. 对数据库进行解压。

关于通过CodeQL CLI创建数据库的方法,这里先不介绍,将来会专门加以阐述。

接下来,我们需要使用CodeQL: Choose Database命令将数据库文件夹添加到VS Code中。我们可以从Command Palette或侧边栏中的CodeQL容器来访问该命令。然后,导航至数据库文件夹(包含db-< language >和src的父文件夹)并进行添加。 

此外,如果我们的某个工作区文件夹中已经含有一个数据库,则可以在文件资源管理器中右键单击它,然后选择CodeQL: Choose Database选项即可。

最后,我们可以通过Databases视图来查看在当前会话中已经添加的所有数据库。

运行查询

为了便于大家学习,GitHub网站上的CodeQL存储库中提供了许多示例查询。如果我们的工作区中包含有该文件夹(或不同的语言包),则可以访问

· 打开一个查询(.ql)文件。这时,编辑器将自动显示该文件。

· 右键单击查询窗口,并选择CodeQL: Run Query命令(此外,我们也可以从命令面板运行该命令)。

这时,CodeQL插件将在当前数据库上运行该查询,并在应用程序的右下角显示查询进度。当返回结果时,会将其显示在Results视图中。此外,我们还可以通过下拉菜单来选择不同的输出形式,如格式化的警报消息或原始结果列表。这些可用的输出格式是由查询和元数据的格式指定的。

如果在运行查询时出现问题,则会在应用程序的右下角看到相关的通知信息。除了错误消息本身外,通知信息还将给出如何修复该问题的详细指导。此外,我们还可以通过查阅日志以获得更多信息。

通过路径查询研究数据流

概述

CodeQL的一个关键特性是,它可以帮助我们跟踪数据在程序中的流动情况,并高亮显示潜在的安全漏洞。

要利用该特性,我们可以运行路径查询——即带有@kind path-problem属性的查询。我们可以在标准的CodeQL库中找到许多路径查询脚本,例如,https://github.com/Semmle/ql/blob/master/java/ql/src/Security/CWE/CWE-079/XSS.ql就是一个挖掘Java项目中的XSS漏洞的查询脚本。

显示路径警示信息

当我们运行路径查询时,查询结果将被显示在Results视图中。

实际上,每个查询结果描述的都是源点和接收点之间的信息流动情况。展开结果后,我们就可以查看数据的流动的各个步骤了。单击某个步骤,就会跳入相应的源代码,以便于进一步研究该问题。如果想要从键盘导航路径,可以将快捷键绑定到CodeQL: Show Previous Step on Path 和 CodeQL: Show Next Step on Path命令上面。

编写路径查询

我们不仅可以运行标准的CodeQL路径查询来识别安全漏洞并手动查看结果,还可以修改现有的查询,以便为项目的特定框架更精确地为数据流进行建模,或者编写全新的路径查询,以查找不同的漏洞。

安全研究人员通常对程序中信息流动的方式特别感兴趣。因为许多漏洞是由看似良性的数据流向意外位置并被恶意使用而引起的。所以,使用 CodeQL 编写的路径查询对于分析数据流来说非常有用,因为它们可用于跟踪变量从可能的起点(源点)到可能的终点(接收点)的路径。为了对路径建模,查询必须提供有关源点和接收点的相关信息,以及链接它们的数据流步骤。下面,我们将为读者详细介绍如何构造路径查询文件,以便探索与数据流分析结果相关联的路径。

路径查询通常由元数据、查询谓词和select语句结构组成。CodeQL提供的内置路径查询的结构一般都比较简单,例如分析C/C++、 C#、 Java和JavaScript语言时,通常使用以下模板:

/**
 * ...
 * @kind path-problem
 * ...
 */
 
import
import DataFlow::PathGraph
...
 
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "

其中:

· Dataflow::PathGraph是需要从标准CodeQL库中导入的路径图模块。

· source和sink是路径图上的节点,而DataFlow::PathNode则是它们的类型。

· Configuration是一个类,它的谓词定义了数据在源点和接收点之间如何流动。

对于Python语言来说,则需要使用一个稍微不同的模板:

/**
 * ...
 * @kind path-problem
 * ...
 */
 
import python
import semmle.python.security.Paths
...
 
from TaintedPathSource source, TaintedPathSink sink
where source.flowsTo(sink)
select sink.getNode(), source, sink, "

其中:

· Paths是从标准CodeQL库中导入的路径图模块。

· Source和sink是路径图上的节点,TaintedPathSource source和TaintedPathSink分别是它们的类型。需要注意的是,在Python路径查询中,无需声明一个配置类来定义数据从源点到接收点之间的流动方式。

下面我们开始为读者介绍要想构造一个合格的路径查询,需要满足哪些要求。

路径查询元数据

路径查询元数据必须包含属性@kind path-problem,以便确保查询结果能够被正确解释和显示。至于是否包含其他的元数据,取决于我们打算如何运行该查询。有关更多信息,请参阅页面https://help.semmle.com/QL/learn-ql/writing-queries/introduction-to-queries.html中的介绍。

生成路径解释

为了生成路径解释,我们的查询需要计算路径图。为此,我们必须在查询中定义一个名为edges的谓词,以定义正在计算的图中的边的关系,这些关系将用于计算与查询生成的结果相关的路径。此外,我们也可以从标准数据流库中的路径图模块中导入预定义的edges谓词。除了路径图模块之外,数据流库还包含数据流分析中常用的其他类、谓词和模块。在导入它们时,具体使用什么样的语句,则取决于我们正在分析的语言。

对于C/C++、C#、Java和JavaScript语言来说,我们可以使用下列导入语句:

import DataFlow::PathGraph

上面的语句将从数据流库(DataFlow.qll)中导入PathGraph模块,其中定义了相应的edges谓词。

对于Python语言来说,由于所需的谓词edges包含在Paths模块中,所以,我们可以使用如下所示的语句:

import semmle.python.security.Paths

此外,我们还可以导入专门用于在各种常见框架和环境中实现数据流分析的库。并且,对于所有语言来说,我们还可以定义一个nodes查询谓词,以指定我们感兴趣的路径图的节点。当然,这不是必须的。如果定义了谓词nodes,则只选择具有由这些节点定义的端点的边。

定义自己的nodes谓词

此外,我们还可以在查询中定义自己的nodes谓词,具体代码如下所示:

query predicate edges(PathNode a, PathNode b) {
/** Logical conditions which hold if `(a,b)` is an edge in the data flow graph */
}

关于如何定义edges谓词的更多示例,请访问标准CodeQL库并搜索edges。

定义源点和接收点

实际上,我们还必须在路径查询中提供有关源点和接收点的相关信息,它们对应于我们正在探索的路径中的节点。源点和接收点的名称和类型必须在查询的from语句中声明,并且类型必须与由edges谓词计算的图的节点相互兼容。

当我们查询C/C++、C#、Java或JavaScript代码(并且在查询中使用了import DataFlow::PathGraph)时,则需要通过数据流库中的Configuration类来访问source和sink的定义。所以,我们需要在from语句中声明这三个对象,如:

from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink

我们可以通过导入数据流库来访问这个配置类,它定义了如何在查询中处理数据流的谓词,具体如下所示:

· isSource() 定义了数据是从哪里流过来的。

· iSink()定义了数据可能流向哪里。

有关如何在分析中使用configuration类的详细信息,请参阅https://help.semmle.com/QL/learn-ql/cpp/dataflow.html和https://help.semmle.com/QL/learn-ql/csharp/dataflow.html中的相关内容。

此外,我们还可以通过扩展configuration类,来给不同的框架和环境创建相应的配置,这方面的详细介绍,请访问https://help.semmle.com/QL/ql-handbook/types.html#defining-a-class。

当我们分析Python代码(并且在查询中使用了import semmle.python.security.Paths语句)时,则应该在from语句中声明TaintedPathSource source和TaintedPathSink sink。我们不需要声明配置类,因为TaintedPathSource和TaintedPathSink的定义中已经包含了所需的所有类型信息:

from TaintedPathSource source, TaintedPathSink sink

此外,我们还可以通过添加不同的源点和接收点来扩展查询,方法是在查询中定义它们,或者为特定框架和库导入预定义的源点和接收点。有关更多细节,请参见之前文章中Python路径查询的相关内容。

定义流动条件

实际上,where子句可以定义应用于from子句中声明的变量以生成结果的逻辑条件。我们可以在这个子句中可以使用聚合、谓词和逻辑公式,以便将感兴趣的变量限制在满足相关条件的较小集合中。

在编写路径查询时,通常会包含一个谓词,该谓词仅在数据从源点流到接收点时才成立。

对于C/C++、C#、Java或JavaScript语言来说,我们可以使用hasFlowPath谓词为给定配置定义数据如何从源点流向接收点:

where config.hasFlowPath(source, sink)

对于Python语言来说,我们只需使用flowsTo谓词来定义数据如何从源点流向接收点:

where source.flowsTo(sink)

Select子句

路径查询的Select子句通常由四“列”组成,其结构如下所示:

select element, source, sink, string

其中,element和string列分别表示警报和警报消息的位置,而第二列和第三列(即source和sink)则表示该查询选择的路径图上的节点。该查询生成的每个结果,都会以跟警报查询相同的方式显示在单独的位置上。此外,每个结果还会提供一个相关的路径,我们可以通过LGTM或CodeQL的VS Code插件进行查看。

至于在第一列中选择什么样的element,具体取决于查询的目的,以及要挖掘的安全问题的类型。这对于安全问题来说尤其重要。例如,如果您认为source的值是全局无效的或恶意的,那么最好在源点处显示警报。相反,如果您认为是需要进行清洗处理的元素,则应该考虑在接收点处显示警报。

最后,我们还可以利用select语句中最后一列中定义的字符串,以链接和占位符的形式提供该查询找到的警报或路径的详细信息。

小结

在前面的文章中,我们为读者介绍了如何分析数据流,以及如何进行污点跟踪和指向分析。在本文中,我们不仅为读者详细介绍了如何通过CodeQL平台为Visual Studio Code提供的插件,即CodeQL for VS Code在本地编写和运行查询,并直接在工作区中展示查询结果,同时,还详细介绍了如何编写路径查询。

备注:本系列文章乃本人在学习CodeQL平台过程中所做的笔记,希望能够对大家有点滴帮助——若果真如此的话,本人将备感荣幸。

参考资料:https://help.semmle.com/

如若转载,请注明原文地址
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务