文章目录
- 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
...