大家好,今天给大家分享的论文是发表在raid’20上的《Confine: Automated System Call Policy Generation for Container Attack Surface Reduction》。作者是来自SBU的Seyedhamed Ghavamnia等人。本篇文章提出了一种自动为Docker容器生成系统调用限制策略的通用方法。
1 Introduction
对于容器而言,它的TCB为整个操作系统。尽管OS提供了一系列安全措施来对容器进行隔离,但攻击者仍然可以通过系统调用中的漏洞来进行提权。为了减小攻击面,目前常见的方法是对程序的代码中不需要的部分进行识别和删除,例如从共享库中删除未被调用的函数或是按需对内核代码进行裁剪。但是这种方法存在一个问题,那就是无法完全识别出所有能够安全删除的代码。许多工作采取动态识别的方式来删除代码,但是这类方法存在一个问题,那就是它们可能无法识别一些调用频率较低的代码,例如被用来处理异常状态的代码。这类代码只在一些特定的情况下才会被调用,因此可能无法被准确识别。为了解决这一问题,作者提出了一个为容器自动化生成系统调用的系统,从而解决系统调用被滥用而导致的容器逃逸等问题。通过动态分析和静态相结合,该系统能够检查容器中应用程序的所有执行路径以及它对应的依赖,从而确定容器正确运行所需的系统调用超集。
2 Threat Model
在Confine的威胁模型中,攻击者可以完全访问在第三方主机上运行的容器。这种访问可能是合法的(例如,作为云服务的常规用户),也可能是通过容器上运行的进程存在的漏洞来进行访问。攻击者的目标包括主机的OS内核以及在其上运行的任何其他容器。
3 Design
如图所示,该系统可以分为两部分:动态分析和静态分析。下面分别对这两部分进行介绍。
3.1 Dynamic Analysis
在系统中,动态分析的目的是为了识别哪些程序将在容器中运行。尽管许多容器都是为运行单一应用服务而构建的,但通常会调用一些其他的程序来支持应用程序的正常运行,例如调用特定程序来对环境进行配置。因此,为了准确地生成系统调用策略,Confine必须确定所有可能在容器的生命周期内运行的程序。为了解决这一问题,Confine记录了自容器创建以来在可配置的时间段(默认为30秒)内启动的每一个应用程序,然后,为收集到的应用程序集生成相应的系统调用策略。对于那些可能包括不是从一开始就启动的应用程序的容器,Confine支持手动提供应需要被分析的可执行文件的列表。除了应用程序之外,Confine还会识别哪些库有被加载。然而,并非所有的库都是在启动阶段被加载的。应用程序可以通过动态加载的方式来在执行过程中动态加载特定的模块,例如Apache Httpd可以通过用户自定义的配置文件来动态加载库。为了识别这种动态加载的库,作者通过监视/proc虚拟文件系统来获取应用程序在运行时加载的库列表,该系统为每个进程提供了这一信息。
3.2 Static Analysis
Confine通过执行静态分析以提取每个应用程序所需的系统调用。应用程序可以通过多种方式进行系统调用:
-
Libc: 用户程序通常通过libc库调用系统调用,libc库提供相应的封装函数(例如,libc中的 read
函数对应了系统调用SYS_read
)。对于libc库,Confine分析了它的源代码,从而导出函数和系统调用之间的映射关系。一个libc函数可能有多条控制流路径通向实际的系统调用。为了识别哪些系统调用被一个给定的libc函数调用,Confine静态地分析了libc的源代码,以得出其完整的调用图,从而准确地将每个函数映射到其各自的系统调用。在libc中,函数指针非常常见。但是,用指针分析的方法去找出所有通过函数指针调用的系统调用是非常耗时的。因此,作者采取了一种更保守的方法,只要该函数的地址出现在控制流路径上,那么就就认为该函数有在该路径上被调用。 -
Direct System Call Invocation: 除了使用libc封装好的函数,应用程序和库也可以直接使用syscall()函数或使用syscall汇编指令来调用系统调用。尽管使用这种方法的应用程序和库的数量有限,但为了保险起见,作者通过反汇编技术对直接调用系统调用的情况进行了处理。
4 Evaluation
为了对Confine进行衡量与评估,作者收集了多个已经披露的CVE,并对它们进行了分类。实验结果表明,Confine能够大幅度缩减容器可调用的系统调用,并在不破坏容器功能的前提下,消除先前披露的51个内核CVE。
原文始发于微信公众号(COMPASS Lab):[论文分享] | Confine