CodeQL缝隙发掘实战

CodeQL是一个白盒源码审计东西,它以一种十分新颖的方法组织代码与元数据,使研究人员可以“像查询数据库一样检索代码”,并发现其中的安全问题。GitHub于去年收买了开发CodeQL的公司Semmel,并与其联合成立了GitHub Security Lab,Semmel在此前推出了面向开源社区和企业的源代码剖析平台LGTM,这个平台可以自动化的发现并预警GitHub上开源软件的安全问题,一起,与CodeQL一样对开源社区与开发者保持免费。

本文将从CodeQL的根底知识出发,跟随GitHub Security Lab发布的互动式体验课程[1],带领读者认识CodeQL在代码审计中的实践运用——在特定版本的uboot源码中发现多个安全问题,经过一段CodeQL查询句子定位到9个CVE缝隙。

一、CodeQL工作原理

CodeQL的整体工作流程[2][3]如下图所示:

CodeQL漏洞挖掘实战

CodeQL的查询需求树立在一个数据库的根底之上,这个数据库是经过Extractor模块对源代码进行剖析、提取后得到的。数据库树立之后,咱们就可以运用CodeQL去探究源码,并发现代码中的一些已知问题。

关于编译型言语,CodeQL会在树立数据库时“模仿”编译的过程,在make等编译东西链调用gcc等编译器时,用相同的编译参数调用extractor模块取而代之,搜集源代码的所有相关信息,如AST抽象语法树、函数变量类型、预处理器操作等等。关于解说型言语,由于没有编译器的存在,CodeQL会以盯梢执行的方法获取类似的信息。

二、CodeQL运用

运用CodeQL CLI对代码库房运行剖析[4]后,咱们就得到了一个“快照数据库”[5](SnapshotDatabase),这个数据库中存储了代码库房在特定时刻点(数据库树立时)的层级表明方法,包含AST语法树、CFG操控流程联系和DFG数据流向联系。在这个数据库中,代码中的每一个要素,比方函数界说(Function)、函数调用(FunctionCall)、宏调用(MacroInvocation)都是可以被检索的实体。在这些根底上,咱们再编写CodeQL句子对代码进行剖析。

CodeQL环境的安装在这里不再赘述,在官方教程[6]与本文涉及的课程内容中[7]都有详细阐明。在VSCode中导入数据库之后,咱们就可以开端编写第一条CodeQL句子了。

三、基本语法

咱们以字节序转化函数为例,查找uboot代码库中ntohs、ntohl、ntohll的界说。

CodeQL漏洞挖掘实战

从上面的代码段中咱们可以看到,CodeQL遵从与SQL类似的基本语义:select … from … where …。但不同的是CodeQL中又加入了面向目标的思想,比方m.getName()可以获取查询目标的姓名,再调用另一个函数进行正则匹配取得咱们终究需求的名称匹配逻辑表达式。本例CodeQL的运行输出如下图所示,表格中每一行的蓝色代码片段都可以点击跳转到uboot代码库中,相应宏的界说方位。

CodeQL漏洞挖掘实战

常用的几种查询目标类型:

  • Function 函数界说、函数声明
  • FunctionCall 函数调用
  • Macro 宏界说
  • MacroInvocation 宏调用
  • Expr 表达式
  • AssignExpr 赋值表达式(是Expr的子集)
  • ConditionalStmt 条件表达式

经过上面的方法,咱们可以运用CodeQL对代码中的基本单元进行查询检索。更进一步,咱们可以界说class将杂乱的判断条件进行封装,输出更精确的成果。

咱们对上一个比如中宏调用的查询进行延申,经过界说一个NetworkByteSwap类,这个类代表符合“某些特征”的表达式的全集,在本例中,咱们限制咱们需求的是包含ntohs、ntohl宏调用的表达式,并经过from n select n的方法将它们简单的罗列出来

CodeQL漏洞挖掘实战

以上CodeQL句子的输出如下图所示。

CodeQL漏洞挖掘实战

咱们点击返回的第1条成果,可以看到编辑器会跳转到表达式所在的文件,并将整条表达式选中高亮起来,十分直观。

CodeQL漏洞挖掘实战

学到现在,咱们就可以用CodeQL来测验一下挖缝隙了!

回忆上一步,咱们界说了一个NetworkByteSwap类,用于挑选出调用ntohs的表达式。接下来,咱们引入CodeQL中的污点盯梢模块,指定这些表达式为污点源(source),并设置数据会聚点为memcpy的第3个参数。依据Linux的manpage[8],memcpy函数的第3个参数是待仿制数据块的长度,由于ntohs是进制转化的函数,因此经过ntohs输入的数据很有可能是用户可控的参数值,经过这条路径传递给memcpy,就能转化为用户可控的内存操作。这便是缝隙的本源。将这个数据流联系转化为CodeQL代码如下。

CodeQL漏洞挖掘实战

咱们只在上一个比如的根底上增加了约20行代码,运行之后,得到9条成果,依据课程介绍[9],此刻应该可以得到9个CVE缝隙了。但笔者才疏学浅,依据CVE缝隙库中的描述,粗略统计往后,能直观看出的缝隙点的CVE缝隙有6个,各位读者可以自行测验。

这便是整个U-Boot Challenge课程的尾声,用40行的代码,挖出了一箩筐CVE缝隙。

CodeQL漏洞挖掘实战

四、结语

代码审计并不是什么新兴范畴,业界、学术界咱们都能找到许多老练的东西,如Fortify SCA、RIPS、Coverity等等,商业软件如Fortify提供了十分完善的规则库,它们可以快速、自动化的发现通用型的安全问题。而CodeQL更接近于一个剖析结构,它赋能研究人员对审计目标进行更为杂乱的安全建模,但一起也更依赖研究者对审计目标、底层技术有更为深化的了解。简单的缝隙可以靠东西发现,更杂乱的缝隙就需求靠人去发掘了。CodeQL作为一个剖析结构,在专家经验的加持下,才可以发挥其最大的功效,这是它的缺陷,但它作为一个结构,可以提供丰厚的API和简练的语义使研究者可以快速验证剖析思路、发现特定场景产生的缝隙,这种高度自由也是其不行忽视的优点。