来自于原文"Weaknesses in Linux Kernel Heap Hardening"。
总结:free_list poisoning
是堆漏洞利用的常用方法,原理是将fd指针改成指向目标地址,这样下一次分配就会将分配到我们的目标地址。然而linux内核引入了堆加固机制,会将fd指针异或加密,导致利用受阻。本文分析一下这种加固机制的脆弱性,在特定情况下,攻击者还是能够利用free_list poisoning
。
1. 介绍内核堆加固机制
在2017年开始,内核开始引入内核堆加固机制。编译选项是CONFIG_SLAB_FREELIST_HARDENED
,patch见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
目标指针与某个地址指针和一个随机值异或加密,这个随机值对于每个slab都不一样,slab知识可以看看SLAB分配器概述。
2.利用面分析
首先需要明白,指向空闲块的指针和被存放在类似大小的空闲块中。也就是说,堆指针和指针所在地址的值相似,例如,分配2个16字节的堆块,很有可能指针和指针所在的地址只有低12bit不同(页大小为4K)。
当free_list 指针被混淆为ptr ^ ptr_addr ^ s->random
,ptr
和ptr_addr
的高位是相同的,导致高位异或ptr ^ ptr_addr
的结果是0。也就是说如果有信息泄露漏洞,我们可以泄露s->random
的前52 bit。
同时注意,我们泄露了几个s->random
的低比特。可以预测,slab块是对齐的,如果分配16字节堆块,则低4bit为0 ;如果分配256字节堆块,则低8bit为0 ;如果分配字4096堆块,则低12bit为0 。所以分配不同大小的slab,我们能够泄露s->random
中不同的比特位。
3.利用
如果要实施free_list poisoning
,首先我们需要一个信息泄露。假设已经申请了1个16字节的堆块,我们需要知道该空闲块内存中的free_list pointer
值(64位系统,则为前8字节,类似于FD)。泄露出free_list pointer
值之后,前52bit和后4bit就是secret值(s->random
)。
现在进行free list pointer corruption
,先分配16字节堆块再释放,目标是修改FD为目标地址TARGET_PTR
。空闲块的地址记为PTR_ADDR
。
secret = high_bits_of_secret | low_bits_of_secret;
OBFUSCATED_POINTER = TARGET_PTR ^ secret ^ PTR_ADDR;
问题:目前我们只知道56bit的secret值,还有8bit未知。虽然这8bit未知,但有一定几率返回同一内存页的地址,只要能控制整个内存页(即所有返回值指向的内存),就有可能利用成功。
4.结论
作者提交了1个patch解决这个问题。
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)