本文是基于CNN-GAP可解释性模型的软件源码漏洞检测方法论文的阅读笔记,这是读到目前论文中唯一一篇中文论文,内容我个人觉得非常不错,很有借鉴价值,尤其是文中提出的关于源码漏洞的可解释性可视化分析的方法,对学习研究有很大的帮助。下面就随我一起好好欣赏这篇文章!
源代码漏洞检测;深度学习;神经网络可解释性
作者首先总结了近年来对于深度学习应用于源代码漏洞检测相关研究内容的局限性:
基于以上局限性,作者提出了一种基于卷积神经网络(CNN)和全局平均池化(GAP)可解释性模型的源代码漏洞检测方法。本文提出的漏洞检测过程如下:
最终通过与VulDeePecker模型(第一篇论文精读)进行对比分析, 表明CNN-GAP模型能达到相当甚至更好的性能,且具有一定的可解释性。
首先作者介绍了传统的源代码漏洞检测方法:基于静态分析的检测方法和基于动态分析的检测方法。
基于静态分析的检测方法:根据人工经验和专家知识构建相应的漏洞模式进行检测。其特点为:
其中包括:
基于动态分析的检测方法:将源代码编译成二进制文件后运行程序进行检测。其特点为:
其中包括:
同时作者总结了近期涌现的一些使用深度学习应用于源代码漏洞检测的框架,对这些框架的研习与总结对于之后的学习非常有帮助。
基于Bi-LSTM神经网络模型的漏洞检测方法
该方法利用抽象语法树表示函数源码,可实现跨项目函数的迁移表示和漏洞检测。此种LSTM框架可以实现对异构数据源在函数粒度上的漏洞检测。
基于深度学习和正样本模糊生成的漏洞检测系统 D e e p B a l a n c e DeepBalance DeepBalance
该系统通过生成“非真实”的正样本以平衡正负样本的数量,然后利用Bi-LSTM框架进行漏洞检测,提高了漏洞检测的准确率。
跨域漏洞检测系统 C D − V u l D CD-VulD CD−VulD
该系统利用度量迁移学习框架, 最小化源域与目标域之间的分布差异来学习跨域表示,并通过Bi-LSTM框架进行漏洞检测,大幅提高了漏洞检测的准确率。
基于Bi-LSTM神经网络模型的漏洞检测系统 V u l D e e P e c k e r VulDeePecker VulDeePecker
该系统将程序源码表示为语法、语义相关的代码小片段,通过Bi-LSTM神经网络模型实现了代码小片段粒度的漏洞检测。
漏洞检测系统$ \mu VulDeePecker 和 和 和VulDeeLocator$
这两种系统在 V u l D e e P e c k e r VulDeePecker VulDeePecker的基础上,分别实现了对多类型漏洞的检测和对漏洞位置的细粒度定位。
基于后端分类器-随机森林的漏洞检测系统
该系统通过卷积神经网络提取函数源码中的特征信息,并将提取的信息输入给后端分类器-随机森林以实现漏洞检测。
基于图神经网络的漏洞检测系统 D e v i g n Devign Devign
该系统将程序源码表示为代码属性图,然后利用图神经网络学习代码属性图中的语义信息进行漏洞检测。
基于注意力机制的漏洞检测工具 V u l S n i p e r VulSniper VulSniper
该工具将程序源码表示为代码属性图,并用144维向量来表示代码属性图中各节点之间的关系,通过注意力机制来捕捉关键的漏洞特征实现函数粒度的漏洞检测。
以上总结的各种基于神经网络的漏洞检测模型虽然可以从样本中学习到漏洞特征,但是仍有以下缺点:
基于以上分析,作者认为当前针对基于深度学习的源代码漏洞检测存在以下困难和挑战:
针对以上困难和挑战,本文提出了一种基于卷积神经网络和全局平均池化的可解释性源代码漏洞检测方法:
通过对比实验发现,本文提出的CNN-GAP模型有如下优点:
这部分是本文的核心内容,详细阐述基于CNN-GAP可解释性模型漏洞检测方法的设计思路。其中具体包括:
对函数源码的预处理主要是处理源码中的自定义标识符,归一化的基本原则为:
对源代码中的标识符进行归一化处理的作用如下:
归一化标识符是对函数名、变量名、字符串等进行归一化,归一化的过程如下:
对源代码的归一化处理过程示例如下:
对源码的预处理不仅包括对标识符的归一化处理,还包括对分隔符的处理。根据分隔符的不同形式,可以建立3种分隔符的语料库,具体分隔符分布如下图所示:
基于以上内容,可以总结对于函数源码预处理的过程如下:
对于神经网络模型的构建应该遵循以下原则:
其中最重要的就是如何对模型的输出结果进行可行性解释,为了解决这个问题,本文采用卷积神经网络和全局平均池化型结合的模型对输入向量进行处理,如下图所示:
对于源码漏洞检测问题,我们的输入都是一维的列向量,所以CNN-GAP模型中的卷积模块采用1维卷积(Conv1D) 。在整个过程中,需要注意以下几个参数的含义:
最后通过 P c P_c Pc概率值的大小判断结果为类 c c c的概率,需要注意的是,在 S o f t m a x Softmax Softmax层忽略偏差项bias,也就是将 S o f t m a x Softmax Softmax层的偏差项设置为0。最后将 F k = ∑ x f k ( x ) F_{k}=\sum_{x} f_{k}(x) Fk=∑xfk(x)代入 S c S_{c} Sc,得到:
S c = ∑ k W c , k ∑ x f k ( x ) = ∑ x ∑ k W c , k f k ( x ) S_{c}=\sum_{k} W_{c, k} \sum_{x} f_{k}(x)=\sum_{x} \sum_{k} W_{c, k} f_{k}(x) Sc=k∑Wc,kx∑fk(x)=x∑k∑Wc,kfk(x)
为了使模型的输出结果可解释,我们需要知道以下两点:
所以使用 M c ( x ) ( M c ( x ) ∈ R ) M_{c}(x)\left(M_{c}(x) \in R\right) Mc(x)(Mc(x)∈R)表示特征图中每个空间位置 x x x对类别 c c c的重要性,那么输入 S o f t m a x Softmax Softmax层的 S c S_c Sc还可以表示为:
S c = ∑ x M c ( x ) S_{c}=\sum_{x} M_{c}(x) Sc=x∑Mc(x)
由式(1)和式(2),可以得到:
M c ( x ) = ∑ k W c , k f k ( x ) M_{c}(x)=\sum_{k} W_{c, k} f_{k}(x) Mc(x)=k∑Wc,kfk(x)
上式表明了特征图中空间位置 x x x对划分为类别 c c c的重要性 M c ( x ) M_{c}(x) Mc(x),可以通过网络模型中的参数 f k ( x ) f_{k}(x) fk(x)和 W c , k W_{c,k} Wc,k计算得出。根据以上内容,我们就可以通过计算每个token对分类的贡献度来实现漏洞的可解释性,这也是本文的最大贡献。同时我们也需要注意以下几个问题:
同时,作者提到当前缓冲区溢出漏洞数据集比其他类型的漏洞数据集更丰富,所以本文主要针对缓冲区溢出类型的漏洞对模型的性能进行实验测试分析。
本文提出的模型测试环境也很有参考价值,当我们复现代码的时候,最好和本文软硬件环境一致。
本文实验测试的硬件环境如下:
本文实验的软件环境如下:
本模型测试所使用的数据集来自: Russell论文中的数据集。其中包括以下几种类型的漏洞函数:
主要采用CWE-119缓冲区溢出类型漏洞作为基本数据集,共进行了三个实验。
实验1将CNN-GAP模型与Russell模型在CWE-119类型的数据集上进行对比,其中一些细节设置如下:
在Russell测试集上的结果如表1所示,我们可以得到以下结论:
图4为CNN-GAP模型和Russell模型在CWE-119类型漏洞函数进行实验测试的Precision-Recall对比图,我们可以得到以下结论:
同时作者也分析了产生以上结论的原因:
由于本实验数据的正负样本不均衡,所以作者还采用ROC曲线图对CNN-GAP模型的性能进行评估,评估结果如图5所示。需要注意的是,曲线向下覆盖的面积即为AUC值,理想情况下AUC值为1.0,此时该分类器为完美分类器。可以看到CNN-GAP模型采用Russell测试集得到的ROC曲线,AUC值为0.95,接近1.0,说明模型整体性能较好。
实验2将已训练好的CNN-GAP模型与VulDeePecker模型,在VulDeePecker测试的CWE-119类型数据集上进行对比,以验证模型的泛化性能。对比实验结果如表2所示。
因为CNN-GAP模型直接在VulDeePecker模型的测试集上进行实验,两种模型的实验结果非常接近,由此可以说明CNN-GAP模型的泛化性能较好。那产生这样结果的原因是什么呢?
主要是因为VulDeePecker模型通过切片只保留了几种易出现漏洞的元素(如API调用、数组变量、指针变量等)及其控制流和数据流依赖,虽然精简了代码量,但是丢失部分代码特征。而CNN-GAP是对整个函数源码进行处理,不仅简化了预处理流程,而且包含了所有代码特征。这也说明了CNN-GAP模型的实用性优于VulDeePecker模型。
实验3将CNN-GAP模型输出结果用CAM进行可视化输出,实现对漏洞特征的可解释性分析。需要注意的是,虽然我们函数源码被切分为300个token序列,但是如果我们将这300个token序列全部进行分类结果的重要性表示,在分布图中就会十分拥挤,不便于展示和理解,所以实验3将GAP中的特征映射到函数源码的每条语句,而不是每个token。
图6的代码片段是CWE-119类型的堆溢出漏洞。 其中使用strncat函数将source所指向的字符串追加到data所指向的字符串的结尾时,由于strncat函数没有对source的大小进行检查,所以可能会超过data的内容大小,从而造成堆溢出漏洞。
图8为上述代码经过神经网络模型判别后,根据可解释性分析输出的可视化结果。其中数值大于0表示对应的代码行与漏洞相关,且该数值越大,贡献度越大。通过可视化结果图可以很清楚的看到行10—行13与漏洞特征相关,这几行正好对应我们刚才说的strncat漏洞代码、data堆内存、source[]数组。
图7的代码片段是CWE-119类型的栈溢出漏洞。其中使用memcpy函数将data内存中的字符串复制到dest内存区域中时,由于没有比较dest的内存大小和复制字符串的大小,所以会造成栈溢出。
通过图9的可视化结果分析来看,也可以很明显的发现第12,13行与漏洞特征相关,这几行代码包含了memcpy漏洞代码。
通过实验3可以在可视化的柱状图得知函数源码中漏洞的大概位置,实现在代码行粒度的漏洞定位,从而实现对源码漏洞特征的可解释性分析。
本文提出了一种基于CNN-GAP可解释性模型的源码漏洞分析方法,并对CWE-119缓冲区溢出类型的漏洞进行检测。本文的主要贡献如下:
本文所提出模型的局限性如下:
针对以上局限性,本文提出的模型还可以从以下几个方面进一步完善:
本文使用CNN(卷积神经网络)和GAP(全局平均池化)方法解决了源码漏洞特征的可解释性分析的问题。基本思路是首先将函数源码进行预处理,将源码中的自定义标识符进行归一化处理,从而缓解库外词过多的问题,然后进行神经网络模型的构建,本文提出的模型中包含了3层的卷积层,每层卷积层包含128个卷积核,最终每个卷积层生成128个特征图,将生成的特征图与其权重相乘输入给 S o f t m a x Softmax Softmax层,最终实现有无漏洞的判定。
本文所提出的方法的优点包括:
本文所提出的方法的缺点包括: