作者:李韵等 来源:软件学报
目录
一、介绍
二、基于机器学习的软件漏洞挖掘流程
三、代码的表征形似
3.1 基于软件代码的度量
3.2 基于Token的表征
3.3 基于抽象语法树的表征
3.4 基于图的表征
四、挑战
早期,根据是否依赖出现运行可将漏洞挖掘技术分为:
!!!注意:上述漏洞挖掘方法一般适用于小型规模软件;但在应对大型复杂软件系统以及变化多样的新型漏洞时,通常无法满足需求。
当前,基于机器学习的漏洞挖掘研究应运而生,静态分析仍然是当前软件漏洞的主要手段,基于静态分析的漏洞挖掘方法和动静态分析相结合的漏洞挖掘方法是近年来研究的重点和热点。
基于静态分析的漏洞挖掘方法面向静态分析场景,通过机器学习方法对代码的词法、语法、控制流和数据流等静态特征进行分析和学习,从而发现代码漏洞。
面向动态分析场景的研究主要针对缓解模糊测试、符号执行等动态分析方法存在的程序路径覆盖率低、路径爆炸以及约束求解难等问题,因此机器学习在动态分析中起到的作用是:用于测试输入生成与筛选、路径约束求解以及模糊测试参数配置预测...
本文主要本文主要侧重于基于静态分析的漏洞挖掘方法和动静态分析相结合的漏洞挖掘方法两类。
(总而言之,漏洞挖掘主要靠静态分析,动静态分析打辅助。)
数据获取模块:收集大量用于训练的软件程序相关数据(如已知是否包含漏洞的二进制代码、源代码、Github 提交信息等)以及用于评估的目标程序相关数据。
(1)在训练阶段:
根据计算得到的特征表达与原始标签的差异构建损失函数,通过优化方法来最小化损失函数,从而不断调整模型的参数。经过若干次训练,得到一个用于进行漏洞分类或预测的分类器模型。
(2)检测阶段
首先将目标代码数据集按照同样的方法进行数据表征;接着,将表征得到的向量送入到由训练阶段得到的分类器模型,得到目标代码的分类或者预测结果;最后,结合测试标签集计算模型算法的精确率(precision)和召回率(recall)等,进行模型评估和调优。
从上述流程可以看出,基于机器学习的软件漏洞挖掘模型主要对数据表征模块中的代码表征形式与编码模型、模型训练模块中的机器学习模型进行改进,以优化漏洞挖掘的准确率和效率。
数据表征模块:实现代码的特征选择过程,即从源数据(数据获取模块收集到的软件程序相关数据)中根据代码表征形式提取最具代表性的信息,并转化成可供后续机器学习模型训练的数值数据。主要讲一下:将程序代码视为一种特殊的文本形式,并通过自然语言处理(natural language processing,简称 NLP)领域中常用的技术来处理.如基于Token 的表征形式提取的代码段、函数调用序列以及程序执行路径等文本序列,通常使用词袋模型、TF-IDF 算 法和 N-gram 模型将源数据视为与频率相关的术语集合,从而将源数据映射到向量空间。树和图的表征形式则通常从代码解析得到的树和图各节点中提取代码的语法信息,通过一定的方式组织成 NLP 领域中的语料形式,再通过词向量模型训练成对应的分布式向量表示。其中,常用的词向量模型有 word2vec。word2vec可分为两种模型:一种是根据周围词来预测中心词的连续词袋模型(continuous bag-of-words model,简称CBOW);另一种是根据中心词来预测周围词的Skip-gram模型。
模型训练模块:选择对当前问题最优的机器学习模型。漏洞挖掘领域常用的机器学习模型有:逻辑回归、随机森林和支持向量机(SVM)等有监督机器学习模型;常用的深度学习模型:多层感知机(multi-layer perception,简称MLP)、卷积神经网络(convolution neural netwaork,简称CNN)、循环神经网络(recurrent neural network,简称RNN)以及长短期记忆网络(long short-term memory,简称LSTM)等。
向量表示是机器学习模型的实际输入,所以需要将源数据转换为能够表现漏洞特性的向量形式。由于需要保留源数据的语义信息(例如数据依赖和控制依赖),通常引入中间表示作为源数据与其对应的向量表示之间的“桥梁”。中间表示即本文提到的代码表征。
常见的代码表征形式有代码度量、Token序列、抽象语法树和图。4 种形式的特征来源、检测速度及检测效果的对比关系见下表。
代码度量是一组用于衡量软件质量的软件度量值 。常用的代码度量指标有代码行数、圈复杂度、继承深度和类耦合等。基于代码度量的表征方式通过选取若干个代码度量指标,得到能够概括代码整体信息的度量序列,使用该度量序列表示相应代码。
基于代码度量的表征方式通过若干个代码度量指标得到对应代码的度量序列,即:这种度量序列通过量化程序属性来表示代码的各种信息,并且量化表示的序列非常适合统计分析,因此检测速度快。虽然相关研究不断地扩大代码度量的范围,但是由于代码度量是基于程序的整体属性,因此与漏洞代码本身关联性不强,检测能力较差。
基于 Token 的表征形式通过对源代码进行词法分析得到,即:在编译过程中扫描源代码的字符流,依据程序语言的词法规则标记源代码的标识符、关键字、函数名和运算符等重要信息,从而将源代码的字符流转换成等价 Token 序列,映射到向量空间作为机器学习模型的输入。
抽象语法树(abstract syntax tree,简称 AST)是编译过程中的一种中间表示形式,树节点上存储的是代码的语法结构信息。基于 AST 的表征形式通常先使用 lex/yacc、flex/bison、ANTLR 等开源的词法和语法分析工具进行预处理,生成词法分析器和语法分析器。接着对代码进行词法分析和语法分析,将分析的结果构建成 AST,对语法树节点进行编码作为代码的特征。
基于图的表征形式能够反映代码中更丰富的语法和语义特征,有很好的检测能力。如常用的程序依赖图表示软件程序的控制依赖和数据依赖关系,控制流图(control flow graph,简称 CFG)则是由编译器在内部维护的一种程序的抽象表现,代表了程序执行过程中会遍历到的所有路径。
在基于机器学习的漏洞挖掘领域中,虽然已经取得许多突破性进展,但是现阶段发展得并不成熟,还面临着以下挑战:
(1)准确数据集的获取。机器学习方法非常依赖数据,但是目前各项研究都依赖于各自构建的数据集,尚未建立可以作为基准的开源数据集。
(2)罕见漏洞的挖掘.由于部分类型的漏洞并不是频繁被利用,因此可用于训练的数据可能较少。根据“长尾效应”,这类漏洞不被挖掘出来始终会对软件的安全使用产生影响。
(3)程序判定是否有漏洞问题的不可判定性。由 Rice 定理可知,判断程序是否存在漏洞是不可判定的。现有的解决方案是在静态分析检测后,再通过动态分析验证结果。但是这种方法显然不适用于大规模软件场景,如何才能确保这类不可判定性问题的结果具有可信度,也需要进一步研究。
(4)逻辑漏洞的挖掘方式。逻辑漏洞是由于程序逻辑不严谨或业务流程中逻辑太过复杂,导致一些逻辑分支不能正常处理而出现的。现有研究中,关于代码特征的构建极少考虑到业务流程,从而很难挖掘到逻辑漏洞。
(5)编程语言的多样性。编程语言的类型庞杂且更新迭代较快,不同语言具有不同的语法规则。如何能在出 现新兴语言的时候快速适配现有模型,也存在一定的挑战。
(6)跨项目和类不平衡场景下的检测 。基于机器学习的软件漏洞检测是可行的,但是应用在跨项目和类不平衡场景中,性能普遍较差。