漏洞
- 该课题旨在完成一个根据漏洞信息来定位对应代码仓库中修复该漏洞代码提交的算法。
- 从 CVE、SNYK 等网站收集可用数据 3000 余条,并筛选 1669 条数据作为正样本;从 5 个维度抽取代码提交与漏洞间的相似性;结合机器学习和深度学习方法与自己提出的投票排序融合方法得到最终模型。
- 最终模型在 Top 1 召回率上优于现存模型 15%,并完成论文的发表。
Shichao Wang, Yun Zhang, Lingfeng Bao, Xin Xia, Minghui Wu. “VCMatch: A Ranking-based Approach for Automatic Security Patches Localization for OSS Vulnerabilities”. 29th IEEE International Conference on Software Analysis, Evolution and Reengineering (SANER 2022, CCF B)
背景
作为一个程序员,都知道自己写的代码不可能每时每刻都有错误,只能尽可能让编写的时候不出现 Bug。而这些 Bug 中,有一些可以被其他人恶意利用的被称为漏洞。比如数据库中的 SQL 注入攻击。因此,当前有很多关于定位项目中漏洞的研究。然而,当前的漏洞研究的数据集较少,很多研究中正样本的数目不到 100 个,即我们需要知道漏洞代码长什么样。因此,我们做的就是找到漏洞补丁的位置,漏洞补丁修改的地方就是漏洞所在的地方。
当前有一些漏洞数据库,在数据库中,每一个漏洞都有一些漏洞属性,漏洞编号、漏洞描述、漏洞被赋予编号的时间等信息。很少一部分数据包含着漏洞的补丁链接。
目的:可以根据漏洞数据库中漏洞的属性和代码仓库来推测漏洞补丁对应代码提交。而这些包含漏洞补丁链接的漏洞就作为项目的正样本。
问题定义
我做的就是根据开源漏洞数据库中的漏洞数据去定位漏洞补丁的位置。不过由于代码仓库中代码提交的数目较多,为了量化效果,在实际研究中,将漏洞补丁的定位问题转换为了漏洞补丁可能性的排序问题。根据漏洞补丁的排名来评判整个模型的效果。
工作
工作方面主要包含以下几个阶段,数据集的收集,数据的处理,特征的生成,模型训练、融合、预测。对于我来说,每个阶段都具有一定的挑战性,因为每个阶段都算是自己第一次去尝试。数据集的收集方面,当初开展研究时没有相关方向的研究,也就没有数据集,因此数据集是自己去想办法收集的。数据处理上,不仅需要漏洞的数据,也需要代码仓库的数据,由于 gitpython 库官方文档并不全,还去 StackOverflow 上请教过使用方法。特征生成上,最开始只是想到了几个简单的特征,后来去 Kaggle 上看了一些 notebook,慢慢想到了一些其他的特征。特征生成时,想到漏洞描述和 commit message 中可能存在语义信息,所以又尝试使用了自然语言处理的技术,去生成语义特征。在模型选择上,尝试了很多模型,最终选择了两个机器学习和一个神经网络模型。之后,根据 Kaggle 上学习的多模型融合有利于最终效果的思想,我又思考并提出了一个新的模型融合方法,叫做投票排序,是根据投票思想想到的。
困难点
数据获取 —— 当前正样本很少,现存的都是 100 多个 commit 形式的数据。也有研究用了 1000 多数据,但是并不公开。因此最开始从 CVE 官方库下手,然后再从 reference 链接中了解到其他的数据库,如 Snyk。Snyk 数据库并没有一个统一的获取方式,就选择通过爬虫+解析 html 页面的方式来获取。漏洞对应的补丁链接,是通过 reference 中基于正则表达式来提出 commit 值,并人工进行校验。有些常用库中,如 tomcat 并没有 commit 链接,就直接去 tomcat 官方网站有个 bug 漏洞纰漏库,有个专门的 bug-id,再去寻找 commit。
期间,发现一些 bug-id 和 commit 的映射关系不正确,还进行了反馈,不过好像没有收到回复。
最终结果
研究中期有了相关研究,将我的模型与它的模型进行比对,在 Top1 的召回率上高 15%。之后,完成并发表了 CCF B 类论文。
不足
没有做成一个自动化的工具,有些官方库存在一定的对应关系,这些应该通过自动化而非模型的方式进行映射,这样准确性会更高。
补充
开展:项目是去年 3 月份开始的
-
前期
最开始做项目的时候,当前方向还没有任何的参考论文,对于漏洞的研究基本都在漏洞检测上。先进行任务的定义,将专业权威的漏洞数据库中给出的漏洞属性作为漏洞的基本信息,找到对应代码仓库中的漏洞补丁提交。其中,选取漏洞编号、漏洞描述、漏洞被赋予编号的时间、漏洞类型、漏洞相关链接作为漏洞的基本信息。 -
查找文献 - 并没有一致的方向,只能一步步开始。
-
数据集的获取 —— 爬取了 Sync 漏洞数据库的数据。漏洞数据库中只表明和漏洞相关的 URL 链接,并没有标明是否有代码补丁链接。因此,采取了遍历每个漏洞的所有 URL 链接,只保留含 commit 关键字的链接。获取链接后,使用正则表达式来提取 commit ID 和仓库名称。一些非 github 或 gitlab 的比较难提取的链接就直接手动标注了。最终提取了 3000 多条数据。但涉及到的仓库多而杂,所以为了方便,只选择了 Linux 在内 10 个仓库的 1600 余条漏洞数据。这就是正样本了。由于后续出来了一个同方向的论文,该论文采用正负样本 1:5000 的比例,因此我们也采用相同的比例随机生成了负样本数据。
-
特征的提取 —— 这就是比较难的地方,从两类中找相关特性。
漏洞方面,漏洞描述、漏洞时间、漏洞编号。代码提交方面,commit message、commit 时间、diff 内容、commit 文件等。
漏洞描述方面,浏览漏洞描述时发现,有些描述中存在函数名、文件名、版本号信息。对于函数名、文件名使用了正则表达式提取出来。对于版本号,当时研究了很长时间,判断版本号和 commit 有没有可以使用的价值,但是由于版本号并非有顺序的,软件也可能多版本同时存在等原因,版本号并没有作用。
漏洞时间,虽然是被赋予漏洞编号的时间,但与漏洞补丁提交时间应该不会太远,也可以作为一个考量。
OpenSSL 仓库最初会将漏洞编号记录在 commit message 中。
漏洞描述和 commit message、commit diff 之间对于单词的特征。由于自然语言处理技术比较成熟,打算对 commit message 和漏洞描述进行处理。为此,训练了基于 Bert 模型的编码模块,将漏洞描述和 commit message 分别编码成 32 维的编码向量。
之后从漏洞基本信息和代码提交中筛选出有用的数据,如使用正则表达式提取漏洞描述中的函数名、方法名、文件路径信息,用于和提交修改的函数名、方法名、文件路径进行比对来生成特征。
最后从代码修改相关特征、标志漏洞身份的特征、漏洞定位的特征和 Token 的特征四个维度来生成特征。到此,特征收集结束了。
-
模型的选择和训练 —— 选择了 XGBoost、LightGBM、CNN 三个模型。原因在于,当前的数据量是非常大的,1600*5000,接近 10 的 7 次方的数据。普通的一些方法训练时间会比较长,XGBoost、等有 GPU 的支持,训练速度快。同时,当前数据集是正负样本比例悬殊的,XGBoost、LightGBM、CNN 也可以有效应对不平衡的数据。训练时,label 为 commit 与漏洞是否匹配。
-
模型融合 - 训练后,依据匹配的概率对所有样本进行排序,正样本的排名越高,则效果越好。之后,为了结合三个模型的效果,提出了一个融合方法——投票融合。这种融合方法借鉴了多数投票——数目最多的类别获胜的思想。具体方法为,对于一漏洞数据,将所有的代码提交根据模型给出的匹配概率进行降序排序,获得代码提交在三个模型下对应的排名,选择三个排名中最接近的两个排名的和作为后续的排序依据。根据两个排名的和,对所有的代码提交进行升序排序,和越小,则排名越靠前,说明该代码提交越匹配。最终的排名顺序就是代码提交和漏洞相匹配的顺序。
项目创新点
- 非常全面的特征选择,四个维度的人工提取和 NLP 提取。
- 多模型的支持和有效的融合方法。
困难、解决
- 漏洞数据库中并没有指明漏洞的补丁提交,我就采用先通过 commit 关键字筛选,然后正则表达式提取的方式来获得 commit ID。
- 特征的选择上,去思考漏洞和代码提交各种相关性。
- 模型融合上,思考一种有效的模型融合方法。
- python 的一些处理函数。
感悟、重做项目该怎么做
- 有了一个比较清晰的研究路线,搜索和阅读文献、了解当前的方法、自己寻求其他或更好的方法、去尝试获取特征、建立模型、训练和验证。
- 对于类似研究有了一定的特征寻找的思路。
- 收获方面,对科研的过程有了一定的了解,对于 python 进行数据分析有了初步的经验和教训。
为什么算法转开发?
算法和开发两者都比较喜欢,在进行算法的过程中包含着工程方面的代码,开发的过程中也包含着设计方面的代码。
选择最终专攻开发的原因在于,综合这些年的形势,算法似乎比开发更激烈一些,所以选择开发作为自己的投递意向。