这篇文章质量很高,也是我非常欣赏的一个作者,他们之前发表了一个类似的方向的文章叫Vuddy,这次这个是上次工具的升级,主要是处理了代码演化上的检测问题。
摘要
第三方开源软件(OSS)组件继承的漏洞可能威胁整个软件系统的安全性。然而,由于开源软件在演化过程中会经历各类修改(具体表现为内部修改如OSS版本更新,以及外部修改如OSS复用过程中的代码变更),导致漏洞代码会以不同语法形式扩散传播,这使得追踪传播性漏洞代码极具挑战性。
本文提出MOVERY方法,该技术能够从修改后的OSS组件中精确发现漏洞代码克隆(VCC)。通过定位最原始的漏洞函数,并从安全补丁中提取核心漏洞代码行和补丁代码行,MOVERY构建的漏洞特征和补丁特征能有效应对OSS的各类修改。为提高可扩展性,MOVERY通过仅分析从其他OSS项目引入的代码,显著缩小目标软件的检测范围。最终,当函数同时满足漏洞特征匹配且与补丁特征存在差异性时,MOVERY将判定该函数为VCC。
我们在十个跨领域热门软件上的实验结果表明:91%的已发现VCC与其披露的漏洞函数存在语法差异。MOVERY的检测效能显著优于现有技术——其发现的415个VCC具有96%精确率和96%召回率,而两种最新VCC检测技术(均未充分考虑OSS内外修改)仅发现163和72个VCC,且最高精确率和召回率分别仅为77%和38%。数据表明,MOVERY的检测数量至少多出现有技术2.5倍,同时保持更高的检测精度。
1 引言
随着开源软件(OSS)数量的快速增长,开发者能够便捷地复用优质开源项目的功能模块[11, 12, 37]。然而,第三方OSS组件传播的漏洞可能威胁整个系统的安全性[14, 17, 48, 49]。*Heejo Lee为通讯作者。
从理论上讲,保持OSS组件处于最新版本是防范此类威胁的有效手段。然而,仓促的组件更新可能对系统产生负面影响(例如向后兼容性问题[40, 44]),这在开发者复用经过代码或结构修改的OSS项目时尤为显著[48]。因此,开发者通常选择回移植上游安全补丁[43]而非直接更新整个组件,以避免漏洞带来的潜在威胁。为实现这一目标,他们首先需要定位待修复组件中的漏洞代码[13, 43],例如借助漏洞代码克隆(VCC)检测技术(如[17, 49])以及识别目标程序中复用组件的软件成分分析(SCA)技术(如[9, 48])。
遗憾的是,从修改后的OSS组件中精确发现漏洞正变得日益困难,其核心挑战在于漏洞代码的语法多样性,这主要由以下两类OSS修改导致:
• OSS内部修改:OSS源码在版本更新过程中频繁变更。在此背景下,漏洞代码可能以不同形态存在于各OSS版本中,并会以多种语法形式传播至其他软件。
• OSS外部修改:由于OSS生态系统中源码常在复用过程中被修改的特性[48],漏洞代码可能在OSS复用阶段被改写。
这两类OSS修改共同导致传播漏洞代码的语法与公开披露的漏洞代码(如通过公共漏洞数据库[31])存在差异,从而严重影响了漏洞代码发现的准确性。据我们所知,现有技术均无法有效检测修改后OSS组件中存在的多种语法形态的漏洞代码。
现有技术局限性分析
当前漏洞代码克隆(VCC)检测技术(如[14,17,49])未考虑OSS内部修改,且对外部修改的检测方式存在局限,导致大量漏报现象。具体而言,这些技术仅能发现包含安全补丁中已删除代码行的VCC,而此类代码行在修改后的OSS组件中常被移除。相较之下,现有软件成分分析(SCA)工具(如[9,48])仅基于复用OSS版本判定漏洞存在性,当漏洞代码通过回移植修补或未被复用时易产生误报。此外,传统代码克隆检测技术(如[35,42])虽可用于VCC发现,但由于无法区分具有高相似度的漏洞代码与已修补代码[17,49],普遍存在误报风险。
MOVERY方法论
为突破上述技术瓶颈,本文提出MOVERY(MOdified Vulnerable code clone discovERY)——一种从修改后OSS组件中精确检测漏洞代码克隆的创新方法。
相较于现有VCC检测技术,MOVERY的核心创新在于生成可扩展的复合特征签名,其覆盖范围从最原始漏洞函数延伸至已披露漏洞函数。MOVERY工作流程包含两大阶段:(1) P1特征生成阶段 与 (2) P2漏洞检测阶段。
P1特征生成阶段 通过重构安全补丁中的漏洞函数与修补函数,生成漏洞特征与补丁特征。本阶段采用两项关键技术:
1. 函数校勘技术:除已披露漏洞函数外,MOVERY额外溯源最原始漏洞函数并进行跨版本比对。由于最原始漏洞函数与已披露漏洞函数间的差异可反映OSS更新过程中的漏洞代码演变,该方法可有效应对内部修改。
2. 核心代码行提取技术:仅保留核心漏洞代码行与补丁代码行构建特征签名。该策略使检测过程免受非核心代码变更干扰(详见第3.1节)。
P2漏洞检测阶段 基于生成特征在目标软件中发现VCC。本阶段实现两大优化:
• 可扩展性优化:集成SCA技术[48]的代码溯源能力,仅检测源自其他OSS的代码片段,显著缩小搜索空间。
• 精确性优化:采用选择性抽象技术应对OSS外部修改。当目标函数同时满足漏洞特征匹配且与补丁特征存在差异性时,MOVERY方将其判定为VCC(详见第3.2节)。
实验评估
实验结果表明,MOVERY在漏洞代码克隆(VCC)检测性能上显著优于ReDeBug [14]和VUDDY [17]等现有技术。为开展评估,本研究从美国国家漏洞数据库(NVD)[31]收集了4,219个通用漏洞披露(CVE)补丁,涵盖所有通过Git平台[19,47]发布的C/C++语言相关CVE补丁。
在跨领域选取的十款热门软件测试中,91%的已检测VCC与公开漏洞函数存在语法差异。MOVERY的VCC检出量至少超出现有技术2.5倍且准确率显著提升——由于ReDeBug与VUDDY未考虑OSS修改且无法有效处理VCC语法多样性,导致严重漏报。具体而言,MOVERY检测到415个VCC(精确率96%,召回率96%),而ReDeBug与VUDDY分别仅发现163和72个VCC(最高精确率77%,召回率38%)(详见第5.1节)。
进一步实验表明,在检测修改组件中的VCC时,MOVERY的准确性显著优于MVP [49](一种循环漏洞检测技术)与CENTRIS [48](一种SCA技术):未考虑OSS内部修改的MVP在十款目标软件中出现184例漏报(召回率54%),而基于CENTRIS的VCC检测方法因忽略回移植补丁导致272例误报(精确率51%)(详见第5.2与5.3节)。
就检测效率而言,MOVERY对十款不同规模目标软件(代码量范围:212,672至14,489,534行C/C++代码)的平均检测耗时控制在200秒以内。该耗时低于ReDeBug(298秒)与VUDDY(798秒),证明MOVERY具备实际应用所需的效率与可扩展性(详见第5.4节)。
技术贡献
本研究的主要贡献可概括为以下三方面:
• 首创精确检测方法:提出首个针对修改后OSS组件的VCC精确检测方法MOVERY。核心技术在于生成能够有效应对OSS内外修改的特征签名。
• 揭示内部修改影响:首次证实OSS内部修改(现有技术鲜少考虑)是导致VCC语法多样性的主导因素。
• 实现高效精准检测:尽管修改组件中91%的VCC语法与公开漏洞函数存在差异,MOVERY仍成功在跨领域十款目标软件中检测到415个VCC,且达到96%精确率与96%召回率。
以下是您提供的学术文本的专业翻译:
2 研究动机
本章明确MOVERY方法的目标问题,并通过典型案例阐明研究动机。
2.1 问题定义
MOVERY致力于发现修改后OSS组件中的传播性漏洞。假设某漏洞在OSS中引入后被修补,定义以下三个函数实体:
- fd:通过公共漏洞数据库披露的漏洞函数
- fp:已修补函数
- fo:旧版OSS中包含的漏洞函数(其代码语法与fd存在差异,见图1)
无论漏洞传播形式如何,所有衍生漏洞函数均需被有效检测与修补。
技术挑战:
核心挑战在于应对漏洞代码的语法多样性,其根源可归结为两类OSS修改行为:
- OSS内部修改:OSS版本更新导致漏洞函数语法变更(如C3、C4类漏洞)
- OSS外部修改:开发者在复用过程中对OSS组件实施定制化代码修改(如C2、C4类漏洞)
现有VCC检测技术(如[14,17,49])仅能覆盖C1及部分C2类漏洞,存在显著漏报问题。此类技术通常将安全补丁中删除的代码行定义为漏洞行,并要求目标函数完全包含这些漏洞行方可判定为VCC。然而,当OSS修改导致fd中定义的漏洞行在fo中不存在时,现有技术即失效。
2.2 典型案例分析
本节通过两个已修复的VCC实例,展示因OSS内外修改导致的语法多样性问题。所有案例均经开发团队确认并修补。
案例1:LibZip v1.3.0之前版本存在内存分配失败漏洞(CVE-2017-14107)。代码清单1展示了该漏洞的补丁片段。研究发现,PHP项目在2017年复用漏洞版LibZip时曾回移植此安全补丁(修补后函数见代码清单2)。
以下是对两个代码清单的技术翻译:
代码清单1:LibZip项目中CVE-2017-14107补丁片段
(代码差异对比格式,展示漏洞修复过程)
1 index 3bd593b1..9d3a4cbb 100644
2 ––– a/lib/zip_open.c
3 +++ b/lib/zip_open.c
4 @@ ... @@ _zip_read_eocd64 (...) {
5 zip_cdir_t *cd;
6 zip_uint64_t offset;
7 zip_uint8_t eocd[EOCD64LEN];
8 ...
9 zip_error_set(error, ZIP_ER_SEEK, EFBIG);
10 return NULL;
11 }
12 - if ((flags & ZIP_CHECKCONS) && offset+size != eocd_offset) {
13 + if (offset+size > buf_offset + eocd_offset) {
14 + /* 目录记录跨越EOCD记录边界 */
15 + zip_error_set(error, ZIP_ER_INCONS, 0);
16 + return NULL;
17 + }
18 + if ((flags & ZIP_CHECKCONS) && offset+size != buf_offset + eocd_offset) {
19 zip_error_set(error, ZIP_ER_INCONS, 0);
代码清单2:PHP项目中修补后的_zip_read_eocd64函数
(【差异点】标注与LibZip原始补丁的语法差异,该函数与LibZip披露漏洞函数仅13%语法相似)
1 static struct zip_cdir * _zip_read_eocd64 (...) {
2 struct zip_cdir_t *cd;
3 zip_uint64_t offset;
4 const zip_uint8_t *cdp; // 【差异点】新增指针变量
5 ...
6 _zip_error_set(error, ZIP_ER_SEEK, EFBIG); // 【差异点】函数名前缀差异
7 return NULL;
8 }
9 if (offset+size > buf_offset + eocd_offset) {
10 /* 目录记录跨越EOCD记录边界 */
11 _zip_error_set(error, ZIP_ER_INCONS, 0); // 【差异点】错误处理函数调用方式
12 return NULL;
13 }
14 if ((flags & ZIP_CHECKCONS) && offset+size != buf_offset + eocd_offset) {
15 _zip_error_set(error, ZIP_ER_INCONS, 0);
由于PHP复用了2013年发布的旧版LibZip并进行了代码修改,其漏洞函数的语法与CVE-2017-14107公开补丁中指定的形式存在差异。当将函数视为代码行集合时,通过计算PHP中漏洞函数与LibZip披露漏洞函数间的Jaccard相似度[46],测得两者相似度仅为13%。此外,由于语法差异问题,PHP开发团队无法直接应用该安全补丁。因此,他们选择对披露的安全补丁进行适应性修改并集成至代码库,而非将整个LibZip组件升级至安全版本(即v1.3.0及以上版本)。
此案例表明,针对与公开漏洞函数存在显著代码差异(本案例中达87%)的VCC进行检测具有必要性。MOVERY通过溯源最原始漏洞函数的语法特征,并在特征签名生成时仅采用核心代码行,能够有效检测此类高差异度VCC(具体技术细节详见第3节)。
技术局限性验证
然而,可扩展VCC检测技术VUDDY [17]由于仅考虑漏洞函数的有限变更,无法检测此类VCC。ReDeBug [14](一种通过分析补丁中相邻三行代码变更的VCC检测技术)同样失效——因其依赖补丁删除行附近的三行代码(即代码清单1第9-11行),而PHP中的漏洞函数对应代码行(代码清单2第6-8行)存在语法差异。
案例2:
以Lua语言解释器v5.1至v5.2.2版本存在的缓冲区溢出漏洞(CVE-2014-5461)为例,该漏洞可导致攻击者实施拒绝服务攻击。代码清单3展示了Lua v5.2.3的补丁片段。研究发现,Redis项目在复用旧版Lua时进行了代码修改,并于2020年9月通过回移植方式应用了该补丁(见代码清单4)。
具体而言,Redis应用的补丁与Lua官方仓库存在差异。原因在于Redis复用的是Lua v5.1.5旧版本,其漏洞函数语法与公开披露版本不同。由于Lua旧版本中缺失代码清单3第12行等关键上下文,Redis开发团队无法直接应用原始补丁,最终选择参照Lua官方漏洞追踪系统建议的方案进行适配性修补。
以下是对该段技术文本的翻译及解析:
技术启示
本案例凸显了检测基于旧版本语法传播的VCC的必要性——此类VCC可能不包含安全补丁中已删除的代码行。MOVERY通过校勘最原始漏洞版本与已披露版本间的漏洞函数(详见第3节),成功实现此类检测。然而,当现有VCC检测技术[14,17,49]所定义的漏洞行(如本案例中代码清单3第12行)未包含在目标VCC中时,此类技术即告失效。反观现有软件成分分析(SCA)技术[9,48],尽管Redis开发团队已通过回移植修复漏洞,仍会因Redis复用Lua v5.1.5(含漏洞版本)而误判系统存在安全隐患。
3 MOVERY方法论
MOVERY方法包含两个核心阶段:(1) **特征生成阶段(P1)**与 (2) VCC检测阶段(P2)。P1阶段通过函数校勘与核心代码行提取技术生成可扩展特征签名,旨在应对漏洞代码的语法多样性问题。P2阶段为提升可扩展性,通过聚焦目标软件中源自其他OSS的代码片段来缩减检测范围,并基于双重判据实现精准检测:当且仅当目标函数满足漏洞特征匹配且与补丁特征存在显著差异时,方判定为VCC。图2展示了MOVERY的整体工作流程。
3.1 特征生成(P1)
给定三元组函数集合——最原始漏洞函数(fo)、已披露漏洞函数(fd)及修补后函数(fp),MOVERY将生成漏洞特征与补丁特征(漏洞函数与修补函数的获取方法详见第4.1节)。
以下是对该技术文本的专业翻译及解析:
3.1.1 特征生成原理
为有效应对漏洞代码克隆(VCC)的语法多样性问题,MOVERY的特征签名生成需满足以下三项核心原则:
(1)最小化原则
特征签名包含的代码行数与语法多样性处理能力呈负相关[49]。因此,MOVERY通过收集能够检测漏洞克隆的最少核心代码行构建特征签名。
(2)可扩展性原则
漏洞特征需具备版本扩展能力(即覆盖从最原始漏洞函数至已披露漏洞函数的语法变体),以应对OSS内部修改。
(3)可感知性原则
特征签名需能判定漏洞代码的上下文环境(如控制依赖关系)在传播过程中是否保持完整。
3.1.2 阶段流程
针对每个漏洞,MOVERY首先通过函数校勘技术分析fo与fd的共有代码行(实现可扩展性),随后从中提取两类核心代码行:
• 本质漏洞代码行(Essential Vulnerable Lines)
• 依赖漏洞代码行(Dependent Vulnerable Lines)(实现可感知性)
通过聚合上述核心代码行(遵循最小化原则),MOVERY生成漏洞特征。补丁特征的生成流程类似:分析fp后提取本质补丁代码行与依赖补丁代码行,最终合成补丁特征。
3.1.3 函数校勘技术
与现有VCC检测技术[14,17,49]仅关注fd不同,MOVERY通过分析(fo, fd)函数对生成可扩展漏洞特征,以应对OSS内部修改引发的语法多样性。由于fo与fd包含相同漏洞,我们推断同时存在于两者且被安全补丁删除的代码行(详见第6节)对漏洞表现具有关键作用。
3.1.4 本质漏洞/补丁行提取
为应对OSS外部修改导致的语法差异,MOVERY在特征生成时仅保留核心代码行。具体定义如下:
▶ 定义I:本质漏洞行与补丁行
• 本质漏洞行(EV):在安全补丁中被删除,且同时存在于fo与fd的代码行
• 本质补丁行(EP):在安全补丁中被添加,且不存在于fo与fd的代码行
数学形式化定义(设l为源代码行):
EV = { l | l ∈ (fd \ fp) ∧ l ∈ (fo ∩ fd) ∧ l ∉ fp }
EP = { l | l ∈ (fp \ fd) ∧ l ∉ (fo ∪ fd) ∧ l ∈ fp }
3.1.5 依赖漏洞/补丁行提取
技术背景:传播性本质漏洞行并不能完全确保漏洞行为在目标环境中依然有效。为此,MOVERY进一步分析依赖代码行——即与本质漏洞/补丁行存在控制流或数据流依赖关系的代码行,这些依赖关系对漏洞表现具有决定性影响[3,49]。
▶ 定义II:依赖漏洞行与补丁行
• 依赖漏洞行(DV):与本质漏洞行存在控制或数据依赖关系,且同时存在于fo与fd的代码行
• 依赖补丁行(DP):与本质补丁行存在依赖关系,且包含于fp的代码行
案例验证(以CVE-2016-8654为例)
该漏洞为Jasper图像库中发现的堆缓冲区溢出漏洞。代码清单5展示Jasper v1.900.31的补丁片段,代码清单6呈现Jasper v1.900.1最原始漏洞函数。在补丁删除的代码行(代码清单5第10-12行)中:
• 第10行存在于最原始漏洞函数,故属于EV
• 第13-15行(补丁新增行)不属于fo或fd,故归入EP
代码清单5:CVE-2016-8654补丁片段(Jasper v1.900.31版本)
1 // Jasper_1.900.31/src/libjasper/jpc/jpc_qmfb.c
2 void jpc_qmfb_split_col (...) {
3 ...
4 if (bufsize > QMFB_SPLITBUFSIZE) {
5 if (!(buf = jas_alloc2(bufsize, sizeof(jpc_fix_t)))) { // 【差异点】内存分配函数变更
6 abort();
7 }
8 }
9 if (numrows >= 2) {
10 - hstartcol = (numrows + 1 - parity) » 1; // 【差异点】变量名变更(hstartcol → hstartrow)
11 - // 原始(错误)代码:m = (parity) ? hstartcol : (numrows - hstartcol);
12 - m = numrows - hstartcol;
13 + hstartrow = (numrows + 1 - parity) » 1;
14 + // 修复代码:m = (parity) ? hstartrow : (numrows - hstartrow);
15 + m = numrows - hstartrow;
16 n = m;
17 dstptr = buf;
18 srcptr = &a[(1 - parity) * stride];
代码清单6:CVE-2016-8654最原始漏洞函数片段(Jasper v1.900.1版本)
1 // Jasper_1.900.1/src/libjasper/jpc/jpc_qmfb.c
2 void jpc_qmfb_split_col (...) {
3 ...
4 if (bufsize > QMFB_SPLITBUFSIZE) {
5 if (!(buf = jas_alloc(bufsize * sizeof(jpc_fix_t)))) { // 【差异点】单参数分配函数
6 abort();
7 }
8 }
9 if (numrows >= 2) {
10 hstartcol = (numrows + 1 - parity) » 1; // 【差异点】与补丁变量名不同
11 m = (parity) ? hstartcol : (numrows - hstartcol); // 【漏洞点】错误条件判断逻辑
12 n = m;
13 dstptr = buf;
14 srcptr = &a[(1 - parity) * stride];
技术差异分析:
-
函数接口变更
• 行5:jas_alloc()
(单参数)→jas_alloc2()
(双参数)内存分配函数接口升级 -
变量语义重构
• 行10:变量名从hstartcol
(列起始)变更为hstartrow
(行起始),反映计算维度修正 -
逻辑优化
• 行12/15:删除冗余三元运算符,直接计算m = numrows - hstartrow
提升代码健壮性 -
漏洞模式
• 行11:原始错误条件判断导致整数溢出漏洞(CVE-2016-8654根本原因)
以下是对该技术文本的翻译及解析:
3.1.6 控制流代码行提取
数学形式化定义
设x 7→c y表示代码行x与y间的控制流依赖,x 7→d y表示数据流依赖,则依赖漏洞行(DV)与依赖补丁行(DP)可形式化定义为:
D<sub>V</sub> = { l | l ∈ (f<sub>o</sub> ∩ f<sub>d</sub>) ∧ (l 7→<sub>c</sub> l<sub>V</sub> ∨ l 7→<sub>d</sub> l<sub>V</sub>) }
D<sub>P</sub> = { l | l ∈ f<sub>p</sub> ∧ (l 7→<sub>c</sub> l<sub>P</sub> ∨ l 7→<sub>d</sub> l<sub>P</sub>) }
其中lV ∈ EV,lP ∈ EP。以CVE-2016-8654为例:
• 控制流依赖:代码清单5和6第9行(条件语句if(numrows >= 2)
)
• 数据流依赖:代码清单5第18行/代码清单6第14行(指针操作srcptr = &a[...]
)
3.1.7 漏洞控制流代码行提取
▶ 定义III:漏洞控制流代码行
漏洞控制流代码行(FV)定义为:从漏洞函数入口到本质漏洞代码行路径上的条件语句集合。
技术必要性:
漏洞函数控制流变更可能导致漏洞触发环境失效,进而引发误报。MOVERY通过提取关键控制流代码行(FV)增强环境感知能力。
CVE-2016-8654案例:
在代码清单5和6中,存在三条通向本质漏洞行(第10行)的条件语句:
- 第4行:
if(bufsize > QMFB_SPLITBUFSIZE)
- 第5行:
if(!(buf = jas_alloc(...)))
- 第9行:
if(numrows >= 2)
经函数校勘后,MOVERY提取共有的第4行与第9行作为FV,排除版本差异导致的第5行(内存分配检查)。
3.1.8 特征签名合成
最终,MOVERY通过聚合以下要素生成特征签名:
漏洞特征 SV = (EV, DV, FV)
补丁特征 SP = (EP, DP)
关键优化策略:
- 文本预处理:对特征代码行进行规范化处理(Normalization)与抽象化处理(Abstraction),消除不影响语义的语法变更(如空格、变量重命名)
- 冗余过滤:补丁特征中新增的控制流代码行已包含在EP,故无需重复纳入DP
以下是对该技术文本的翻译及解析:
3.1.9 特征预处理机制
预处理技术实现
MOVERY采用双重预处理策略生成鲁棒性特征签名:
- 规范化处理(Normalization)
• 删除函数中所有空格与注释
• 统一转换为全小写字符格式
- 抽象化处理(Abstraction)
• 参数/变量名 →PARAM
• 变量类型 → DTYPE
• 被调用函数 → FCALL
• 数据变量 → DNAME
处理流程
-
特征元素识别:从三元组函数(fo, fd, fp)中提取EV/DV/FV等要素
-
双重预处理:
• 首轮仅应用规范化处理,生成norm
特征• 次轮联合应用抽象化+规范化,生成
abst
特征 -
特征存储策略:
• 同时保留两种预处理形式,缓解单一抽象方法导致的误报(详见3.2节)• 自动过滤字符数<15的代码行,避免通用短代码干扰(详见第6节讨论)
3.2 漏洞代码克隆检测(P2阶段)
给定目标软件 ( T ) ( T ) (T)、漏洞特征 ( S v ) ( S_v ) (Sv)、补丁特征 ( S p ) ( S_p ) (Sp)及漏洞函数集合 ( { f o , f d } ) (\{f_o, f_d\}) ({fo,fd}) ,MOVERY通过以下技术实现高效精准的VCC检测:
3.2.1 阶段流程概览
MOVERY采用双重优化策略:
- 搜索空间缩减:仅聚焦于复用OSS代码区域
- 选择性抽象匹配:动态选择特征匹配粒度
检测判据:目标函数被判定为VCC需同时满足:
- (1) 包含 ( S v ) ( S_v ) (Sv)特征
- (2) 不包含 ( S p ) ( S_p ) (Sp)特征
- (3) 语法与 ( f o / f d ) ( f_o/f_d ) (fo/fd)相似 :语法与补丁对应的漏洞函数相似或者最原始的漏洞函数相似
3.2.2 搜索空间缩减技术
技术原理:
基于先进软件成分分析(SCA)技术[48],将目标软件( T )的代码库划分为:
- 复用代码区:来自第三方OSS的代码片段
- 应用代码区:软件独有功能代码
MOVERY仅检测复用代码区,通过以下流程定位该区域:
- 函数路径提取:提取( T )和原始漏洞组件( C )的所有函数及其文件路径(如
./src/file.c
) - 公共函数比对:
- 存在公共函数:收集( T )中所有公共函数的目录路径,标记为复用代码区
- 无公共函数:判定( T )未复用( C ),跳过检测
技术优势:
- 避免全代码库扫描,提升检测效率(详见5.5节实验验证)
- 自动过滤无关OSS漏洞,增强可扩展性
3.2.3 选择性抽象匹配技术
技术挑战:
现有技术(如VUDDY[17])的全量抽象策略可能导致误判。例如当补丁仅修改变量名时(见代码清单5),抽象后的漏洞函数与补丁函数将无法区分。
MOVERY解决方案:
- 双重预处理:对( f_d )和( f_p )分别进行规范化与抽象化处理
- 动态匹配策略:
- 抽象特征差异:当( f_d )与( f_p )的抽象形式不同时,使用签名中的
abst
字段匹配 - 抽象特征相同:仅使用
norm
字段匹配,规避抽象失效场景
- 抽象特征差异:当( f_d )与( f_p )的抽象形式不同时,使用签名中的
案例说明(参考代码清单7):
- 若补丁仅修改
hstartcol
→hstartrow
(变量名变更),抽象后均为DVAL
,此时启用规范化匹配 - 若补丁修改控制逻辑(如操作符变更),抽象形式不同,启用抽象化匹配
代码清单7:CVE-2016-8654漏洞特征签名示例
Sv = (
Ev = [{
"norm": "hstartcol=(numrows+1-parity)>>1;",
"abst": "DVAL=(PARAM+1-PARAM)>>1;"
}],
Dv = [{
"norm": "if(numrows>=2){",
"abst": "if(PARAM>=2){"
},{
"norm": "srcptr=&a[(1-parity)*stride];",
"abst": "DVAL=&PARAM[(1-PARAM)*PARAM];"
},...],
Fv = [{
"norm": "if(bufsize>QMFB_SPLITBUFSIZE){",
"abst": "if(DVAL>QMFB_SPLITBUFSIZE){"
},...]
)
预处理定义
- 规范化(Normalization):
删除函数中所有空格与注释,并将函数内所有字符转换为小写形式。 - 抽象化(Abstraction):
将函数中出现的参数、变量名、变量类型及被调用函数名分别替换为符号PARAM
、DNAME
、DTYPE
和FCALL
。
签名生成流程
- 代码行识别:MOVERY首先确定需存入签名的所有代码行。
- 规范化处理:对给定三元组函数(即
f_o
、f_d
和f_p
)应用规范化,并将处理后的代码行存入签名的"norm"
字段。 - 联合处理:对三元组函数同时应用抽象化与规范化,结果存入签名的
"abst"
字段。
双重存储目的:
同时保留规范化与抽象化形式,旨在最小化因简单抽象方法导致的误报(详见第3.2节)。
过滤规则:
若规范化代码行字符数小于15,MOVERY将跳过该行,以避免通用短代码引发的误报(该决策分析详见第6节)。
漏洞代码克隆检测流程
MOVERY最终通过对比漏洞特征( S_v )与补丁特征( S_p ),在目标软件的复用代码区域中识别漏洞代码克隆(VCC)。
▶ 定义IV:漏洞代码克隆
若目标软件( T )中的函数( f )满足以下条件,则判定为VCC:
- 条件1:( f )包含( S_v )中所有代码行
( ∀ l ∈ S v . ( l ∈ f ) ) ( \forall l \in S_v.\ (l \in f) ) (∀l∈Sv. (l∈f)) - 条件2:( f )不包含( S_p )中任何代码行
( ∀ l ∈ S p . ( l ∉ f ) ) ( \forall l \in S_p.\ (l \notin f)) (∀l∈Sp. (l∈/f)) - 条件3:( f )的语法与( f_o )或( f_d )相似
( Sim ( f , f o ) ≥ θ ∨ Sim ( f , f d ) ≥ θ ) ( \text{Sim}(f, f_o) \geq \theta \ \lor \ \text{Sim}(f, f_d) \geq \theta ) (Sim(f,fo)≥θ ∨ Sim(f,fd)≥θ)
检测实现细节
-
候选函数筛选
当函数( f )满足条件1和条件2时,MOVERY将其标记为VCC候选。 -
误报过滤机制
为避免签名代码行过少或过于通用导致的误报,引入条件3:- 将( f )与漏洞函数 ( f o / f d ) ( f_o/f_d ) (fo/fd)拆分为代码行集合
- 计算Jaccard相似度[46]:
( ∣ f ∩ f d ∣ ∣ f ∪ f d ∣ ) ( \frac{|f \cap f_d|}{|f \cup f_d|} ) (∣f∪fd∣∣f∩fd∣)和 ( ∣ f ∩ f o ∣ ∣ f ∪ f o ∣ ) ( \frac{|f \cap f_o|}{|f \cup f_o|} ) (∣f∪fo∣∣f∩fo∣) - 设置低阈值 ( θ ) ( \theta ) (θ)(如0.5)以兼容OSS外部修改
特殊场景处理策略
为覆盖更多漏洞类型,MOVERY采用条件组合检测:
场景 | 条件组合 | 技术应对 |
---|---|---|
1. 补丁无新增代码 | 仅用( S_v )(条件1+3) | 忽略( S_p )检测 |
2. 补丁无删除代码 | 仅用( S_p )(条件2+3) | 忽略( S_v )检测 |
3. 无原始漏洞版本 | 仅用( f_d )生成签名及条件3 | 跳过内部修改分析 |
4. 代码行顺序变更 | 条件1+3 + 代码行顺序验证 | 记录漏洞函数代码顺序 |
实验验证:上述四类非主导场景(占比<30%)下,MOVERY仍能保持高检测精度。
4 MOVERY系统实现
本章介绍MOVERY的具体实现方案,包括漏洞数据集构建方法与系统架构设计。
4.1 漏洞数据集构建
安全补丁采集
我们采用既有研究方法[19,32],通过美国国家漏洞数据库(NVD)筛选包含Git提交链接(即安全补丁提交记录)的CVE条目,并爬取对应Git仓库中的补丁提交。选择C/C++语言漏洞作为初始目标,因为C/C++软件中普遍存在代码片段复用与修改后OSS复用现象[17,47-49]。截至2021年3月,共收集4,219个C/C++安全补丁。
函数重构方法
从安全补丁中重建漏洞函数( f_d )与修补函数( f_p ):
- 解析补丁头信息获取漏洞文件与修补文件的提交ID(如代码清单3中
aafa3dca2
表示漏洞文件,d02e11328
表示修补文件) - 从漏洞文件中提取包含补丁删除行的函数作为( f_d )
- 从修补文件中提取包含补丁新增行的函数作为( f_p )
- 排除非漏洞修复性变更(如注释/空格修改)的( (f_d,f_p) )对
最旧漏洞函数( f_o )重建
- 根据NVD提供的通用平台枚举(CPE)[30]定位漏洞起源的最旧软件版本
- 手动验证CPE准确性:从CPE标注的最旧版本开始顺序检测,首个包含与( f_d )同名函数的版本被确定为最旧漏洞版本
- 处理并行版本分支(如OpenSSL 1.0.0与1.0.1):依据版本发布日期推断时序关系
- 从最旧版本中提取满足以下条件的( f_o ):
- 函数名与( f_d )一致
- 存在于漏洞文件路径中
- 语法与( f_d )存在差异
最终重构7,762组( (f_d,f_p) )对与5,936个最旧漏洞函数(数据统计见表2)。
数据集观测分析
OSS内部修改对漏洞代码语法多样性具有主导影响:
- 在5,936个存在多版本漏洞函数中,78%(4,623例)的( f_d )与( f_o )存在语法差异
- ( f_d )与( f_o )的Jaccard相似度平均值仅为56%
- 上述事实表明:44%的漏洞代码语法变更由OSS内部更新引起,验证了VCC检测中考虑内部修改的必要性
4.2 系统架构
MOVERY由以下三大核心模块构成:
- 数据集采集器:负责收集安全补丁并重构漏洞函数与修补函数
- 特征生成器:为每个漏洞生成漏洞特征与补丁特征
- VCC检测器:在目标软件中执行实际漏洞代码克隆检测
各模块采用800-1,000行Python代码实现(不含函数解析器等外部依赖)。MOVERY源代码已开源:
https://github.com/wooseunghoon/MOVERY-public
函数解析与分析工具
- 函数提取:采用高精度开源工具Universal Ctags[6]从漏洞文件、修补文件及目标软件中提取函数
- 依赖分析:使用Joern解析器[50]分析函数的控制流与数据流依赖关系,用于特征生成
- 生成代码属性图(整合抽象语法树、控制流图与程序依赖图)
- 通过图结构获取:
(1) 函数的完整控制流路径
(2) 代码行间依赖关系
5 实验评估
本章从多维度评估MOVERY性能:
- 5.1节:对比现有VCC检测技术(VUDDY[17]、ReDeBug[14]),验证MOVERY在修改组件中的检测精度
- 5.2-5.3节:分别对比循环漏洞检测技术MVP[49]与SCA技术CENTRIS[48]
- 5.4节:评估MOVERY检测速度与可扩展性
- 5.5节:分析搜索空间缩减机制的实际效能
- 5.6节:典型检测案例研究
实验环境:
- 操作系统:Ubuntu 16.04 LTS
- 处理器:Intel Xeon @ 2.40GHz
- 内存:32GB DDR4
- 存储:6TB HDD
5.1 MOVERY检测精度评估
目标软件选择标准
为验证MOVERY的泛化能力,目标软件需满足:
- 热门性:属于热门的C/C++开源软件
- 组件修改性:包含足够数量的修改后OSS组件
- 领域多样性:避免特定领域偏差
筛选流程:
- 从GitHub收集星标数(stargazer counts)>1,000的C/C++仓库(星标数作为流行度指标)
- 使用CENTRIS[48]根据修改组件数量对仓库排序
- 从高到低筛选至少被MOVERY检测到1个VCC的软件
目标软件数据集
表3汇总了最终选定的10款目标软件,其特点包括:
- 领域覆盖:跨领域选取(网络服务、数据库、嵌入式系统等)
- 代码规模:C/C++代码量跨度大(212,672 - 14,489,534行)
- 数据代表性:累计代码量达VUDDY[17]数据集的80倍,与MVP[49]数据集规模相当
实验方法论
对比基准:
- ReDeBug[14](基于补丁行上下文的VCC检测)
- VUDDY[17](基于函数哈希的克隆检测)
实验配置:
- 对10款目标软件同时应用MOVERY、ReDeBug与VUDDY
- 基准工具采用默认参数配置(参照原始论文[14,17])
- MOVERY相似度阈值θ设为0.5(本节末尾含阈值敏感性分析)
评估指标:
- 真阳性(TP):正确检测的VCC数量
- 假阳性(FP):误报的VCC数量
- 假阴性(FN):漏报的VCC数量
- 精确率(Precision):( \text{Precision} = \frac{#TP}{#TP + #FP} )
- 召回率(Recall):( \text{Recall} = \frac{#TP}{#TP + #FN} )
FN测量约束:
由于无法穷尽检测目标软件中所有漏洞,仅测量无可争议的FN(例如:被VUDDY/ReDeBug检出但MOVERY漏报的VCC[17,49])
人工验证流程:
由两名安全分析师对检测结果进行人工审核,通过以下步骤判定结果有效性:
- 审查VCC代码上下文
- 验证漏洞触发环境是否完整
- 交叉核对补丁修复有效性
代码清单8:FreeType2中CVE-2014-9669补丁片段
1 tt_cmap12_validate (...) {
2 num_groups = TT_NEXT_ULONG( p );
3 if ( length > (FT_ULong)( valid->limit - table ) ||
4 - length < 16 + 12 * num_groups )
5 + /* length < 16 + 12 * num_groups ? */
6 + length < 16 ||
7 + ( length - 16 ) / 12 < num_groups )
代码清单9:CVE-2014-9669最原始漏洞函数片段(FreeType2 v2.1.10)
1 // 提取自FreeType2 v2.1.10("./src/sfnt/ttcmap.c")
2 tt_cmap12_validate (...) {
3 ...
4 num_groups = TT_NEXT_ULONG( p );
5 if ( table + length > valid->limit || length < 16 + 12 * num_groups ) // 【差异点】条件表达式结构不同
检测精度总览
表4汇总了MOVERY、VUDDY与ReDeBug的VCC检测结果。实验中共发现434个VCC(来自121个CVE漏洞),其中ReactOS项目因包含大量漏洞函数导致检出数最多。
关键发现:
- 高语法差异场景:91%的VCC(396个)与公开漏洞函数存在语法差异
- MOVERY优势:检出415个VCC(精确率96%,召回率96%)
- 基准工具对比:
- ReDeBug检出163个(精确率65%,召回率38%)
- VUDDY检出72个(精确率77%,召回率17%)
覆盖性分析:
- MOVERY覆盖了VUDDY检出的所有VCC(100%),以及ReDeBug检出的88%(144个)
- VUDDY与ReDeBug仅分别覆盖MOVERY检出量的17%(72个)与35%(144个)
现有技术漏报(FN)分析
-
ReDeBug漏报:
- 62.4%漏报率(271个)
- 失效场景:
- 安全补丁中删除的代码行在目标函数中不存在
- 代码语法超出ReDeBug处理能力(如多行逻辑重构)
-
VUDDY漏报:
- 83.4%漏报率(362个)
- 失效场景:
- 代码修改类型超出其检测范围(如Type-3语义克隆)
- 内部OSS修改导致签名失效(见CVE-2014-9669案例)
典型案例(CVE-2014-9669):
- 漏洞类型:FreeType2整数溢出漏洞
- 技术失效原因:
- ReDeBug/VUDDY依赖的漏洞行(代码清单8第4行)在原始漏洞函数中因内部修改呈现不同语法(代码清单9第5行)
- 导致基准工具无法检测,MOVERY通过溯源最旧版本成功捕获
ReDeBug与VUDDY的误报(FP)分析
ReDeBug误报根源:
- 缺乏代码规范化:未对非安全变更(如注释修改)进行标准化处理,误判未修改代码为漏洞
- 忽略函数语义:当补丁新增代码行与补丁前代码行部分相同时,误将已修补函数标记为漏洞[17,49]
VUDDY误报根源:
- 抽象化局限性:若补丁仅修复VUDDY的抽象目标(如变量重命名),抽象后漏洞函数与补丁函数无法区分,导致误报
MOVERY检测精度详析
表5总结了MOVERY可检测但现有技术难以发现的VCC类型:
类型 | 定义 | 占比 | 技术突破 |
---|---|---|---|
T1 | 与最旧漏洞函数( f_o )语法高度相似 | 8%(32个) | 函数校勘技术溯源原始漏洞 |
T2 | 与( f_o )的代码相似度高于( f_d ) | 56%(221个) | 兼容内部版本演化差异 |
T3 | 与( f_d )相似度<50% | 166个(T2子集) | 外部修改鲁棒性检测 |
关键优势:
- 选择性抽象优化:消除VUDDY报告的22个误报中的21个(95.5%)
- 跨类型检测能力:检出34个ReDeBug无法发现的Type-2[34]类VCC
MOVERY误报场景
- 抽象化失效:
- 案例:代码清单10中第5-6行经抽象化后均转换为
FCALL(PARAM);
,即使应用补丁仍误判为漏洞
// Listing 10示例 5 original_function(param); // 抽象后 → FCALL(PARAM); 6 patched_function(param); // 抽象后 → FCALL(PARAM);
- 案例:代码清单10中第5-6行经抽象化后均转换为
- 语法相似函数干扰:
- 场景:补丁( p )与( p’ )分别应用于语法相似函数( f )与( f’ ),MOVERY误判( p’ )未应用于( f )(反之亦然)
- 概率:实验中出现概率极低(<0.5%)
MOVERY漏报(FN)场景
MOVERY在以下两种场景中报告漏报:
- 语义相似但语法变更的代码(例如
for
循环改为while
循环) - VCC与漏洞函数的相似度低于阈值θ
尽管降低θ值可增加检出量,但会引入更多误报。实验表明当前θ=0.5的设置实现了精确率与召回率的最佳平衡,MOVERY性能显著优于现有技术。
阈值敏感性分析
在VCC检测实验中,默认阈值θ设为0.5。为评估参数敏感性,以0.1为步长在[0,1]区间调整θ并测量指标变化:
评估方法:
- 数据基础:基于已检出的434个VCC(因新检出VCC人工验证成本过高)
- 评估指标:
- 精确率(Precision)与召回率(Recall)
- F1分数[45]:
F 1 = 2 × Precision × Recall Precision + Recall F1 = \frac{2 \times \text{Precision} \times \text{Recall}}{\text{Precision} + \text{Recall}} F1=Precision+Recall2×Precision×Recall
实验结果(图3):
- 最优平衡点:θ=0.5时F1分数最高
- 项目差异:
- Crown与Emscripten项目在θ>0.5时F1更高(因其VCC修改比例低,高θ减少误报)
- 全局趋势:
- θ<0.5时精确率下降
- θ>0.6时召回率显著降低
边界案例:
- 低θ扩展性:θ<0.5时MOVERY额外检出15个VCC(原始实验未覆盖)
- 残余误差:
- θ=0时仍存在4个漏报
- θ=1时仍存在3个误报
(详见第6节讨论)
5.2 与MVP的对比
我们进一步将MOVERY与MVP[49](一种循环漏洞检测技术)进行对比,以验证MOVERY在检测修改组件中VCC的优越性。由于MVP未开源,我们基于其论文[49]复现了该工具并采用默认配置。
实验结果分析
MVP在10款目标软件中共发现220个VCC(真阳性,TP),但报告了46个误报(FP)和184个漏报(FN)(详见表6)。关键发现如下:
-
检测局限性:
- MVP未考虑OSS内部修改,导致其几乎无法检测T1与T2类VCC(见表5)
- T1类漏报(32个):漏洞函数未包含安全补丁删除的代码行
- T2类漏报(142个):漏洞函数因内部修改(如变量类型/调用函数名变更)导致语法差异
-
误报根源:
- 语法相似性误判(11个FP):安全函数与漏洞函数语法相似但语义不同
- 抽象化失效(35个FP):补丁仅修复MVP的抽象目标(如变量名)导致无法区分漏洞与修补函数
- MOVERY通过选择性抽象消除了35个误报中的31个
-
MOVERY漏报:
- MVP检出8个MOVERY未发现的VCC,均与公开漏洞函数语法相似度低于θ
- 其中2个VCC未被VUDDY和ReDeBug检出
结论:MVP因未考虑OSS内部修改,漏报量(184个)显著高于MOVERY,验证了MOVERY在修改组件检测中的高效性。
有效性威胁:
- MVP复现可能存在细节偏差,且其设计初衷为检测循环漏洞而非传播性漏洞
- 本研究旨在证明MOVERY在修改组件检测中的优势,而非否定MVP的总体有效性
5.3 与CENTRIS的对比
现有方法(如商业工具Trivy[1]、Black Duck[38])及SCA技术CENTRIS[48]试图通过识别复用OSS组件及其版本来发现漏洞,但未考虑修改后组件的复用问题。我们基于CENTRIS的组件识别能力,利用CVE Details[7]的"产品搜索"和"版本搜索"功能关联漏洞,模拟其VCC检测流程。
实验结果分析
CENTRIS方法在10款目标软件中报告553个VCC,其中:
-
误报率高达49%(272个FP):
- 漏洞代码未被实际复用
- 漏洞已通过回移植修补(案例见第2.2节)
-
漏报率38%(152个FN):
- CENTRIS未能识别含漏洞的组件
- 版本信息预测错误
-
MOVERY未覆盖的37个VCC:
均因补丁未通过Git发布导致,若补丁加入数据集,MOVERY亦可检出
结论:仅基于组件名称和版本的漏洞检测方法因忽略OSS修改产生大量误报,MOVERY在修改组件的漏洞发现能力显著优于SCA方法。
关键数据总结
指标 | MOVERY | MVP | CENTRIS |
---|---|---|---|
真阳性(TP) | 415 | 220 | 553* |
误报(FP) | 22 | 46 | 272 |
漏报(FN) | 21 | 184 | 152 |
精确率(Precision) | 96% | 65% | 51% |
召回率(Recall) | 96% | 38% | 62% |
*注:CENTRIS报告的TP中包含大量误判,实际有效TP需人工验证。
5.4 MOVERY的检测速度与可扩展性
本节评估MOVERY在VCC检测中的时间效率与扩展能力。我们将总耗时划分为三个部分:
- 特征生成时间:生成漏洞/补丁签名的耗时
- 目标预处理时间:分析目标软件代码结构的耗时
- 匹配时间:执行VCC检测的耗时
实验结果对比
指标 | MOVERY | ReDeBug | VUDDY |
---|---|---|---|
特征生成时间 | 32小时 | 0小时* | 2小时 |
平均预处理时间/软件 | 45秒 | 28秒 | 298秒 |
平均匹配时间/软件 | 155秒 | 132秒 | 85秒 |
总耗时/软件 | 200秒 | 160秒 | 383秒 |
*注:ReDeBug直接使用原始补丁,无需特征生成阶段。
关键发现
-
特征生成效率:
- MOVERY需32小时处理4,219个补丁(见表2),耗时高于VUDDY(2小时),原因包括:
- 需重构最旧漏洞函数
- 分析代码行依赖关系
- 该阶段为一次性任务,不影响日常检测
- MOVERY需32小时处理4,219个补丁(见表2),耗时高于VUDDY(2小时),原因包括:
-
预处理时间对比:
- VUDDY因需构建全量函数哈希表,预处理时间最长(298秒/软件)
- MOVERY通过复用代码区域缩减,预处理时间仅45秒/软件
-
匹配时间分析:
- MOVERY因采用细粒度代码行集合匹配(而非函数单元)及依赖分析,匹配时间最长(155秒)
- 但总耗时仍为三者最短(200秒/软件),且在14.5M行代码量下仍保持稳定(见表3)
5.5 搜索空间缩减机制效能
MOVERY通过聚焦"借用代码部分"(borrowed code parts)优化检测效率:
可扩展性提升
指标 | 全代码库 | 借用代码部分 | 缩减比例 |
---|---|---|---|
目录数量 | 539,781 | 375,489 | 30%↓ |
需扫描代码行数 | 28,548,340 | 13,417,720 | 53%↓ |
技术收益:跳过约53%的非复用代码,显著降低计算负载。
检测精度提升
- 误报减少机制:
- 现有技术在全代码库搜索时,易将语法相似但语义无关的函数误判为VCC
- MOVERY通过限制检测范围,消除:
- VUDDY的3个误报
- ReDeBug的24个误报
典型案例:
- 误报场景:某日志处理函数与漏洞签名语法相似(如
memcpy
调用模式),但位于非复用区域 - MOVERY应对:通过代码区域过滤自动排除
5.6 案例研究:Git中的漏洞
MOVERY发现,针对Glibc v2.30之前版本存在的堆缓冲区越界读取漏洞(CVE-2019-9169)的修复补丁,未应用于最新版Git(最流行的版本控制系统之一)。由于Git复用了v2.27之前的旧版Glibc,其漏洞代码与CVE-2019-9169公开补丁的语法差异显著(相似度仅为65%)。更为严重的是,我们验证了该漏洞在最新版Git中仍可引发内存泄漏。我们已向Git团队提交责任报告,其确认漏洞存在并回复称将在后续任务中修复,因当前漏洞未构成严重威胁(责任披露机制详见第6节讨论)。
6 讨论
阈值设定策略
实验中使用了两项阈值:
- 短代码行过滤:跳过字符数<15的规范化代码行
- 相似度阈值θ:设为0.5(相关实验见第5.1节)
短代码行过滤依据:
人工分析显示,字符数<15的规范化代码行中,超过90%为高频出现的非漏洞代码,例如:
return
语句- 常见变量声明(如
int i;
) - 括号
else
/continue
等控制语句
若特征签名包含此类短代码行,可能引发误报。当略微提高字符限制(如16-17字符)时,非漏洞代码比例下降。因此,最终设定阈值为15字符。
阈值适应性:
此阈值依赖具体漏洞数据集。若MOVERY应用于其他数据集,需重新校准该值。
6.1 最旧漏洞函数的应用
MOVERY在生成特征签名时引入最旧漏洞函数( f_o ),以应对OSS内部修改。尽管MOVERY可使用任何与已披露漏洞函数( f_d )存在语法差异的旧版本函数,但选择最旧版本的动机在于覆盖更多内部修改场景。
核心逻辑验证:
假设需从漏洞函数中提取本质漏洞代码行(详见3.1节),可能存在三种情况:
- 同时存在于( f_o )与( f_d )
- 均不存在于两者
- 仅存在于其中一个
设( v_o )为最旧漏洞版本,( v_d )为已披露漏洞版本。通过以下推论可验证MOVERY方法的合理性:
- 推论1:若本质漏洞代码行在( f_o )与( f_d )中均不存在,则( v_o )与( v_d )应被排除出受影响版本
- 推论2:若本质漏洞代码行仅存在于( f_d ),则( v_o )不属于受影响版本
因此,只要NVD提供的CPE信息正确(数据源见4.1节),且漏洞影响范围包含( v_o )与( v_d ),本质漏洞代码行必然同时存在于两者,验证了MOVERY方法的有效性。
6.2 漏洞披露实践
我们向开发团队报告了14个可触发的VCC(含PoC验证),涉及Git、OpenMVG等目标软件及LibAV、LibGDX等流行OSS项目。其中:
- 10个VCC(71%)为MOVERY独有检出(现有技术[14,17,49]无法发现)
处理进展:
- 已确认漏洞:9个团队确认报告(2个已修复,2个计划修复)
- 讨论中:5个案例(如LibAV、OpenMVG)仍在沟通(含1个待处理PR)
- 暂缓披露:未复现的VCC暂不披露,待通过协作复现(参考方法[18,27])或补丁应用后跟进
6.3 局限性分析
MOVERY存在以下应用限制:
1. 依赖源码可获取性
- 当前仅支持源码级检测,若二进制文件能精确提取函数控制/数据流依赖,未来可扩展至二进制场景
2. NVD的CPE准确性[8,47]
- 部分CVE的CPE信息错误,可能影响最旧漏洞函数重构精度
- 尽管已人工验证(见4.1节),但效率低下,需依赖CPE准确性提升
3. 函数内漏洞检测局限
- 无法检测跨函数漏洞(如函数间依赖或C预处理器相关漏洞)
- 未来计划通过引入函数间数据流关联信息扩展检测能力
4. 残余误差
- 即使θ设为极值(0或1),仍存在4个漏报(FN)与3个误报(FP):
- FN根源:语义相似但语法变更的代码(如循环结构转换)
- FP根源:语法相似但补丁不同的函数
- 若支持Type-4克隆检测或停用抽象化可缓解,但会牺牲可扩展性与精度
- 当前方案在性能均衡性上显著优于现有技术,残余误差留待未来研究
7 相关工作
本节系统梳理与本研究相关的技术领域。
7.1 代码克隆检测技术
现有代码克隆检测技术(如[4,10,15,16,24,28,29,33-36,41,42])主要关注通用代码复用场景。由于未考虑漏洞特异性,此类技术直接应用于VCC检测时会产生大量误报[17,49]。
7.2 软件成分分析技术
软件成分分析(SCA)技术(如[2,9,21,25,39,48,51,52])旨在识别目标软件中的第三方组件。典型应用包括:
- OSSPolice[9]:通过常量特征匹配检测Android应用的1-day漏洞
- ATVHunter[51]:利用控制流信息精准识别Android第三方库版本
但此类技术无法应对补丁回移植或非复用漏洞场景(详见5.3节误报分析),难以解决本研究核心问题。
7.3 VCC检测技术
主流VCC检测方案包括:
- ReDeBug[14]:基于切片窗口技术的Token级检测
- VUDDY[17]:函数级可扩展检测框架
- VGRAPH[3]:基于代码属性图(CPG)的Type-3克隆检测
- MVP[49]:通过切片代码行实现低相似度漏洞检测
现有技术存在两大局限:
- 无法有效检测OSS内部修改引发的VCC
- 过度依赖补丁删除行检测(详见5.1-5.2节精度对比)
此外,基于学习的方法(如[22,23])与缺陷检测技术(如[20,26])虽具备通用漏洞发现潜力,但难以精准识别修改后OSS传播的VCC。
8 结论
修改后OSS组件中传播性漏洞的检测是软件安全领域的紧迫挑战。本研究提出MOVERY方法,通过三重技术创新实现高精度VCC检测:
- 跨版本函数校勘:溯源最旧漏洞函数应对内部修改
- 选择性抽象匹配:动态调整特征粒度平衡检测效能
- 复用代码空间缩减:聚焦风险区域提升可扩展性
实验表明,MOVERY在检测精度(96%精确率/96%召回率)与效率(200秒/项目)上显著优于现有技术。通过MOVERY的检测结果,开发者可及时修复修改后OSS组件中的潜在威胁,助力构建更安全的软件生态体系。