2018_2021_TDSC_SySeVR_A Framework for Using Deep Learning to Detect Software Vulnerabilities
源码:https://github.com/SySeVR/SySeVR
1 现有方法的缺点
- 基于代码相似性的方法主要检测代码克隆引起的漏洞,对于非代码克隆引起的漏洞具有较高的假阴性率
- 基于规则的方法需要人类专家定义特征,过程繁琐易出错
- 基于机器学习的方法主要分为三种:
- 基于深度学习的漏洞检测方法VulDeePecker有四个明显的不足之处:
- 只考虑与库/API函数调用相关的漏洞
- 只利用由数据依赖引起的语义信息,没有考虑控制依赖
- 只考虑双向长短期记忆BLSTM的特定RNN
- 未解释假阳性和假阴性的原因。
2 本文贡献点
- 提供第一个基于深度学习用于检测C/C++源码漏洞的系统框架SySeVR
- 引入Syntax-based Vulnerability Candidates(SyVCs)和Semantics-based Vulnerability Candidates(SeVCs)的概念,分别表示漏洞的语法特征和语义特征(数据依赖和控制依赖),同时本文设计了自动化提取SyVCs和SeVCs的算法。
- SySeVR使用多种神经网络检测多种类型漏洞,SySeVR表明双向RNNs,尤其是双向门控递归单元(BGRU)比CNNs更有效,CNNs比DBNs更有效
- BGRU的有效性依赖于训练数据,如果某些语法元素经常出现在代码的漏洞(非漏洞)片段中,那这些语法元素就会导致高的假阳(阴)性率,解释了假阳性率和假阴性率的原因
- 考虑更多的语义信息(比如控制依赖和数据依赖)减少30.4%的假阴性率
3 SySeVR框架
- 在图像处理领域,研究者从图像中提取出很多的region proposal,然后向量化,使用深度学习训练检测。对于程序代码而言,也可以模仿图像中的操作,利用proposal的思想来完成漏洞检测。如果使用函数作为proposal,则粒度太高,无法定位具体漏洞位置;如果使用语句作为proposal则会导致正负样本不均衡,且分割了代码语句之间的语义信息。因此,本文采用代码片段作为proposal,以其为单位进行代码漏洞检测。
- SySeVR框架依次生成程序的SyVCs,SeVCs,对SeVCs向量化后使用深度学习进行训练和检测。
- SyVCs包含了符合某种漏洞语法特征的代码元素(比如函数调用、指针使用)。SeVCs是由SyVCs生成的,在SyVCs的基础上增加了代码的语义信息,它是一部分相互数据依赖和控制依赖的代码语句的集合。下图是一个直观的示例,SyVCs中的每一个红框代表一个SyVC,不同的SyVC可以相互包含,因为其代表不同的漏洞,比如第18行。
3.1 数据集
- 从NVD和SARD收集数据。对于NVD,本文收集了1591个开源C/C++程序,其中874个是有漏洞的。对于SARD,本文收集了14000个C/C++程序,其中13906个是有漏洞的,这里的有漏洞包含bad和mix,bad指的是有漏洞,mix指的是漏洞版本和修复好的版本都有。本文共收集了15591个程序,其中14780个是有漏洞的,对应126种CWE漏洞类型。
3.2 获取漏洞语法特征
- 使用抽象语法树节点属性表示漏洞语法特征 H={hk}1≤k≤β
- 本文通过checkmarx工具提取出了4种类型的漏洞语法规则
- API/库函数调用(FC):包含了811个函数调用,对应于106种CWE漏洞。
- 数组使用(AU):包含87种CWE漏洞,比如数组元素访问,地址计算等等。
- 指针使用(PU):包含103种CWE漏洞,比如不合法的指针计算、引用、传参。
- 算术表达式(AE):包含45种CWE漏洞,不合法的算术表达式,比如整数溢出等。
- 下图为这4类漏洞覆盖的CWE漏洞类型。
3.3 提取SyVCs
- SyVCs指与漏洞语法特征 H={hk}1≤k≤β 中某一特征相匹配的代码元素
- 可用于漏洞检测的代码元素可能是AST的叶子节点或中间节点,因此遍历抽象语法树的节点,如果该节点与某个漏洞语法特征相匹配,则将该节点对应token加入到SyVCs中,具体的伪代码如下。
将代码元素与漏洞特征进行匹配,示例如下图所示
- API/库函数调用(FC)判定条件:该代码元素是一个函数调用,且属于811种函数调用之一。
- 数组使用(AU)判定条件:该代码元素是是由一个标识符声明语句声明的,且声明语句中包含'['和']'。
- 指针使用(PU)判定条件:该代码元素是是由一个标识符声明语句声明的,且声明语句中包含'*'。
- 算术表达式(AE)判定条件:该代码元素是一个表达式语句且包含'=',并且等号右端有两个以上的元素。
3.4 将SyVCs转化为SeVCs
SyVCs表示与漏洞相关的语法特征,SeVCs表示与漏洞相关的语义特征,将SyVCs转化为SeVCs,就是利用程序切片技术在SyVCs的基础上添加其控制依赖和数据依赖相关的语句
- 控制流图CFG,它的节点是函数语句,边为有向边,表示相邻语句间的运行先后关系。
- 数据依赖:如果控制流图中有一条A->B的路径,且在A语句中计算得到的值会在B语句中使用,则称B数据依赖A。
- 控制依赖:如果控制流图中有一条A->B的路径,且B是否执行需要看A执行的结果, 则称B控制依赖A。
- 程序依赖图PDG,它的节点是CFG中节点(函数语句),边为有向边,表示相邻语句间的数据依赖或控制依赖
- 前向切片:一个SyVC代码元素的前向切片包含PDG中从该代码元素出发所有可达节点的语句
- 过程间前向切片:包含前向切片的所有语句,以及PDG中SyVC代码元素通过函数调用可以到达SyVC代码元素的节点的语句
- 后向切片:一个SyVC代码元素的后向切片包含PDG中所有可达该代码元素的,且以该代码元素为终点的节点的语句
- 过程间后向切片:包含后向切片的所有语句,以及PDG中通过函数调用可到达SyVC代码元素的节点的语句
- 程序切片:由过程间前向切片和过程间后向切片的语句融合而成,删除其中重复的部分
将SyVCs转化为SeVCs步骤如下:
1. 生成每个函数的程序依赖图PDG。
2. 对于SyVCs中的每一个元素生成其程序切片。首先生成SyVC的前向切片和后向切片,然后融合前向切片和被该函数调用的函数的前向切片,得到过程间前向切片;融合后向切片、被该函数调用的函数的后向切片,以及调用该函数的函数的后向切片,得到过程间后向切片;融合过程间前向切片和过程间后向切片得到程序切片。
3. 将程序切片转化为SeVCs。首先将程序切片中的属于同一函数的所有语句按照在源代码中出现的顺序存入一个集合,对于不同的函数的语句而言,调用者的语句在被调用者的语句之前。调整好顺序后,该集合就是SyVC对应的SeVC,对SyVCs中的每一个SyVC这样处理,就可以生成SeVCs。将SyVCs转化为SeVCs的伪代码如下所示:
下图是一个以源代码第25行的data为SyVC的一个转换示例,体现了从PDG到程序切片再到SeVC的完整过程。
- 前向切片26-1-3-4
- 后向切片:7-9-10-11-13-14-16-18-22-23-24
- 程序切片:7-9-10-11-13-14-16-18-22-23-24-26-1-3-4
本文使用joern生成程序依赖图,根据算法2生成SeVCs,最终生成SeVCs的结果如下表所示:
3.5 将SeVCs编码成向量
- 将SeVCs转化为符号表示,首先移除非ASCII字符和注释,然后将用户自定义的变量名映射为符号名(V1、V2),再将用户自定义的函数名映射为符号名(F1、F2)
- 使用word2vec将符号表示转化为向量,首先将SeVCs的符号表示通过词法分析分割为符号序列,例如将“V1=V2-8;”分割成“V1”、“V2”、“-”、“8”、“;”,然后将符号序列中每个符号转化为固定长度的向量,将每个符号的向量拼接在一起得到每个SeVC的向量表示。
- 由于神经网络的输入是固定长度的向量,而不同SeVC具有不同数量的符号,所以得到的向量长度不同,因此需要对向量长度进行处理。指定一个合适的长度 θ 。
- 若向量长度小于θ,则在末尾填充0
- 若向量长度大于θ ,则需要去除一部分保证SyVC处于向量的中间位置
- 若SyVC左边的子向量长度小于 θ2 ,则删除向量最右边部分,使向量长度为 θ
- 若SyVC右边的子向量长度小于 θ2 ,则删除向量最左边部分,使向量长度为 θ
- 否则保留SyVC左边 ⌊θ−12⌋ 和右边 ⌈θ−12⌉ 部分,使向量长度为 θ
- 本文中每个符号向量长度为30,一个SeVC最多500个单词, θ 为15000
3.6 生成SeVCs的标签
分两步生成SeVCs标签
- 首先生成初始标签。针对NVD数据集,若一个SeVC的diff文件包含删除或修改的语句前缀有'-',则被标记为1,若包含的移动的语句前缀有'-',且这个文件包含一个已知的漏洞,则被标记为1;其他情况都标记为0。针对SARD数据集,若一个个SeVC提取自一个good程序,则被标记为0;如果提取自bad或mix程序,则分情况,若该SeVC包含至少一个漏洞语句则标记为1,否则标记为0。
- 使用交叉验证的方式来修正标签。将数据集分为5份,4份训练,1份验证。在验证过程中发现的假阴性样本(有漏洞的样本检测为无漏洞)将会被考虑是否标错了,对于这类样本手动检查修正标签。
4 实验
- 本文将SySeVR-BLSTM模型分别检测4种类型的漏洞,与VulDeepecker模型的结果进行比较,如下图所示,可见SySeVR-BLSTM模型的结果明显优于VulDeepecker模型,证明了该模型可以有效的检测多种不同类型的漏洞。
- 本文比较了使用LR,MLP,DBN,CNN,LSTM,GRU,BLSTM,BGRU等神经网络进行训练和检测的效果,发现使用BGRU对SeVCs进行训练检测的效果最好。双向RNNs比单向的RNNs和CNN更好,RNNs和CNN比DBN和浅层网络更好,假阴性率高于假阳性率(没检测出来的偏多)。
- 本文对比了用于向量表示的word2vec和词袋法,基于词袋法,分别使用LR、MLP、CNN和BGRU进行实验,结果如下表所示。与表3进行对比可知,词袋法在浅层的神经网络中效果较好,而word2vec更适用于深层神经网络,因为词袋法无法捕获上下文信息。
- 本文分别在各个模型上分别只使用数据依赖(DD)和同时使用数据依赖控制依赖(DDCD)进行实验,结果如下表所示,可见控制依赖可以很大程度上提升模型的效果。
- 将本文的SySeVR-BGRU模型与Flawfinder、RATS,checkmarx、VUDDY、VulDeepecker进行比较,可见SySeVR-BGRU模型显著优于之前所有的模型。
5 不足之处
- 只适用于C/C++程序源码,不能用于其它语言或可执行文件
- SARD数据可能不能表示真实软件程序
- 生成SyVCs和SeVCs的算法可以进行改进,以包含更多的语义信息
- 仅使用一种模型来检测多种漏洞,可思考利用多模型方法检测多种漏洞
- 在切片级别检测漏洞,可探索更有效的方法
- 可探索更有效的自动化打标签方法