文章目录

  • 1.测试
  • 1-1. 测试一
  • 1-2. 测试二
  • 2. [**quarantine randomization**](https://www.openwall.com/lists/kernel-hardening/2020/09/29/6) 隔离随机化
  • 3. 性能


介绍:该保护机制的全称是

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
   ...