@inproceedings{brown2019carve,
  title={Carve: Practical security-focused software debloating using simple feature set mappings},
  author={Brown, Michael D and Pande, Santosh},
  booktitle={Proceedings of the 3rd ACM Workshop on Forming an Ecosystem Around Software Transformation},
  pages={1--7},
  year={2019}
}

CARVE: Practical Security-Focused Software Debloating Using Simple Feature Set Mappings

0 摘要

  • 现有技术对普通用户不实用存在创造不稳定程序和引入漏洞的风险,而且不适用于debloating网络协议实现等复杂软件
  • 本文我们提出了CARVE,这是一种简单而有效的以安全为重点的debloating技术
    • 采用静态源代码注释来映射软件特性源代码,在debloating过程中消除了对高级软件分析的需求,降低了用户所需的整体技术水平
    • CARVE通过引入替代debloating技术超越了现有技术,该技术能够保留软件的互操作性并减轻创建不稳定程序或引入漏洞的风险
    • 我们在12个debloating场景中评估了CARVE,并展示了满足或超过现有技术的安全性和性能改进。

1 介绍

  • 软件膨胀对几乎所有软件都产生负面影响,在安全性和性能方面产生各种问题[1,6,7]
  • 膨胀的主要来源是功能蔓延,即软件包随着时间的推移积累新功能的趋势[8]
  • 膨胀的另一个来源是软件工程实践,这些实践倾向于重用模块化的软件库。程序通常仅使用每个外部库提供的功能的一个小子集[1],然而,在运行时,整个库都加载到程序的内存空间中。
  • 现有技术[2-5,9-10,18]存在限制

1.1 动机

  • 基于功能的简化方法基于复杂的规范和先进的分析技术,有很高的技术门槛,一般用户难以使用
    • Chisel[3]需要用户提供测试脚本,脚本需要编译源代码并包含对生成程序的测试,且消耗时间长
    • TOSS[2]和TRIMMER[4]要求用户和LLVM,angr,TEMU等复杂工具进行交互以简化
  • 以上技术只能删除代码,无法保留良好的软件属性,如优雅的错误处理、用户体验、规范合规性和互操作性
    • 如ChiselBench中Chisel的简化结果会产生无意义的输出
    • TOSS简化的服务器软件对请求无法以规范协议响应
  • 现有方法在安全性上也存在限制
    • 使用动态分析引导去膨胀的技术,如 CHISEL 和 TOSS,由于对与功能相关的代码识别的不精确方式,可能导致产生不稳定的程序并引入新的漏洞 [18]
    • 在源代码级别操作且只能进行代码删除的去膨胀工具(例如 CHISEL)也可能由于功能特定代码与与功能无关的代码之间的交互而引入新的漏洞
    • 正如 Brown 和 Pande [22] 所示,改变程序的表示会以不可预测的方式引入新的Gadget,产生安全影响,多次简化和分析迭代可以减缓这种影响,而简化慢的工具不适合这项任务(如Chisel)

1.2 贡献

  • 提出CARVE:一种简单有效以安全为中的的简化方法

    • CARVE 不使用动态分析来识别与功能相关的代码,相反,用户使用简单灵活的基于注释的映射,将功能静态的映射到实现它们的源代码
    • 这些功能映射允许用户进行稳健的、细粒度的去膨胀,可以选择用保留高级程序属性的替代代码替换去膨胀的代码,例如协议兼容性,并防止引入漏洞
    • CARVE 在预处理步骤中去膨胀软件,将具有特定功能映射源代码 和 识别不需要的功能的配置文件 作为输入
    • CARVE扫描源代码并智能地删除与不需要的功能相关联的代码,并根据映射插入替代代码。
    • 其简化高效,因为与特征映射解耦,最终产品是一个仅包含所需功能的源代码的精简版本,可以在不更改构建链的情况下进行处理。
  • 我们通过使用CARVE从四个不同程度的侵略性的网络协议实现中去膨胀功能来评估CARVE

    • 这类软件具有输入/输出复杂性、规范合规性、跨平台互操作性和状态性质,去膨胀这类软件是具有挑战性的。
    • 我们分析了我们的方法产生的去膨胀变体,并观察到安全性和性能方面的好处,包括消除漏洞、减少代码重用小工具集效用、减小代码大小和消除外部依赖
  • 下面第2节讨论Carve实现细节,第3节提供评估结果,第4节确定未来工作

2 设计

  • 基于特征的简化将软件特征映射到相应源代码并随后删除不需要的代码

    • 现有方法通过动态方法 处理映射,即使用具体输入执行程序,即Cov;
    • 这些方法存在显著局限性
      • 它们要求用户提供复杂的规范或依赖于先进的软件分析工具生成映射。
      • 它们难以识别非确定性特征、由外部条件触发的特征(例如拥塞控制)或没有可观察输出的特征(例如性能特征)。
      • 简化软件的规模:因为确保生成的映射的正确性可能需要大量执行,这些方法的成本随着输入空间和执行时间的增加而上升,可能在处理大型、功能丰富的程序时变得不切实际。
  • CARVE通过静态特征映射克服了这些限制,其中用户直接将映射放置在目标程序的源代码中

    • 基于注释的特征映射是程序员熟悉的技术,这种方法在 自动化软件文档工具Doxygen,Sphinx[24,25] 中使用
      • 类似于C/C++预处理器指令(如 #ifdef)
      • 且不仅限于输入触发的特征,也可以在指令级别防止
    • 缺点:
      • 需要手动进行注释进行特征映射,工作量可能大;
      • 然而现代软件大部分重视代码质量,因此大多数软件特征可以直接映射到其关联的源代码
    • 针对缺点
      • CARVE 通过整合几个设计元素来减少用户生成映射的工作量,由此减轻了缺点
      • 由于特征映射为静态且在源代码中持久存在,因此生成映射的工作量仅需一次
      • 此外,软件特征可以嵌套在特征结构中,简化了将 某个代码段 映射到多个相关特征的任务
      • CARVE还支持隐式特征映射,对常见代码结构进行语法感知进行简化,自动处理因为代码移除造成的控制流影响. 隐式映射提供了比预处理指令方案更丰富的简化支持

2.1 特征映射剖析

  • 下图展示内嵌映射,由三部分组成
      1. 用户配置标记(///),将注释和特征映射分开
      1. 紧随标记,用户标明一个或多个特证明,用[] 包裹
      1. 可选操作符,用来区分 显式文件映射,显式段映射,隐式段映射

2.2 显式特征映射

  • 支持两种显式映射(图中蓝色标记)
      1. 文件映射使用 ! 操作符,指导简化器移除文件所有代码如果特征不需要
      1. 段显式映射,使用 ~操作符,指导简化器移除到下一个///~之间的代码段,如果该功能不需要
      • 对于段显式映射,还可以在两个替换标记之间///^指定替换代码段 ,例如,第13行的带替换映射指示debloater移除第17-19行的代码,并用第15行的代码替换它。

2.3 隐式特征映射

  • 图中绿色标出,其通过将两个关键任务从简化器中去除,减少了生成特征映射所需的工作量
    • 首先,隐式特征映射不需要 终止标签来标记代码段的结束: 隐式映射指示简化器分析映射后的代码,从而确定其机构,进而确定因嘎嘎i删除的代码
    • 隐式映射 消除了使用 替代代码的显式映射, 来处理与控制流影响
      • 例如:switch语句或条件分支 的隐式映射指示简化器分析删除映射代码的控制流影响,以产生 不产生意外控制流的简化代码
        • 图二中,第9行映射时,简化器将知道简化代码仅为一条语句,仅删除第10行
        • 第22行和第25行的映射函数定义,此时,简化器将扫描和函数定义相关联的右括号,以确认要删除哪些行,对于这些映射,简化器将删除第23行和26-32行
    • 下图展示了具有控制流的简化结构实例,右侧代码时左侧代码移除Feature_B后的结果 隐式映射
      • 在对第4行switch语句进行语法感知分析时,简化器将确定由于缺少break语句,6,7行的映射代码是可到达的,此时,简化器无法删除这些行,只能删除case标签
      • 对第12,13行条件分支语句进行分析,确定分支构造中的else块的存在 组织删除12行,为了保持else块的正确分支,只能删除else if块部分
    • 在简化器中处理隐形映射需要对不同PL进行特定的语法感知分析,因此,CARVE的设计支持使用自定义语言的简化模块. 针对C/C++的简化器支持单个语句,函数和结构定义,switch case以及条件分支的隐式映射

2.4 表达能力

  • 我们发现隐式特征映射在大多数情况下足够表达,能够正确识别某个功能的代码.
  • 但由于高级PL的灵活性,有时候隐式映射无法表达,此时如果有必要需要使用显式映射并使用替代代码. 此时通过删除和替代代码,任何简化操作都足够表达
  • 在我们4个基准测试中, 不得不使用显式映射+替换的比例为 5/668

2.5 简化器操作

  • CARVE使用的静态特征映射方案将 特征映射到代码 与 代码简化 解耦. 因此简化变得简易
  • CARVE流程
      1. CARVE 以一个单独的配置文件作为输入
      • 文件指定了特征映射源代码的未知
      • 代码所使用语言(指示语法感知模块)
      • 简化特征的层次结构
      • 用户希望简化的特征列表
      1. 执行时,简化器复制指定的源代码目录,并扫描特征映射的源代码,当发现特征映射时,简化器将于指定的简化特征进行比较,如果是需要简化特征的子集,简化器将处理该映射,否则继续扫描,直到所有文件都被扫描完毕

2.6 可靠性

  • 当用户提供的特征映射(包括替代代码)是正确的,Carve会是一种可靠的简化技术
    • 简化器执行的所有转换在语法和控制流上将会都是正确的
    • 静态特征映射是 细粒度的,将特征对应到源代码,因此映射可以精准对应到特征; 而对于动态方法,由于其不可预测性,可能会产生不正确的映射
      • 如Chisel被证明简化的程序引入了新的漏洞并产生了额外的意外行为[18]
      • TOSS使用的特征识别机制不能保证识别所有特征代码, 需要对期望的功能和不期望的功能进行 去噪模糊测试以检测不可靠的变体 [2]
  • 高级程序语言表达是高度抽象的,所以在简化时具有挑战性,删除代码时有时不得不产生漏洞
    • 下面代码中,从switch语句中删除任一case都不影响语义,但会在会授权的情况执行受保护的函数
    • CARVE通过支持 替代 代码来缓解这一问题,本例子中可以替代异常处理代码
switch(auth_type){
  case 1:perform_auth_1(creds);break;
  case 2:perform_auth_2(creds);break;
}
protected_function();
  • 于编写源代码类似,编写特征映射也可能出错
    • 在实践中,创建特征映射发生的错误可以使用 确保源代码正确的工具 来检测
    • 导致违反语言语义映射的错误会被编译器捕获
    • 成功编译的代码中存在的映射错误可以通过常见的测试技术,如单元测试和集成测试来识别
    • 使用Coverity [26] 这样的常见静态分析工具可以识别安全问题,比如无意中删除边界检查

3 评估

  • 选择4个网络协议软件包,这些软件包在大小,特征密度和复杂性方面都各不相同,简化这些软件时调整性的,因为其涉及复杂的交互和严格的规范,软件包括
    • libmodbus v3.1.4,一个工业协议库。
    • Bftpd v4.9,一个FTP服务器实用程序。
    • libcurl v7.61.0,一个数据传输实用程序库。
    • mongoose v.6.8,一个嵌入式Web服务器库。
  • 对每个软件包,我们手动创建了各种粒度的特征信息,以及三种不同的简化配置,以适应不同的场景
    • 保守(C):对软件包中的一些外围特征进行简化
    • 中等(M):对软件包中的一些外围特征和一些核心特征进行简化
    • 激进(A):对软件包中除了一小部分核心特征之外的所有可简化特征进行简化
  • 每种情景中
    • 我们使用CARVE对软件包进行简化,使用默认的软件包构建配置 和 GCC v7.3.0 构建了结果变体,所有软件在同一平台上构建,每个构建保持不变
    • 我们使用开发者提供的测试脚本和自定义测试脚本来测试所有变体,以确保其保留特征的正确性,并且调用简化掉的特征时不会导致变体崩溃

3.1 安全提升

  • 简化可以消除漏洞
      1. 在CVE库中搜索了选择软件包的已知漏洞
      • 对libmodbus和Bftpd,没有CVE,libcurl有6个,mongoose有17个
      • 随后分析简化后漏洞的消除情况,结果如图
      1. 软件抵抗代码重用攻击
      • 该攻击将执行重定向到内存中现有的指令,以引发恶意效果
      • 在基于小工具的代码重用攻击方法(如ROP、JOP和COP)中,执行被重定向到程序中存在的有序短指令序列(小工具),以构造恶意有效载荷而无需注入代码。
      • 评估基于 Gadget的安全 是复杂的,因为再简化后可能引入新的. 我们使用Brown和Pande [22]提出的度量来衡量CARVE对基于小工具的攻击的有效性,即特殊用途小工具的可用性和小工具集表达性。我们的结果显示在表1的分组列中。
  • 总体而言,CARVE在减少小工具集表达性方面是有效的,针对三个不同表达性水平 [20, 21]
    • 在十二个情景中的七个中,简化减少了整体小工具集的表达性
    • 在三个情景中简化没有效果
    • 在两个情景中产生了负面影响
      • 我们结果中观察到的负面副作用与CHISEL [22]和TRIMMER [4]中观察到的负面副作用一致。由于CARVE易于重新配置且去噪速度快,因此它非常适合使用迭代去噪来缓解这些负面副作用 [22]。

3.2 大小减小

  • 减小效果如下图
    • 二进制减小高于预期,因为对简化代码编译器效果优化的提升
    • 简化还消除了部分外部库的依赖,降低了运行时内存消耗
      减小效果

3.3 性能开销

  • CARVE的解耦涉及将简化开支最小化,每个情景的简化均不超过5s,同时也方便扩展
    • 技术的主要开销在功能映射上,创建和维护映射有很多工作量,包括对软件的输入程度,特征密度及目标软件的复杂性
    • 在实践中,我们发现在用户对源代码的了解有限的情况下,创建初始特征映射并会因成本而放弃
    • 软件工程实践倾向于模块化和分离软件包,这将帮助减少生成合理映射所需要的工作量
  • 对于我们的基准软件,生成密度如下(mappings/KLOC)
    • ibmodbus为61.5 m/KLOC
    • Bftpd为24.2 m/KLOC
    • mongoose为13.4 m/KLOC
    • libcurl为4.9 m/KLOC
    • 且很少使用替代映射,libmodbus需要17个,libcurl需要20个,另外两个不需要
  • 这标明创建特征映射的一次性成本是合理的。

4 讨论

  • 与现有技术比较
    • Chisel[2]和TOSS[3] 平均减少代码大小 86%,43.8%在ChiselBench中,Carve与其相当
    • Trimmer[4]平均减少代码大小 21% 在复杂的基于网络的软件中; 此时Carve性能优于Trimmer
    • 消除CVE方面,CARVE与CHISEL相当
    • CARVE在减少和引入新 gadgets方面与Chisel和Trimmer相当
  • 限制:
    • 仅对源代码操作,不适用于闭源软件
    • 可以用于任何基于文本且支持注释的软件,但目前只支持 C/C++ 语言
  • 未来工作
    • 基于创建其它语言的简化模块
    • 开发静态分析工具,帮助用户快速生成可靠的特征映射; 这些工具将实时分析用户创建的映射,提供建议的新映射,并识别个别简化时潜在的可靠性问题

5 结论

  • 本文介绍Carve,一种简单而强大的软件瘦身技术,它克服了现有方法的缺点
    • 在不需要先进软件分析的情况下实现有效简化
    • 同时保留了如 交互性和规范性 等理想软件特性
  • 评估显式
    • CARVE适合于解决网络协议的简化问题,在4个协议简化中,安全性和性能都很出色
    • 在12个场景中,CARVE消除了漏洞,减少了代码工具重用
    • 并使用了 易于用户使用的简单技术,缩减了代码大小,与现有方法相当