0x01 摘要
常见的堆利用技术中的空闲列表投毒会破坏堆分配器的空闲块的链接列表。堆分配后将返回投毒的地址,Linux内核引入了堆强化,这使这些链接列表中的指针会变得非常模糊。在不知道密钥和指针地址的情况下,攻击者无法可靠地用选定的地址毒化指针。在这篇文章中,我将分析Linux内核的free列表指针混淆有一些缺陷,在适当的条件下,攻击者可以进行自由列表投毒攻击。
0x02 介绍
Linux内核中的默认堆分配器是SLUB分配器。每个空闲块中都有一个指针,该指针是跟踪这些空闲块的链表的一部,空闲列表投毒是针对Linux内核的一项已知技术,在《内核开发指南:攻击核心》一书中有记录,2010年,攻击者使用精心设计的地址覆盖了空闲列表指针,该地址随后将在将来的堆分配中返回。
自2017年以来,使用配置选项CONFIG_SLAB_FREELIST_HARDENED在Linux内核中缓解了自由列表投毒的问题。
https://patchwork.kernel.org/patch/9864165/中显示了此缓解措施的补丁。
进行混淆的重要代码如下所示:
* * Returns freelist pointer (ptr). With hardening, this is obfuscated * with an XOR of the address where the pointer is held and a per-cache * random number. */ static inline void *freelist_ptr(const struct kmem_cache *s, void *ptr, unsigned long ptr_addr) { #ifdef CONFIG_SLAB_FREELIST_HARDENED return (void *)((unsigned long)ptr ^ s->random ^ ptr_addr); #else return ptr; #endif
目标指针与指针的地址和一个随机值进行异或,每个主机的随机值是唯一的,我们可以看到,除非知道秘钥在块地址上也有信息泄漏,否则无法显示或覆盖此指针。
0x03 准备
理解攻击的关键是,指向空闲块的指针存储在类似的空闲块内。实际上,这意味着指针和这些指针的地址几乎相同,例如,分配2个16字节的内存块时,指针和指针的地址很可能仅在其较低的12位不同(对于具有12位页面大小的体系结构)。
当空闲列表指针被ptr ^ ptr_addr ^ s-> random混淆时,则由于ptr和ptr_addr共享相同的高位,这将导致ptr ^ ptr_addr的结果高位为0。因此,如果混淆指针的信息泄漏,那么我们随后可以泄漏s-> random的高52位。
还注意到,泄漏了s-> random的一些低位。SLUB分配器是基于board的分配器,它将分配舍入到board的大小。我们可以做出一些假设,即分配给我们的内存将与舍入后的board大小对齐。因此,如果我们分配16个字节的内存,则堆分配的低4位将为0。如果分配256个字节,则低8位将为0,如果分配4096个字节,则低12位将为零。但是,如果我们将board尺寸混合在一起,则对于不同的board会出现新的s-> random。
0x04 攻击
我们要执行自由列表投毒攻击。首先,我们需要信息泄露,假设我们分配了一个16字节的块,需要知道空闲块中空闲列表指针的内存内容,空闲列表指针是64位体系结构上空闲块的前8个字节。
当我们公开这个模糊的指针时,只需要高52位和低4位即可,这些位是前面显示的secret值,即s-> random,我们将其称为“secret”。
现在我们要进行空闲列表指针破坏。我们分配一个16字节的块,释放此缓冲区,将破坏它的指针。我们还需要知道这个空闲块的地址,将其称为PTR_ADDR。
我们将用来毒化的目标缓冲区称为TARGET_PTR,要计算模糊的空闲列表指针,我们使用:
secret = high_bits_of_secret | low_bits_of_secret; OBFUSCATED_POINTER = TARGET_PTR ^ secret ^ PTR_ADDR;
当覆盖该指针时,仍然不知道secret的中间位。在上述情况下,我们只知道56(52 + 4)位,而8位仍然未知,中间位可以是任意值。
我们使用OBFUSCATED_POINTER进行毒化,并具有8位熵来表示堆分配器将返回内存的位置。但是,它仍可能从同一页内存中返回一个地址,因此,如果能够控制这些可能结果中的每一个,那么攻击就可以成功。
0x05 结论
这篇文章分析了SLUB分配器中的堆强化功能,以混淆free列表指针,我证明了该方法存在弱点,并且可以在这些混淆的指针中显示secret信息。
0x06 跟进
用于Linux内核补丁:
引述一提交消息的部分:
kmalloc-32 freelist walk, before: ptr ptr_addr stored value secret ffff90c22e019020@ffff90c22e019000 is 86528eb656b3b5bd (86528eb656b3b59d) ffff90c22e019040@ffff90c22e019020 is 86528eb656b3b5fd (86528eb656b3b59d) ffff90c22e019060@ffff90c22e019040 is 86528eb656b3b5bd (86528eb656b3b59d) ffff90c22e019080@ffff90c22e019060 is 86528eb656b3b57d (86528eb656b3b59d) ffff90c22e0190a0@ffff90c22e019080 is 86528eb656b3b5bd (86528eb656b3b59d) ... after: ptr ptr_addr stored value secret ffff9eed6e019020@ffff9eed6e019000 is 793d1135d52cda42 (86528eb656b3b59d) ffff9eed6e019040@ffff9eed6e019020 is 593d1135d52cda22 (86528eb656b3b59d) ffff9eed6e019060@ffff9eed6e019040 is 393d1135d52cda02 (86528eb656b3b59d) ffff9eed6e019080@ffff9eed6e019060 is 193d1135d52cdae2 (86528eb656b3b59d) ffff9eed6e0190a0@ffff9eed6e019080 is f93d1135d52cdac2 (86528eb656b3b59d)
本文翻译自:https://blog.infosectcbr.com.au/2020/03/weaknesses-in-linux-kernel-heap.html如若转载,请注明原文地址: