slab-quarantine-针对内核UAF利用的堆隔离机制


介绍:该保护机制的全称是 Linux Kernel Heap Quarantine—— SLAB_QUARANTINE,主要针对内核UAF利用。

UAF利用:利用堆喷来控制victim对象,原理是kmalloc()总能分配得到最近释放的堆块。

SLAB_QUARANTINE原理:一是把将要释放的堆块暂时放入隔离队列,等待被真正释放,这样堆喷时就不能立刻获得该堆块了;二是采取quarantine randomization 隔离随机化机制,当隔离区增大后,随机选取batch(堆隔离采用batch来存储对象)随机释放一半的对象,使得再次分配的得到漏洞对象的次数就不确定了(避免确定性堆喷,但是如果喷射次数很多,可能可以绕过该机制);三是设置 CONFIG_SLAB_QUARANTINE 选项,调用init_on_free来清空内存块,避免放入隔离区的堆块中仍存有payload。

1.测试

测试源码:作者开发了两个lkdtm程序进行测试,源码在这里

1-1. 测试一

内容:程序名为lkdtm_HEAP_SPRAY,模拟堆喷过程,先从kmem_cache分配和释放一个对象,然后再分配400,000个相似的对象,检查是否分配到刚释放的对象。

#define SPRAY_LENGTH 400000
    ...
    addr = kmem_cache_alloc(spray_cache, GFP_KERNEL);
    ...
    kmem_cache_free(spray_cache, addr);
    pr_info("Allocated and freed spray_cache object %p of size %d\n",
                    addr, SPRAY_ITEM_SIZE);
    ...
    pr_info("Original heap spraying: allocate %d objects of size %d...\n",
                    SPRAY_LENGTH, SPRAY_ITEM_SIZE);
    for (i = 0; i < SPRAY_LENGTH; i++) {
        spray_addrs[i] = kmem_cache_alloc(spray_cache, GFP_KERNEL);
        ...
        if (spray_addrs[i] == addr) {
            pr_info("FAIL: attempt %lu: freed object is reallocated\n", i);
            break;
        }
    }
    
    if (i == SPRAY_LENGTH)
        pr_info("OK: original heap spraying hasn't succeeded\n");

如果关闭CONFIG_SLAB_QUARANTINE,则成功分配到刚释放的堆块:

  # echo HEAP_SPRAY > /sys/kernel/debug/provoke-crash/DIRECT
   lkdtm: Performing direct entry HEAP_SPRAY
   lkdtm: Allocated and freed spray_cache object 000000002b5b3ad4 of size 333
   lkdtm: Original heap spraying: allocate 400000 objects of size 333...
   lkdtm: FAIL: attempt 0: freed object is reallocated

如果开启CONFIG_SLAB_QUARANTINE,则分配失败:

  # echo HEAP_SPRAY > /sys/kernel/debug/provoke-crash/DIRECT
   lkdtm: Performing direct entry HEAP_SPRAY
   lkdtm: Allocated and freed spray_cache object 000000009909e777 of size 333
   lkdtm: Original heap spraying: allocate 400000 objects of size 333...
   lkdtm: OK: original heap spraying hasn't succeeded
1-2. 测试二

内容:隔离区的size必须足够大才行。程序名为lkdtm_PUSH_THROUGH_QUARANTINE,先从kmem_cache分配和释放一个对象,然后分配和释放400,000次—— kmem_cache_alloc()+kmem_cache_free()

    addr = kmem_cache_alloc(spray_cache, GFP_KERNEL);
    ...
    kmem_cache_free(spray_cache, addr);
    pr_info("Allocated and freed spray_cache object %p of size %d\n",
                    addr, SPRAY_ITEM_SIZE);

    pr_info("Push through quarantine: allocate and free %d objects of size %d...\n",
                    SPRAY_LENGTH, SPRAY_ITEM_SIZE);
    for (i = 0; i < SPRAY_LENGTH; i++) {
        push_addr = kmem_cache_alloc(spray_cache, GFP_KERNEL);
        ...
        kmem_cache_free(spray_cache, push_addr);

        if (push_addr == addr) {
            pr_info("Target object is reallocated at attempt %lu\n", i);
            break;
        }
    }

    if (i == SPRAY_LENGTH) {
        pr_info("Target object is NOT reallocated in %d attempts\n",
                    SPRAY_LENGTH);
    }

将对象放入堆隔离区,等待其返回到freelist后再重新分配出来。可见,每次覆盖漏洞对象所需要的分配次数很相近,这对UAF利用很有利。所以作者开发了quarantine randomization

  # echo PUSH_THROUGH_QUARANTINE > /sys/kernel/debug/provoke-crash/
   lkdtm: Performing direct entry PUSH_THROUGH_QUARANTINE
   lkdtm: Allocated and freed spray_cache object 000000008fdb15c3 of size 333
   lkdtm: Push through quarantine: allocate and free 400000 objects of size 333...
   lkdtm: Target object is reallocated at attempt 182994
  # echo PUSH_THROUGH_QUARANTINE > /sys/kernel/debug/provoke-crash/
   lkdtm: Performing direct entry PUSH_THROUGH_QUARANTINE
   lkdtm: Allocated and freed spray_cache object 000000004e223cbe of size 333
   lkdtm: Push through quarantine: allocate and free 400000 objects of size 333...
   lkdtm: Target object is reallocated at attempt 186830
  # echo PUSH_THROUGH_QUARANTINE > /sys/kernel/debug/provoke-crash/
   lkdtm: Performing direct entry PUSH_THROUGH_QUARANTINE
   lkdtm: Allocated and freed spray_cache object 000000007663a058 of size 333
   lkdtm: Push through quarantine: allocate and free 400000 objects of size 333...
   lkdtm: Target object is reallocated at attempt 182010

2. quarantine randomization 隔离随机化

原理:堆隔离采用batch来存储对象,当隔离区增大后,随机选取batch随机释放一半的对象。以下可见,再次分配的得到漏洞对象的次数就不确定了。

   lkdtm: Target object is reallocated at attempt 107884
   lkdtm: Target object is reallocated at attempt 265641
   lkdtm: Target object is reallocated at attempt 100030
   lkdtm: Target object is NOT reallocated in 400000 attempts
   lkdtm: Target object is reallocated at attempt 204731
   lkdtm: Target object is reallocated at attempt 359333
   lkdtm: Target object is reallocated at attempt 289349
   lkdtm: Target object is reallocated at attempt 119893
   lkdtm: Target object is reallocated at attempt 225202
   lkdtm: Target object is reallocated at attempt 87343

问题:单纯随机化并不能阻止攻击者,隔离区的对象很可能已经存储了攻击者的payload。如何在放入隔离区之前就清空堆内容?

解决:内核中已有清零代码init_on_free,作者开发了CONFIG_SLAB_QUARANTINE,作者发现init_on_free没有及时清零,于是提交了patch来修复此问题。作者还提交了 additional patch 来便于调试分析,有助于理解隔离机制,输出示例如下:

   quarantine: PUT 508992 to tail batch 123, whole sz 65118872, batch sz 508854
   quarantine: whole sz exceed max by 494552, REDUCE head batch 0 by 415392, leave 396304
   quarantine: data level in batches:
     0 - 77%
     1 - 108%
     2 - 83%
     3 - 21%
   ...
     125 - 75%
     126 - 12%
     127 - 108%
   quarantine: whole sz exceed max by 79160, REDUCE head batch 12 by 14160, leave 17608
   quarantine: whole sz exceed max by 65000, REDUCE head batch 75 by 218328, leave 195232
   quarantine: PUT 508992 to tail batch 124, whole sz 64979984, batch sz 508854
   ...

3. 性能

Network throughput:

init_on_free=on相对于init_on_free=off带来28%的性能损失。

CONFIG_SLAB_QUARANTINEinit_on_free=on要减少2%的性能损失

参考:

Linux kernel heap quarantine versus use-after-free exploits

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值