由于深度学习模型强大的建模能力,基于深度学习的漏洞检测方法近年来取得了显著发展。然而,目前基于深度学习的方法几乎仅专注于单一粒度的漏洞检测(函数级或切片级)。事实上,切片级别的漏洞检测方法虽然检测粒度细,但切片难以覆盖完整的漏洞特征,导致模型难以学习到全面的漏洞特征;而函数级别的方法虽然样本包含较为完整漏洞特征,但同时会引入过多漏洞无关的噪声语句,导致模型的检测效果不够理想。同时,已有方法更关注样本是否存在漏洞而无法输出具体的漏洞语句,即检测结果的解释。为此,论文提出了一种多粒度的可解释漏洞检测方法。该方法有效融合了函数级和切片级漏洞检测方法的优点,并能够依靠检测模型本身输出对于检测结果的解释。在真实漏洞数据集上的实验结果表明,该方法的检测效果高于SySeVR和Devign等7个漏洞检测工具,此外,其解释效果在准确率和时间开销方面均优于GNNExplainer 和PGExplainer这2种通用的图神经网络解释器。此外,mVulPreter扫描8个开源软件并发现了28个真实世界的漏洞,证明了mVulPreter具有在真实世界中检测漏洞的能力。
该成果“mVulPreter: A Multi-granularity Vulnerability Detection System with Interpretations”发表在国际期刊IEEE Transactions on Dependable and Secure Computing (TDSC)。TDSC期刊每年出版6期,主要关注计算机及网络安全、可信计算等领域的前沿研究,2021年影响因子是7.329,属于中国计算机学会CCF A类期刊。
论文链接:https://ieeexplore.ieee.org/document/9864301
代码链接:https://github.com/tao7777/mVulPreter
背景与动机
由于深度学习强大的建模和学习能力,已经受到广泛关注。因此,安全研究人员将它们应用于漏洞检测领域。根据模型处理的代码粒度,目前基于深度学习的漏洞检测方法可以分为两大类,即函数级和切片级。针对函数级漏洞检测,一个完整的函数被标注并作为训练样本使用;针对切片级漏洞检测,训练样本是以经常引入漏洞的漏洞关注点(即,敏感API、指针使用、数组使用和整数使用)为程序切片的起点,根据程序依赖分析的结果生成。事实上,一个有漏洞的函数可以涵盖完整的漏洞特征,但会引入了许多与漏洞无关的语句。与函数相比,切片可以更好地捕捉到噪音较小的漏洞特征。然而,切片执行的起点是漏洞的根本原因(root cause),所以并不能保证切片能覆盖漏洞的触发位置。因此,有漏洞的切片中可能会缺失必要的漏洞特征。此外,现有的基于深度学习的漏洞检测方法大多不能解释漏洞检测的结果。他们仅关注待测代码是否存在漏洞,但不能准确地指出哪一行代码更有可能是漏洞语句。
设计与实现
针对上述问题,本文的目标是将切片级细粒度漏洞语义与函数级完整漏洞语义的优势结合起来,构建一个准确且可解释的漏洞检测器mVulPreter。具体来说,我们主要面对两个挑战:
①如何结合切片级和函数级漏洞检测的优势以降低漏报和误报?
②如何对漏洞检测的结果进行细粒度解释?
为了解决上述挑战,mVulPreter的基本思想是针对同样的输入数据,同时训练两个函数级和切片级的漏洞检测模型。其中,切片级模型负责提供一个函数内每个切片的预测概率,从而帮助过滤漏洞无关语义,而函数级模型则负责对已净化语义的函数的检测。此外,切片级和函数级模型均能够为漏洞解释做出贡献。具体而言,为了解决第一个挑战,mVulPreter根据切片级的漏洞检测模型对切片的预测概率实现对函数语义的净化,从而实现更准确的函数级漏洞检测;为了解决第二个挑战,结合函数级模型中注意力机制对切片的权重预测和切片级漏洞检测模型对切片的预测概率,得到每一代码行的重要性分数,从而完成对检测结果的解释。
图1 mVulpreter的具体运行实例
mVulPreter的结构框架如图1所示,其输入是待测函数的源代码,输出是检测结果和关键的漏洞语句(即,漏洞检测结果的解释)。在提取函数的图表征之前,我们首先对源代码进行预处理。首先进行代码的规范化,mVulPreter采用三个等级的规范化方法,使其能够抵御常见的代码修改带来的干扰,也同时保留程序语义。图1中的步骤1-3说明了一个函数的详细规范化过程。针对规范化后的代码,我们使用静态分析工具Joern提取其程序依赖图(PDG)。
为了净化函数内与漏洞无关的语义,我们采用了一个切片级的漏洞检测模型过滤掉对于函数漏洞检测结果贡献较小的切片。首先,我们生成切片数据从而完成对切片级模型的训练和测试。具体地,给定一个函数,我们以四类漏洞关注点为切片执行的起始点,分别执行前向和后向切片(即,数据和控制依赖分析)。例如,在图1中*data(指针类型),dataBuffer(数组类型),dest(数组),和strncat(敏感API)被识别为漏洞关注点,并分别生成图1中的切片S1-S4。对于以上切片,我们对其分别进行节点嵌入和切片嵌入。其中,节点嵌入是为了将节点中的代码嵌入为向量表示。我们采用预训练的Sent2Vec依次对每个节点中的语句进行嵌入。切片嵌入的目的在于刻画切片的图结构,因此我们采用Gated Graph Recurrent Network (GGNN)对切片进一步实现切片嵌入。我们使用该向量进行切片级漏洞检测模型的训练和预测,其输出为切片为有漏洞的预测概率。函数中预测概率最低的切片可以被认为与漏洞的关联性最小,换句话说,它们对函数级漏洞检测的结果贡献非常小。因此,过滤掉这些贡献小的语义能够减少函数内噪声语义。具体而言,我们对函数中每个切片的预测概率进行排序,并根据设定的过滤比例(本文中设定为25%)丢弃一定比例的切片。例如,在图1的步骤6中,我们丢弃了预测概率最低的切片S4。因此,函数中与漏洞相关度最低的语义(即,切片)被过滤,保留的部分进行下一步的函数级漏洞检测。
最后一个阶段的目标是检测函数级漏洞并输出解释。我们利用一个基于注意力的GNN模型作为函数级的漏洞检测器。其中,注意力机制能够增强重要切片在其函数的影响力,同时能够为函数中的每个切片分配注意力分数。我们将过滤切片后的函数(仅保留函数中与漏洞相关的切片)输入函数级漏洞检测模型,最终得到待测函数的检测结果和函数中每个切片的注意力分数。最后,结合切片的注意力分数和切片级别模型输出的预测概率,我们能够为函数中每个切片计算出一个具体的分数。因此,每个代码行通过累积它所属的切片分数被赋予得分。代码行的得分越高,它越可能是一个有漏洞的代码行,从而完成对漏洞检测结果的解释。
发现与讨论
在检测有效性方面,本文在Big-Vul数据集上分别进行参数实验并与最先进的漏洞检测器进行对比实验。实验结果如图2所示,可以看出当我们丢弃预测概率排名最后25%的切片时,mVulPreter可以达到最佳检测性能。此时,mVulPreter的检测效果优于Checkmarx、FlawFinder、RATS、TokenCNN、StatementLSTM、SySeVR和Devign,如图3所示。结果说明过滤掉函数中与漏洞无关的语义能够有效提升模型的检测效果。
图2 mVulpreter在不同切片过滤比例下的检测性能
图3 对比实验结果
在解释效果方面,我们关注mVulPreter和两个常用的GNN解释器(GNNExplainer和PGExplainer)的准确性和时间性能。我们通过解释结果中是否包含了漏洞补丁的修改语句来评估解释结果的准确性。此外,我们从数据集中随机选择500个函数进行解释器时间开销的评估。实验结果如表1所示,可以看出mVulPreter解释的有效性和运行效率都超过了对比解释器。就效率而言,mVulPreter的运行时间明显少于这两个比较工具。因为GNNExplainer必须对每个解释的实例进行重新训练,所以在解释大量的节点时耗时巨大。相比之下,PGExplainer一次性针对全部训练数据进行模型训练,因此,它比GNNExplainer花费的时间更少。然而,mVulPreter不是一个额外的解释器,它不需要任何高开销的训练过程,而仅需要对每个切片注意力和预测概率进行简单的算术运算就可以得到解释结果,因此解释耗时很短。
表1 漏洞解释方法的准确性和时间开销实验结果
此外,我们进行了一个案例研究,以检验mVulPreter在现实世界中发现漏洞的实用性。具体来说,我们下载了四个常用的开源软件作为待测目标:Libav、Xen、Openssl和Thun-derbird。mVulPreter总共扫描函数635,451个,总代码数超过2500万行。mVulPreter的检测时长为92.9分钟,共检测到17个1-day漏洞和11个0-day漏洞,如表2所示。
表2 mVulPreter检测到的真实软件漏洞
详细内容请参见:
Deqing Zou, Yutao Hu, Wenke Li, Yueming Wu, Haojun Zhao, and Hai Jin. mVulPreter: A Multi-granularity Vulnerability Detection System with Interpretations. IEEE Transactions on Dependable and Secure Computing, 2022.
https://ieeexplore.ieee.org/document/9864301
声明:本文来自穿过丛林,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。