浅谈基于深度学习的漏洞检测

前言

这几天爆火的ChatGPT到处刷屏,又是写诗又是刷leetcode,加上之前打败柯洁和李世乭的AlphaGo,以及在TI上打败人类职业DOTA选手的OpenAI Five,让我们不禁反思,会主动不通过图灵测试的人工智能什么时候会诞生?

2018年华中科大的邹德清教授课题组第一次提出了使用深度学习进行漏洞检测,算是敲开了基于深度学习的漏洞检测领域的大门,自此各种新的方法被全世界的研究者们提了出来。

那么我们最关心的问题也随之被提了出来:深度学习是怎么识别并检测漏洞的?

安全领域中深度学习的现状

软件安全关乎到未来软件行业市场发展,依据目前的挑战和机遇,应制定多层次、多维度、多方位的信息安全策略,提高信息安全保障水平。因此,在严峻的安全形势下,研究安全自主可控的软件安全漏洞检测技术是大势所趋。

深度学习在图像处理、视频实体识别、自然语言处理等领域中已经取得了长足的发展和突出的成果,这自然驱使着安全研究员们将神经网络引入到安全领域中。

区别于相对成熟的CV、NLP等领域,面向源代码的漏洞检测目前没有与之非常契合的神经网络进行特征抽取或表征学习。那么退而求其次,能不能寻找一种合适的源代码表征方式使得现有的、成熟的神经网络来找找漏洞呢?

答案是可以的。

——注:本篇只讨论面向源代码的静态漏洞检测。

基于深度学习的漏洞检测方法论

其他领域中的特征表征方式

利用深度学习的关键步骤之一是需要神经网络能够学习到所输入源代码的特征。在图像识别中,作为输入数据的图像可以以灰度的形式被神经网络所接受。

以手写字识别为例。

浅谈基于深度学习的漏洞检测_第1张图片

该图像为28*28像素的灰度图,每个像素点上的灰度即为我们要输入到神经网络中对应神经元的值,称为activation。这些灰度值通过flatten和concat成为包含784个属性值的向量后,在神经网络中逐层计算传递,最终会激活输出层对应的神经元从而达到识别手写字的目的。

或者以NLP中情感识别为例。

考虑句子

“I am so annoyed”,

将其进行分词变为

“I”, “am”, “so”, “annoyed”,

然后将文本或单词映射到实数向量(embedding)。如词袋(BOW),通过预定义的字典统计句子中的词汇,0表示该词汇不存在于字典中,1表示存在于字典中。如预定义的字典中包括

”happy”, “sad”, “annoyed”, “pissed”, “very”, “little”, “so”,

则这个句子可以转换为向量(0, 0, 1, 1)被神经网络所接受,最终会将这个句子的情感分类为“生气”。

漏洞的表征方式

通过图像和自然语言处理中的例子,我们可以得出我们的目标,那就是把源代码向量化

将源代码向量化可以大致通过以下三种途径(还有诸如二进制、字节码编码等)。

  • 借鉴NLP进行embedding
  • 通过抽取AST(或PDG/CFG)转为图
  • 计算目标代码片段的代码度量

(1)借鉴NLP对源代码进行词嵌入

源代码从表达的层面来说具有与英文等语言具有类似的语义特征,包括上下文、逻辑的连续性以及类似于形容词、名词、动词的词性。例如,对于源代码片段:

int i;

int sum = 0;

int product = 1;

int w = 7;

for(i = 1; i < N; ++i) {

    sum = sum + i + w;

    product = product * i;

}

write(sum);

write(product);

我们想要寻找write(sum)这一句的语义相关的代码语句从而构成一段具有上下文语义连贯性的代码片段,可以得到

int i;

int sum = 0;

int w = 7;

for(i = 1; i < N; ++i) {

    sum = sum + i + w;

}

write(sum);

那么,我们称这个具有上下文语义连贯性的代码片段为语义相关的代码片段。将这个代码片段进行词嵌入处理,就可以使用NLP中成熟的神经网络进行特征的学习,从而实现漏洞检测的目标。

(2)通过抽取AST转为图

抽象语法树(Abstract Syntax Tree)、程序依赖图(Program Dependency Graph)、控制流图(Control-Flow Graph)在一定程度上具有与语义相关的代码片段相似的语义特性。将这种包含节点、有向线段的图就可以构成这个代码片段的特征。将其中的图节点和边映射到向量空间,会变成具有相同维度的具有矢量区别的样本。在经过大量样本的训练之后,就可以有效的识别包含漏洞的代码和不包含漏洞代码之间的特征的区别,从而实现漏洞检测的目的。

(3)通过计算目标代码片段的代码度量来获取特征

代码度量,又称软件度量,按照维基百科的定义,

“软件度量(software metric)是一个对于软件性质及其规格的量测。软件度量的目的是获得客观、可以复制及量化的量测结果,依软件度量性质及特性的不同,可以分别应用在软件开发的时程及预算规划、成本估算、质量保证测试、软件调试、软件性能优化或项目人员配置的优化等领域。”

曾经和同事讨论过基于代码度量的漏洞检测和其他基于语义的漏洞检测有什么区别,得出了一个比较形象的结论。

基于图/语义的漏洞检测,就好比使用卫星云图、历史天气数据来预判明天会不会下雨;

而基于代码度量的漏洞检测,就好比去问一个患有风湿的人腿疼不疼,腿疼就是会下雨。

听起来好像不那么靠谱,但是可行。

一些常见的代码度量有:

Program Length,Difficulty,Effort,Volumn,Maintainability,Committed Bugs,SLOC,圈复杂度,耦合度,注解密度,客户需求行数 and so on。(来源:https://en.wikipedia.org/wiki/Software_metric)

每个代码片段的样本都可以通过计算得出代码度量,即,每个样本的特征都是一个包含复数个属性的一维向量,这对于神经网络来说是可以接受的。

代码度量数据集的样例如下。


@relation R_data_frame

@attribute Empty_Lines numeric

@attribute Lines_Of_Comments numeric

@attribute Lines_Of_Program numeric

@attribute Physic_Lines numeric

@attribute n1_Number_Of_Distinct_Operators numeric

@attribute n2_Number_Of_Distinct_Operands numeric

@attribute n_Program_Vocabulary numeric

@attribute N1_Total_Number_Of_Operators numeric

@attribute N2_Total_Number_Of_Operands numeric

@attribute N_Program_Length numeric

@attribute B_Number_of_Delivered_Bugs_1 numeric

@attribute B_Number_of_Delivered_Bugs_2 numeric

@attribute D_Difficulty numeric

@attribute E_Effort numeric

@attribute T_Time_Required_To_Program numeric

@attribute V_Volume numeric

@attribute _N_Calculated_Program_Length numeric

@attribute McCab_Number numeric

@attribute IsVulnerable {no,yes}

@data

0,0,11,11,13,14,27,51,32,83,0.000333,0.131552,12.000000,4735.867952,263.103775,394.655663,101.408685,13,yes

其中,@attribute标注表示该属性的名称和类型,@data标注为数据。每一行都代表一个代码片段样本的属性集,可以被读取为向量;最后一列的yes/no则是该样本的标签,用于有监督的学习,从而被神经网络学习以达到漏洞检测的目的。

漏洞的表征学习方式

不同的漏洞源代码表征方式需要用到不同的神经网络进行训练。

通常的,长短期记忆网络(Long-short term memory, LSTM)/双向长短期记忆网络(BiLSTM)缓解了传统循环神经网络(RNN)中梯度随时间呈指数下降的问题(梯度消失),具有可以长时间记住有效数据的特性,常常被用于NLP领域中。同样的,鉴于面向语义的漏洞检测具有于人类语言相似的语义特性,也可以使用LSTM/BiLSTM及其变种进行训练。

图神经网络(Graph Neural Network, GNN)设计的关键元素是成对的消息传递,即图节点之间通过消息传递来更新节点的值。而这一特性的存在,使得基于图(即AST/PDG/CFG等)的漏洞检测有了适用的神经网络。图神经网络主要包括图卷积网络、图注意力网络、图自动编码网络、GraphSAGE、Gated GNN、子图嵌入等等。其本质也是将图节点映射到向量空间中。

卷积神经网络(Convolution Neural Network, CNN)是目前适用范围最广泛的神经网络之一,它对于局部的特征的敏感性使得其在CV领域取得突出的成果。基于代码度量的特征,由于属性集中每个属性都具有线性或非线性相关,所以通过局部的卷积可以抽取“视野”中的特征值,从而能够用来表征漏洞代码样本。

总结与展望

基于深度学习的漏洞检测才刚刚起步,本文也浅尝辄止,只针对漏洞源代码的表征方式进行了讨论。除此之外,数据的收集、数据预处理、漏洞特征抽取、表征学习以及分类,甚至构建适用于源代码的神经网络也有长远的探索空间。

当然,除了面向源代码的静态检测,也有许多动态方法如:

  1. 利用生成对抗网络(GAN)进行FUZZ
  2. 基于GAN的污点分析
  3. 面向智能合约的漏洞检测
  4. And so on…

那么未来在哪儿呢?是端到端的漏洞检测?还是能够嵌入到开发环境中,在编写代码的同时对开发人员提出告警?

诸君,道阻且长。

参考文献

[1] Li Z , Zou D , Xu S , et al. VulDeePecker: A Deep Learning-Based System for Vulnerability Detection[J]. 2018.

[2] An W, Chen L, Wang J, et al. AVDHRAM: Automated Vulnerability Detection based on Hierarchical Representation and Attention Mechanism[C]//2020 IEEE Intl Conf on Parallel & Distributed Processing with Applications, Big Data & Cloud Computing, Sustainable Computing & Communications, Social Computing & Networking (ISPA/BDCloud/SocialCom/SustainCom). IEEE, 2020: 337-344.

[3] Wang H, Ye G, Tang Z, et al. Combining graph-based learning with automated data collection for code vulnerability detection[J]. IEEE Transactions on Information Forensics and Security, 2020, 16: 1943-1958.

[4] Cao S, Sun X, Bo L, et al. BGNN4VD: Constructing Bidirectional Graph Neural-Network for Vulnerability Detection[J]. Information and Software Technology, 2021, 136: 106576.

[5] Guo, J., Wang, Z., Li, H. et al. Detecting vulnerability in source code using CNN and LSTM network. Soft Comput (2021). https://doi.org/10.1007/s00500-021-05994-w

[6] Junjun Guo, Zhengyuan Wang, Li Zhang, Yang Xue, Kai Long, Xin Jing, Jing Cheng, Guiping Li, “SFN: A Novel Scalable Feature Network for Vulnerability Representation of Open-Source Codes”, Computational Intelligence and Neuroscience, vol. 2022, Article ID 2998448, 12 pages, 2022. https://doi.org/10.1155/2022/2998448


文/Thoughtworks 王正源
原文链接:https://insights.thoughtworks.cn/vulnerability-detection-based-on-deep-learning/

你可能感兴趣的:(新兴技术,技术雷达,深度学习,人工智能)