今天要为大家推荐的文章是来自西北大学邢新宇研究组投稿并发表于USENIX Security 2024的工作Take a Step Further: Understanding Page Spray in Linux Kernel Exploitation。
Background
在Linux内核中,堆喷射(Heap Object Spray)已经成为了最广泛的利用技术。Heap Spray的核心思想是,在特定的利用场景下,通过大量的触发 Kernel Heap Object 的分配,实现Kernel Object层面的控制,进而完成漏洞利用目标。目前绝大部分的高级利用方式都基于堆喷射,比如 DirtyCred、Cross-Cache Attack等,如果从内核内存分配的原理上看,Heap Allocator,或者说 Slab-Based 的 Allocator,其底层过程可以简单描述为:当内核需要一个新的slab cache,那么一个(或多个)slab page(s)将会被分配,进而组成一个slab cache,供给Heap Allocator使用,而Heap Allocator将Kernel Object布置在对应的slab pages上,遍形成了针对Heap Object的分配。
然而,如果我们更进一步,暂时抛开Heap Allocator,那么可以看见,在更底层的位置,Page Allocator是这一系列上层操作的基础,那么为什么我们不尝试直接绕过 Slab-Based Heap Allocator 进而通过内核中的直接页分配来进行利用呢?于是 Page Spray 的想法就出现了。在本篇文章中,研究者们针对 Page Spray 技术进行了系统的研究与分析,并将 Page Spray 成功应用到多个不同的real-world context下,包括 Ubuntu Desktop、Android Kernel【BlackHat USA 23 “Bad io_uring”,[1]】、以及云环境中, Google kCTF[2]。
Threat Model
研究者在威胁模型中,假设realworld adversary尝试使用一个内核堆上的漏洞,比如 Out-Of-Bounds、Use-After-Free、Double-Free。并考虑所有的基本防御开启的情况下进行漏洞利用,排除并未合并进主线的external mitigation,不考虑由硬件差异造成的影响。
Exploit Model
研究者将exploit model的名字命名为Dirty Page,对于一个典型的 Page Spray 利用,研究者以一个 Double Free 漏洞作为例子,考虑最基本的一个slab中只有一个page的情况。
除此以外,研究者们也讨论了Dirty Page的一些其他的变体,比如针对UAF漏洞,可以原生的维护一个free object的reference,此时Page Spray的使用将会更加自然。比如在 CVE-2022-2588 上,当victim object为cred结构体时,通过构造fake cred对象,通过页喷射注入到对应的Page上,可以直接实现权限提升。针对exploit model的可适用性,研究者指出,Double Free/Invalid Free/UAF 等漏洞都是可以原生的适用的。而Page Spray更像是一种plug-in的利用技术,可以与其他不同的利用技术搭配,非常灵活。(针对OOB漏洞,需要先将其进行转化)。
Root Cause Analysis
在这一节,作者们尝试回答一个问题,即 “为什么 Page Spray 这种情况会出现在内核中?”。研究者们给出了两种通用的解释与模式
1. 拷贝-写入模式。
– Raw Page-Level Buffer: 考虑到内核的设计,几个子系统都包含一种类似的功能,该功能是首先设置一个缓冲区,然后根据需要触发并写入该缓冲区。可以在列表 1 所示的代码片段中观察到这样的示例,它是管道子系统的一部分。(同样的其实在io_uring子系统中也存在)
– Non-linear Page-Frags Buffer: 在网络子系统中,缓冲区的设计可以分为两种类型:线性缓冲区、非线性缓冲区。线性缓冲区在skb创建的早期阶段分配,主要用于保存协议头和部分实际数据。在此初始阶段不发生数据页面分配。另一方面,当线性缓冲区无法为数据存储提供足够的内存空间时,非线性缓冲区会发挥作用。非线性缓冲区的组织类似于多个片段,每个片段维护一个特定页面作为数据缓冲区。内核使用复制写入操作将数据从用户空间传输到这些连续的页面片段。一个典型的相关拷贝函数是 skb_copy_datagram_from_iter。
2. 重映射模式。
– 通常,当用户空间应用程序执行 mmap() 调用以在调用进程的虚拟地址空间中建立新映射时,内核主要授予对虚拟地址的访问权限作为此调用的返回值。当深入研究内核实现时,研究者们发现内存的重映射行为给 Page Spray 提供了机会。当数据复制操作发生在用户进程和内核空间之间时,这两个内存地址空间之间的边界通常需要检查操作有效性和内核空间中的数据重建。因此,穿越用户进程和内核空间之间边界的复制操作可能会引起一些性能问题,特别是在优先考虑性能的子系统中,例如网络子系统。为了应对这一挑战并提高整体系统性能,Linux 采用了零拷贝设计。通过这一特性,内核空间和用户进程之间的数据传输减少了内存复制带来的额外开销。在这种设计中,用户空间进程显式设置(环)缓冲区属性,并且分配的页面被组织成多个页面相关的结构。当用户空间的进程执行 mmap() 调用时,其详细实现和执行将由相应的内核函数表根据套接字的属性进行重定向。例如,当在 PF_PACKET 中设置数据包环形缓冲区时,mmap() 调用由内核中的 packet_mmap() 函数执行。在这个实现中,它引入的与页面相关的结构是 Page Vector,它可用于维护对有效页面的引用,类似于前面讨论的 skb 页面片段。内核首先在第 8 行 3 中将页向量转换为原始页引用。随后,在下一行中,调用 vm_insert_page() 将目标页插入到用户空间进程的虚拟地址映射空间中。在这次插入之后,用户空间进程获得了对页面进行直接内存写操作的权利。换句话说,在这个阶段,相关的用户进程能够将数据喷射到内核空间分配的页面上。(在io_uring中也有类似的实现)
Static Analysis Model
在这一节,作者尝试提出一种static model在内核中分析这样的调用点。
-
对于拷贝-写入模式,可以首先在内核层面构建对应的call graph,分别中从allocation interface函数和copy interface函数向上进行call graph追踪,尝试查找到这两条路径的一个交点函数。在焦点函数内部,判断从allocation到copy操作的依赖关系,并与对应的control struct匹配,进而确定拷贝-写入点。
-
对于重映射模式,在初始化阶段首先收集对应的 mmap() 信息,以及对应的 ops 结构体信息。接下来从对应的remapping interface向上回溯,尝试抓取到对应子系统的 *_mmap() 函数,并进行数据流分析,与allocation interface对应的control struct进行相互匹配。
作者设计了一个analyzer来实现这一套分析,并尝试找出了21个潜在的调用点。除此以外,作者也引入了动态分析,通过在对应的潜在调用位置插入panic function,配合Syzkaller,判断齐可达性,以及对应的系统调用。
Evaluation
在Evaluation部分,研究者们将 Effectiveness 的实验分成了两部分:即 Exploitability(可利用性)和 Stability(稳定性)。
在可利用性(Exploitability)的层面上,研究者引入了15个realworld Kernel CVEs进行测试,其中一个涉及Mobile Device,漏洞类型比例为 UAF/OOB/DF = 10/3/2。
结果显示,Heap Object Spray 和 Page Spray 均成功利用了其中的14个,但都fail了其中的某一个case。研究者对 Page Spray利用失败的case,即 CVE-2016-10150 的原因进行了分析。在 CVE-2016-10150 中,该漏洞是在KVM内核模块中触发的。UAF 的分配和释放发生在对 ioctl() 的单次调用期间。这种情况引入了具有挑战性的竞争条件,并且时间窗口非常窄。这是页面喷射的一个极端情况,因为如第 4 节所示,页面布局操作和页面喷射必须发生在分配易受攻击的对象之后、释放之前,才能有效利用该漏洞。然而,本例所施加的严格时间限制使得为 Page Spray 的成功建立必要条件变得极其困难。而对于 Heap Object Spray 利用失败的 CVE-2022-2585,作者在Case Study中描述并分析了对应的情况。
在利用稳定性的层面,研究者们与来着 USENIX Security 2022 的 K(H)eaps 分别在两种不一样的workload下进行对比(idle and busy),以更系统的验证Page Spray的稳定性。
Stability实验的结果显示,Page Spray在idle status下优于传统的Heap Object Spray,而在Busy Status下,Page Spray达到了与传统Heap Spray互补的效果。
两项评估的结果表明,Page Spray 是一种稳健且有效的方法,可以作为传统 Heap Object Spray 方法的宝贵补充。当在正确的场景中使用时,这些技术可以弥补彼此的局限性,从而导致可利用的漏洞范围更广,并最终增强成功利用的稳定性。
Mitigation Discussion
在这一节,作者指出了目前很多 exploitation method 的根本原理都是基于 page-level memory reuse,而想要防御类似Page Spray这样的攻击,最直接的办法就是进行页级内存隔离。作者进行了一种简单并直接的尝试,即将对应的页喷射的分配操作,隔离到 DMA 区域中,基于这样的情况,Page Spray所带来的Page Reclaim将会被block,进而导致利用失败,在Phoronix Benchmarks下,这种方式仅导致了非常低的开销。除此以外,为了引起community的重视,研究者们也提到了Google的最新Mitigation(目前还未合并进入主线),即slab virtual,通过创建一个新的专用虚拟内存区域(以前是地址空间中未使用的空洞)来运行。在这个区域中,分配slab对象,将它们与常规内存分配过程隔离,并配合调整 virt_to_phys() 等函数的操作,来实现缓解,其开销大约 4%,不过其引入了对于KFENCE、KASAN的不兼容性。
Conclusion
Page Spray 作为一种页面级漏洞利用方法,补充了领域的现有技术。Page Spray 提供了一种可行的替代方案,在现实场景中具有相当甚至优越的可利用性和稳定性。通过对 Page Spray 根本原因的调查表明,它与 Linux 内核设计中的某些机制密切相关。解决这些根本原因并减少 Page Spray 应该是未来内核安全发展的任务之一,这可能涉及在页面级别引入增强的内存隔离设计以增强安全措施。
论文下载:https://arxiv.org/abs/2406.02624
参考链接:
[1] Blackhat USA 2023 “Bad io_uring”:https://www.blackhat.com/us-23/briefings/schedule/index.html#bad-io_uring-a-new-era-of-rooting-for-android-32243
[2] Google kCTF/TyphoonPWN:https://ssd-disclosure.com/ssd-advisory-linux-clock_thread_cputime_id-lpe/
投稿作者信息:
个人主页:http://ziyiguo.site/
原文始发于微信公众号(安全研究GoSSIP):G.O.S.S.I.P 阅读推荐 2024-06-24 Take a Step Further