今天为大家推荐的论文是来自浙大网安投稿的关于容器安全的最新研究工作Making Memory Account Accountable: Analyzing and Detecting Memory Missing-account bugs for Container Platforms,该工作的更多具体细节将在今年的ACSAC上为大家展示。
「论文概述」
内存是计算机系统最重要的资源之一,因此内存记账和内存使用限制是所有操作系统内核的基本功能。Linux内核使用内存控制组(memory control groups, 简称memcg)实现了内存记账。内存控制组被广泛使用在几乎所有容器和云原生系统中,比如Docker和Kata Containers等容器引擎、容器即服务平台(Container-as-a-Service, CaaS)和函数即服务平台(Function-as-a-Service, FaaS)等。
尽管 memcg 被广泛使用,但它的内部实现非常复杂而易错。其一,Linux内核通过在内存分配/释放路径中插入内存记账接口来实现记账,但因为内存分配/释放接口具有使用量大、用法多样等特点,所以memcg在插入内存记账接口调用时难以覆盖到所有的执行路径,从而出现内存记账缺失漏洞(即内存分配但并未记账)。其二,即使接口处于正确的位置,内存记账行为是否确实发生也进一步取决于记账标志是否置位,然而论文的分析发现这些标志经常没有置位,也会导致内存记账缺失。因此,内存记账缺失是内核中很常见的问题。
然而memcg内存记账缺失漏洞却从未被系统地分析过,使得人们对两个问题认识不足。首先,人们不清楚内存记账缺失漏洞会造成什么样的安全问题,以及这些漏洞可否被利用发起攻击,导致内核开发人员往往忽视修复内存记账缺失漏洞的重要性。其次,当前缺少自动检测内存记账缺失漏洞的工具。如前所述,内存记账的设计和实现很复杂,即使是有经验的用户也很难正确理解内存记账的设计和实现。如果没有自动检测,开发人员几乎不可能人工验证内存记账的正确性。因此,如何检测内存记账缺失漏洞是一个亟待解决的问题。
本论文是首个系统性研究Linux内核内存记账缺失漏洞的工作。针对上述的两个问题,本论文首先系统调研了内存记账缺失漏洞的可利用性,同时给出了详细的攻击案例,包括在多种底层运行时(runC和Kata Containers)和多种平台(如Docker,OpenShift和OpenWhisk)上进行攻击实验,证明了内存记账漏洞可用于发起对整个操作系统的拒绝服务攻击(DoS);在此基础上,本论文提出了基于页计数器的接口识别、内存申请-记账映射分析以及记账标志位分析等技术,成功解决了内存记账系统使用量大、用法多样等难点。本论文向Linux内核社区提交了53个漏洞补丁;其中37个已经被接收,并获得了两个CVE编号CVE-2021-3759和CVE-2022-0480。
「内存记账背景」
内存记帐是每个现代操作系统内核的核心功能,目前Linux内核采用内存控制组(memcg)来实现进程粒度的内存记帐。一个 memcg 实例可包含多个进程,这些进程的内存使用量都由该memcg进行记账和限制。Memcg实例间以树形结构组织,父节点memcg的内存限制也会影响所有子节点。目前memcg 有两个版本:v1 和 v2,它们的区别主要在于层次结构和用户接口,在记账方面的实现则大致相同。目前两个版本都被广泛使用,本文以v1为主要研究对象,大多数结论也适用于 v2。
与基于虚拟机 (VM) 的内存控制技术相比,memcg 更加细粒度和轻量级,因此memcg广泛应用于Docker等容器引擎以及基于容器的CaaS和FaaS平台。CaaS 平台主要为用户提供预置的容器实例,平台上的用户可以使用自定义的容器镜像创建/启动/停止/删除容器。而FaaS 平台不给用户提供容器实例,而是允许用户输入函数和触发规则,并自动创建容器实例来执行输入函数。
Docker 和 CaaS/FaaS 平台通常使用原生容器运行时(即runC)运行容器实例。在安全性非常关键的场景下,这些平台也可能会使用安全的容器运行时以进一步提高容器之间的隔离性,例如 gVisor 和 Kata Containers。gVisor 是Google 开发的沙盒容器运行时,它在名为 Sentry 的用户空间内核上运行容器实例。Sentry会拦截并处理来自容器的大多数系统调用。因此,Sentry 减少了从容器直接到物理机内核的系统调用。Kata Containers则使用虚拟化技术来隔离容器实例,其每个容器实例都在单独的VM 中运行以实现强隔离。
论文的实验表明,不管是原生运行时还是安全运行时,都容易受到内存记账缺失漏洞导致的内存耗尽攻击。并且内存记账缺失漏洞可被利用来攻击 Docker、CaaS 和 FaaS 平台,导致内存耗尽,从而导致单个节点甚至整个集群崩溃。
「内存记账缺失漏洞的可利用性和攻击案例」
图1 论文独立发现的CVE-2021-3759漏洞,内核信号量对象在申请时并未被正确记账。
为了展示内存记账缺失漏洞的可利用性,这篇论文基于独立发现的内存记账缺失漏洞,CVE-2021-3759,研究不同平台上的漏洞利用效果。漏洞产生细节如图1所示,内核在调用sem_alloc()进行信号量对象申请时,由于在第8行调用kvzalloc()内存申请函数时传入的是GFP_KERNEL标志(而非GFP_KERNEL_ACCOUNT),导致内存记账缺失,因此用户态程序申请的所有信号量对象所占用的内存都不会被memcg记账和限制。攻击者通过反复申请信号量对象占用大量内存,而这一过程只需要攻击者拥有普通用户权限。
图2 在OpenShift上发起内存耗尽攻击时,尽管节点上内存已经被耗尽,内存的记账量却远远小于内存的实际使用量
论文在三个平台上评估了内存记账缺失漏洞的可利用性,包括本地的Docker平台,Google Cloud上的自建OpenShift CaaS平台和自建OpenWhisk FaaS平台。实验在三种环境下运行同样的攻击脚本,反复申请大量的未被记账的内核信号量对象触发CVE-2021-3759漏洞,并观察平台上的内存使用情况和记账情况。图1展示了OpenShift平台上的实验情况,可以发现尽管节点上内存已经被耗尽,内存的记账量却远远小于内存的实际使用量,其他两个平台上的实验结果和OpenShift上类似。
图3 论文发现的CVE-2022-0480漏洞,攻击者可利用该漏洞在Kata容器内部重复申请文件锁,虚拟机将请求直接转发给宿主机内核,触发宿主机内核的记账缺失漏洞。
论文也发现内存记账缺失漏洞甚至能穿透Kata Containers这样基于虚拟机的安全容器,并上报了CVE-2022-0480漏洞。Kata Containers是一个基于虚拟化技术的容器运行时,其每个容器实例都运行在一个轻量虚拟机中以实现强隔离。然而Kata 运行时会将虚拟机内的文件操作转发给宿主机,使得宿主机面临内存记账缺失漏洞攻击的风险。如图 4 所示,攻击者是容器内的普通用户,并通过fcntl系统调用在一组文件上分配大量 POSIX 锁,此时虚拟机内核会将请求转发给宿主机机上运行的 virtio-fsd 守护进程,守护进程则会在宿主机内核中分配相应的POSIX 锁,占用大量宿主机内存。虽然守护进程的内存使用量受到宿主机 memcg 的限制,但锁对象所使用的内存却受到内存记账缺失漏洞CVE-2022-0480的影响而未被记账,导致恶意容器可以耗尽主机上的所有物理内存。
上述攻击表明,攻击者可以利用内存记账缺失漏洞攻击多种容器运行时、Docker 和 CaaS/FaaS 平台,耗尽容器节点甚至整个集群的所有内存引发拒绝服务。并且攻击者只需要普通用户权限就可以发起攻击,攻击门槛大大降低。虽然内存记账缺失漏洞危害大且广泛存在, 目前却没有能全面检测和消除这种漏洞的工具。
「漏洞检测」
这篇论文开发了MANTA分析框架以精准识别内存记账缺失漏洞。识别内存记账缺失漏洞存在缺乏内存记账接口的文档、内存记账和内存申请之间路径复杂以及条件性记账等技术挑战。如图2所示,论文提出了基于内存页计数器的记账接口识别、内存申请-记账映射分析和内存记账条件标志的追踪分析等技术一一解决,最后通过在操作系统内动态触发的方式检验检测结果的准确性。最终的分析结果证实了这些技术的有效性。
图4 MANTA分析框架的主要架构
由于内核并没有明确关于内存记账接口的文档,且内存记账接口的使用方式多种多样,MANTA创新性地通过定位内存页计数器来实现内存记账接口识别。MANTA 遍历每个内核 IR 指令并检查它是否增加/减少记录记账页数量的页计数器(即page_counter类型变量),并将直接包含这些指令的函数标记为基本记账函数。因为Linux 内核中的page_counter只供 memcg使用,所以该方法可以实现很高的准确率。MANTA 随后根据基本记帐功能识别记帐接口。MANTA随后使用 深度优先搜索从基本记帐函数沿着调用链条向上遍历,同时标记所有访问过的函数,而遍历会在到达 memcg 子系统外的函数时停止。对于被标记函数,如果它被memcg 子系统外部调用,MANTA就将其标记为记帐接口。
基于识别出的内存记账接口,MANTA需要更进一步分析内存申请调用和内存记账调用间的映射关系。在正常情况下这种映射应该是一对一的,即申请一次内存只需要记账一次。然而内核中的调用关系异常复杂,从内存申请到内存记账的执行路径数量可能极多,因此MANTA通过提前算好内核中每个函数是否对某个内存页有申请或记账的操作,并保存到函数摘要中。这样MANTA在分析函数调用时可以不必跟进被调用函数内部遍历所有执行路径,而是直接读取函数摘要中记录的关键信息即可。
即使内存申请和内存记账接口间存在一一映射的关系,内存记账行为是否确实发生进一步取决于传入内存申请接口的标志位,比如图1第8行中调用kvcalloc()传入的第二个参数。如果没有传入正确的标志位,则同样会引发内存记账缺失漏洞。MANTA使用了过程间的数据流追踪技术来分析标志位的置位情况。这个过程中,MANTA会递归地追踪标志位的use-def chain,且能跨函数调用进行追踪,如果MANTA发现该标志位的定义中没有置位__GFP_ACCOUNT位,就会生成记账缺失的警告。相比于LLVM自带的数据值追踪分析,MANTA能实现跨函数追踪,因此精确度更高。
对于所有静态分析生成的警告例,MANTA进一步通过动态触发的分析方法验证这些警告是否是可触发的漏洞,包括使用基于LTP的测试例触发以及人工审计漏洞代码并开发测试例进行触发。在MANTA生成的242个内存漏记账缺失警告例中,论文证实了证实了其中162个漏洞能被动态触发,向Linux社区报告了53个高危漏洞,并开发了17个PoC验证这些漏洞能被重复触发占用大量未记账的内存。Linux社区已确认其中37个漏洞并接收了论文提交的其中18个补丁。
论文下载地址:
https://wenboshen.org/papers/memcg.pdf
投稿作者:
杨昱天,浙江大学网络空间安全学院博士生,导师是申文博老师,研究方向是操作系统保护,程序分析及云原生系统安全等。
还没有评论,来说两句吧...