1.基本信息
A large-scale empirical study on vulnerability distribution within projects and the lessons learned
2020ICSE (A)
中科院信工所
研究项目内部的漏洞分布
2.论文概述
在本文中,作者通过使用自动爬虫程序并花费数月的人工工作,收集了一个大型漏洞数据集,其中包括与五个代表性开源项目相关的所有已知漏洞。然后,我们在四个维度上分析了每个项目中的漏洞分布,包括文件、函数、漏洞类型和负责的开发人员。基于结果分析,我们提出了12个关于漏洞分布的实用见解。最后,我们将这些见解应用于几个漏洞发现解决方案(包括静态分析和动态模糊),并帮助他们在目标项目中找到10个zero-day漏洞,表明我们的见解是有用的。
motivation
例如,如图1所示,我们在研究microsoft Windows Journal中现有的一些漏洞(标记为MSxx)时,发现了5个新的漏洞(标记为NVx)。我们发现,所有这些新的漏洞都与调用图中现有的漏洞(标记为MSxx)非常接近。其中一些漏洞甚至与现有漏洞驻留在相同的功能中,或者具有类似的代码片段。因此,这意味着,项目中的漏洞可能在空间上具有局域性(例如,功能位置)和模式(例如,代码结构),并且可以利用这种局域性来定位项目中已知漏洞周围的新漏洞。
受以上例子启发,
以往的研究要么专注于分析bug而不是漏洞,要么只研究项目之间的总体漏洞分布,而不是每个项目内部的漏洞分布。
solution
result
- 漏洞分布在空间(文件,而不是函数)、类型和负责任的开发人员维度上遵循Pareto法则。
- 大多数(超过60%)脆弱函数在调用图中至少有一个脆弱邻居函数位于2跳范围内。换句话说,大多数脆弱的函数不是单独存在的。
- 漏洞补丁不完整或错误是高频漏洞函数存在的最常见原因,而补丁工作流程ROPO (Report One issue and Patch One position based on reference PoC)是另一个重要原因
contribution
- **大规模的脆弱性数据集。**为了研究项目中的漏洞分布,我们花了5个人数月的时间,构建了一个关于一组开源项目的最大的漏洞数据集。我们向公众分享这个数据集,以促进其他研究。
- **彻底的实证研究。**与以往的研究不同,我们的研究揭示了漏洞在文件、函数、类型和负责任开发人员等多个维度上的分布特征。此外,我们还研究了漏洞的相关性,以揭示它们存在的原因。
- **实用的见解。**我们根据分析结果提出了几个实际的见解。特别地,我们发现大多数漏洞不是单独存在的。我们还发现,漏洞补丁不完善、打补丁的工作流程ROPO是普遍现象,导致某些功能漏洞反复出现。我们还提出了一个解决方案来缓解ROPO问题。
- **发现新的漏洞。**在此基础上,我们提出了利用它们指导漏洞发现的解决方案,并将其应用于静态分析和动态模糊等多种漏洞发现方案中,帮助他们成功发现了目标项目中的10个零日漏洞。
3.论文详解
数据来源:FFmpeg , ImageMagick , OpenSSL , PHP-SRC 、Linux kernel
数据收集:
- 爬取5个project的所有commit——该爬虫利用GitHub开发人员api[20],下载更改的代码、问题、拉请求和负责任的开发人员每次commit。
- commit初步过滤——设置了一个爬行规则,它过滤掉不做任何语义感知的更改commit。更具体地说,对配置文件、代码注释和间距的更改基本上与漏洞无关,因此没有体现在我们的数据集中。在应用此规则之后,我们总共得到1,085,623个提交。
- 过滤掉非安全相关的commit——使用NVD和CVE来维护大量漏洞,并识别与漏洞相关的真实commit。在此任务中,并非所有CVE条目都有用,因此我们通过指定供应商和产品来限制条目。例如,可以通过搜索产品“FFmpeg”和供应商“FFmpeg”的条目来提取FFmpeg漏洞。这样可以得到3806个CVE表项。
如图2所示,有几个任务需要专业人员的参与。在本次研究中,我们招募了3名安全专家,他们都具有5年以上的漏洞研究经验。他们总共在封闭/开源软件中发现了几十个未知的漏洞,超过30个漏洞被分配了CVE标识符。他们中的一个甚至被列入了2016年MSRC Top 100 Security researcher。
为了确保手工分析结果的正确性,我们采用了定制的同行评审过程。首先,每个任务由两位专家进行分析。如果他们能达成协议,任务就完成了。否则,第三位专家将参与解决分歧。如果她/他同意前面两位专家中的任何一个,那么我们就遵循简单的多数决定原则来得出最终的分析结果。在最坏的情况下,三个专家报告三个不同的结果,然后我们遵循一个保守的原则来解决问题。具体来说,3名专家将一起讨论,得出一个保守的结果。以CWE标注任务为例,给定一个CVE漏洞,如果三位专家报告了三种不同的CWE标签,那么最终将产生覆盖他们的广义CWE标签或UNKNOWN标签。
数据处理
将收集到的数据分为三个步骤进行处理:关联分析、漏洞定位和VCG(漏洞调用图)构建:
关联分析
首先尝试将CVE条目与负责任提交关联起来。如果发现一个commit与CVE条目有关,我们将其视为与漏洞相关的提交。注意,NVD/CVE数据库中的CVE条目通常会引用外部链接,包括脆弱项目GitHub存储库中负责提交的链接。根据这个指导,我们实现了一个工具来解析这些引用并提取相关的GitHub提交链接,并自动构建commit与大多数CVE条目的对应连接。对于其余的CVE条目,我们花了三个人数月的时间,通过查看来自多个来源的详细漏洞描述,包括谷歌OSS-Fuzz、RedHat BugZilla和各种安全分析博客的报告,手工调查相应的提交。
数据质量
为了使漏洞数据集无错误、精确,我们在以下方面做了努力:
- 1)去冗余。如果一个漏洞与多个commit相关联,我们将检查这些commit是否具有相同的内容,但出现在不同的分支中。如果是这样,我们只保留最新的commit。
- 2)类型校正。数据集中的每个漏洞都被分类为CWE类别。然而,现有的分类结果往往是错误的或不精确的(参见第3.2节)。因此,我们按照CWE-1000的分类标准,手动纠正每个漏洞的类别。以Linux-kernel项目中的CVE-2016-2549漏洞为例,该漏洞被标记为CWE-20 (Improper Input Validation),并与NVD中的commit 2ba1fe7关联。但是在查看了CVE的描述、RedHat BugZilla的注释、负责提交的消息以及对应的补丁后,我们确定这实际上是一个死锁问题,并达成了将其标记为CWE-833 (deadlock)的共识。
如表1所示,在选定项目的CVE/NVD数据库中记录了总共3,806个漏洞。在自动和手动关联分析之后,2,476个CVE条目成功地与它们负责的提交关联起来。请注意,在手动分析期间,我们还发现了大量为修复CVE/NVD中未列出的安全bug而创建的提交。我们总共找到了1462个这样的提交,并将它们与未列出的漏洞关联起来,并将它们放入我们的数据集中。
最后得到了一个交叉验证的漏洞数据库,包括3938个漏洞条目。每个条目都与负责任的提交和相关的元数据信息相关联,包括位置、提交日期和负责任的漏洞开发人员。
漏洞定位
在确定了与漏洞相关的提交之后,我们寻求以细粒度的粒度对漏洞代码进行定位。通过统一的diff格式[27],提交的变化主要发生在两个编程结构上:函数和数据结构。对于函数的更改,我们需要恢复以前易受攻击的代码。这是通过内置的git reset命令实现的,它将当前的HEAD重置为父提交。另外,在分析过程中,我们会处理以下异常:
- 1)如果一个提交没有删除任何行,只有添加,我们首先识别出受添加代码块影响的关键漏洞变量,然后确定变量定义或引用的位置作为漏洞点。
- 2)如果一个提交添加了一个新函数,这个函数不会被视为脆弱函数。
论文结果
- finding1:小部分(30%)的漏洞文件占了大部分(66%)的漏洞。然而,这些文件只贡献了40%的代码大小和复杂性。因此,更高的代码复杂度不一定会导致更多的漏洞,缺陷预测中的复杂性度量对漏洞不再有效。
- finding2:考虑到脆弱函数,前30%的脆弱函数占50%的漏洞,不如脆弱文件明显。
- finding3:在我们调查的漏洞的类型标签中有9.6%的错误,10.9%的不精确和7.2%的遗漏问题,这些问题已经报告给NVD。
- finding4:一个C/ C++调查的项目,70%的漏洞属于20%的类型;前3种类型的漏洞平均占比接近50%。此外,一个项目中最可能存在的漏洞类型很大程度上取决于项目的核心功能和编程语言。
- finding5:项目中的顶级类型不会受到时间的显著影响,并且在未来仍然是大多数类型。这意味着安全分析师可以根据历史数据优先审计流行类型的漏洞。
- finding6:几乎所有的漏洞都是由项目中不到10%的开发人员引入的。这10%的开发人员贡献了从60%到几乎100%的代码,这在不同的项目中取决于他们开发人员的数量。
- finding7:虽然我们不能推测引入< 10个漏洞的开发人员的类型偏好,但它确实存在于剩余的更高效的开发人员中。因此,根据某个开发人员可能引入的漏洞类型,这为按优先级排序的代码审查提供了指导。
- finding8:漏洞出现的地点非常集中。每个调研项目80%以上的脆弱功能聚集在一个2-cluster中。
- fingding9:一个项目中60%以上的脆弱函数与另一个具有不同漏洞的脆弱函数至少有一个基本调用关系。
- fingding10:有相当数量(25.5%)的函数平均出现3次重复现象。但是,两次安全修复之间的间隔可达514.95天。
- fingding11:不完全修复和引入修复是漏洞在同一功能中反复出现的主要原因。这意味着一些开发人员可能无法修复所有现有的漏洞,或者从补丁代码中产生新的漏洞。
- fingding12:漏洞修复中常见的ROPO (Report One and Patch One)问题。揭示了漏洞修复过程中的一个薄弱环节,为今后的研究方向提供了启发,如增强补丁修复过程,高效检测类似或克隆的漏洞。